analyzer: mark synthetic comment references as such

This allows us to ignore synthetic comment references in the
`comment_references` lint rule.

Fixes https://github.com/dart-lang/linter/issues/5009

Cq-Include-Trybots: luci.dart.try:flutter-analyze-try,analyzer-win-release-try,pkg-win-release-try
Change-Id: I57d3878dd938bcfba6c2cbea62d784ff6237c9a6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/374122
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index aea74a6..b8c2c66 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -3373,12 +3373,16 @@
 
   CommentReferableExpressionImpl _expression;
 
+  @override
+  final bool isSynthetic;
+
   /// Initializes a newly created reference to a Dart element.
   ///
   /// The [newKeyword] can be `null` if the reference isn't to a constructor.
   CommentReferenceImpl({
     required this.newKeyword,
     required CommentReferableExpressionImpl expression,
+    required this.isSynthetic,
   }) : _expression = expression {
     _becomeParentOf(_expression);
   }
diff --git a/pkg/analyzer/lib/src/fasta/doc_comment_builder.dart b/pkg/analyzer/lib/src/fasta/doc_comment_builder.dart
index 1e4331c..13c77c5 100644
--- a/pkg/analyzer/lib/src/fasta/doc_comment_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/doc_comment_builder.dart
@@ -316,9 +316,11 @@
         } else {
           var referenceStart = index;
           index = content.indexOf(']', index);
+          var isSynthetic = false;
           if (index == -1 || index >= end) {
             // Recovery: terminating ']' is not typed yet.
             index = _findCommentReferenceEnd(content, referenceStart, end);
+            isSynthetic = true;
           }
           if (ch != 0x27 /* `'` */ && ch != 0x22 /* `"` */) {
             if (_isLinkText(content, index)) {
@@ -328,6 +330,7 @@
               var reference = _parseOneCommentReference(
                 content.substring(referenceStart, index),
                 offset + referenceStart,
+                isSynthetic: isSynthetic,
               );
               if (reference != null) {
                 _references.add(reference);
@@ -616,7 +619,8 @@
   /// Parses the [source] text, found at [offset] in a single comment reference.
   ///
   /// Returns `null` if the text could not be parsed as a comment reference.
-  CommentReferenceImpl? _parseOneCommentReference(String source, int offset) {
+  CommentReferenceImpl? _parseOneCommentReference(String source, int offset,
+      {required bool isSynthetic}) {
     var result = scanString(source);
     if (result.hasErrors) {
       return null;
@@ -652,9 +656,10 @@
       token = secondPeriod.next!;
     }
     if (token.isEof) {
-      // Recovery: Insert a synthetic identifier for code completion
+      // Recovery: Insert a synthetic identifier for code completion.
       token = _parser.rewriter.insertSyntheticIdentifier(
           secondPeriod ?? newKeyword ?? _parser.syntheticPreviousToken(token));
+      isSynthetic = true;
       if (begin == token.next!) {
         begin = token;
       }
@@ -675,6 +680,7 @@
           secondToken,
           secondPeriod,
           token,
+          isSynthetic: isSynthetic,
         );
       }
     } else {
@@ -690,6 +696,7 @@
             secondToken,
             secondPeriod,
             token,
+            isSynthetic: isSynthetic,
           );
         }
         var keyword = token.keyword;
@@ -713,10 +720,10 @@
   /// Parses the parameters into a [CommentReferenceImpl].
   ///
   /// If the reference begins with `new `, then pass the Token associated with
-  /// that text as [newToken].
+  /// that text as [newKeyword].
   ///
   /// If the reference contains a single identifier or operator (aside from the
-  /// optional [newToken]), then pass the associated Token as
+  /// optional [newKeyword]), then pass the associated Token as
   /// [identifierOrOperator].
   ///
   /// If the reference contains two identifiers separated by a period, then pass
@@ -737,8 +744,9 @@
     Token? firstPeriod,
     Token? secondToken,
     Token? secondPeriod,
-    Token identifierOrOperator,
-  ) {
+    Token identifierOrOperator, {
+    required bool isSynthetic,
+  }) {
     // Adjust the token offsets to match the enclosing comment token.
     var token = begin;
     do {
@@ -761,6 +769,7 @@
       return CommentReferenceImpl(
         newKeyword: newKeyword,
         expression: expression,
+        isSynthetic: isSynthetic,
       );
     } else if (secondToken != null) {
       var expression = PrefixedIdentifierImpl(
@@ -771,11 +780,13 @@
       return CommentReferenceImpl(
         newKeyword: newKeyword,
         expression: expression,
+        isSynthetic: isSynthetic,
       );
     } else {
       return CommentReferenceImpl(
         newKeyword: newKeyword,
         expression: identifier,
+        isSynthetic: isSynthetic,
       );
     }
   }
diff --git a/pkg/linter/lib/src/rules/comment_references.dart b/pkg/linter/lib/src/rules/comment_references.dart
index 2959bbf..8ea91d6 100644
--- a/pkg/linter/lib/src/rules/comment_references.dart
+++ b/pkg/linter/lib/src/rules/comment_references.dart
@@ -123,6 +123,7 @@
 
   @override
   void visitCommentReference(CommentReference node) {
+    if (node.isSynthetic) return;
     var expression = node.expression;
     if (expression.isSynthetic) return;
     if (expression is Identifier &&
diff --git a/pkg/linter/test/rules/comment_references_test.dart b/pkg/linter/test/rules/comment_references_test.dart
index ea5a2bb..54c5fde 100644
--- a/pkg/linter/test/rules/comment_references_test.dart
+++ b/pkg/linter/test/rules/comment_references_test.dart
@@ -50,6 +50,15 @@
 ''');
   }
 
+  test_markdown_inlineLink_textSpansLines() async {
+    await assertNoDiagnostics(r'''
+/// A [link spanning
+/// multiple lines](http://tools.ietf.org/html/rfc6234)
+///
+class C {}
+''');
+  }
+
   test_markdown_inlineLink_withTitle() async {
     await assertNoDiagnostics(r'''
 /// A link to [Sha256](http://tools.ietf.org/html/rfc6234 "Some") hash function.