Repository: flutter/flutter
File: dev/bots/analyze.dart (Dart)

Code before:
        node,
        error: 'A @Deprecation annotation must have exactly one deprecation notice String.',
      );
      return;
    }
    final Expression deprecationNotice = arguments.first;
    if (deprecationNotice is! AdjacentStrings) {
      _addErrorWithLineInfo(node, error: 'Deprecation notice must be an adjacent string.');
      return;
    }
    final List<StringLiteral> strings = deprecationNotice.strings;
    final Iterator<StringLiteral> deprecationMessageIterator = strings.iterator;
    final bool isNotEmpty = deprecationMessageIterator.moveNext();
    assert(isNotEmpty); // An AdjacentString always has 2 or more string literals.

    final [...List<StringLiteral> messageLiterals, StringLiteral versionLiteral] = strings;

    // Verify the version literal has the correct pattern.
    final RegExpMatch? versionMatch = versionLiteral is SimpleStringLiteral
        ? deprecationVersionPattern.firstMatch(versionLiteral.value)
        : null;
    if (versionMatch == null) {
      _addErrorWithLineInfo(
        versionLiteral,
        error:
            'Deprecation notice must end with a line saying "This feature was deprecated after v<version> ...".',
      );
      return;
    }

    final int major = int.parse(versionMatch.namedGroup('major')!);
    final int minor = int.parse(versionMatch.namedGroup('minor')!);
    final int patch = int.parse(versionMatch.namedGroup('patch')!);
    final hasBuild = versionMatch.namedGroup('build') != null;
    // There was a beta release that was mistakenly labeled 3.1.0 without a build.
    final bool specialBeta = major == 3 && minor == 1 && patch == 0;
    if (!specialBeta && (major > 1 || (major == 1 && minor >= 20))) {
      if (!hasBuild) {
        _addErrorWithLineInfo(
          versionLiteral,
          error:
              'Deprecation notice does not accurately indicate a beta branch version number; please see https://docs.flutter.dev/install/archive to find the latest beta build version number.',
        );
        return;
      }
    }

    // Verify the version literal has the correct pattern.
    assert(messageLiterals.isNotEmpty); // An AdjacentString always has 2 or more string literals.
    for (final message in messageLiterals) {
      if (message is! SingleStringLiteral) {

Review comment (suggestion):
Just a suggestion, but I see that the deprecation lines all end with the version, followed by a single period. Maybe we can verify that, instead of the ellipsis.

```suggestion
            'Deprecation notice must end with a line saying "This feature was deprecated after v<version>.".',
```

Code after:
        node,
        error: 'A @Deprecation annotation must have exactly one deprecation notice String.',
      );
      return;
    }
    final Expression deprecationNotice = arguments.first;
    if (deprecationNotice is! AdjacentStrings) {
      _addErrorWithLineInfo(node, error: 'Deprecation notice must be an adjacent string.');
      return;
    }
    final List<StringLiteral> strings = deprecationNotice.strings;
    final Iterator<StringLiteral> deprecationMessageIterator = strings.iterator;
    final bool isNotEmpty = deprecationMessageIterator.moveNext();
    assert(isNotEmpty); // An AdjacentString always has 2 or more string literals.

    final [...List<StringLiteral> messageLiterals, StringLiteral versionLiteral] = strings;

    // Verify the version literal has the correct pattern.
    final RegExpMatch? versionMatch = versionLiteral is SimpleStringLiteral
        ? deprecationVersionPattern.firstMatch(versionLiteral.value)
        : null;
    if (versionMatch == null) {
      _addErrorWithLineInfo(
        versionLiteral,
        error:
            'Deprecation notice must end with a line saying "This feature was deprecated after v<version>.".',
      );
      return;
    }

    final int major = int.parse(versionMatch.namedGroup('major')!);
    final int minor = int.parse(versionMatch.namedGroup('minor')!);
    final int patch = int.parse(versionMatch.namedGroup('patch')!);
    final hasBuild = versionMatch.namedGroup('build') != null;
    // There was a beta release that was mistakenly labeled 3.1.0 without a build.
    final bool specialBeta = major == 3 && minor == 1 && patch == 0;
    if (!specialBeta && (major > 1 || (major == 1 && minor >= 20))) {
      if (!hasBuild) {
        _addErrorWithLineInfo(
          versionLiteral,
          error:
              'Deprecation notice does not accurately indicate a beta branch version number; please see https://docs.flutter.dev/install/archive to find the latest beta build version number.',
        );
        return;
      }
    }

    // Verify the version literal has the correct pattern.
    assert(messageLiterals.isNotEmpty); // An AdjacentString always has 2 or more string literals.
    for (final message in messageLiterals) {
      if (message is! SingleStringLiteral) {