analyzer: Use docImport scope in field and top-level variable declarations

Fixes https://github.com/dart-lang/sdk/issues/56100

Also privatize and inline some code.

Cq-Include-Trybots: luci.dart.try:flutter-analyze-try,analyzer-win-release-try,pkg-win-release-try
Change-Id: Ie9fea09cd719a62d2ef58373deaeb1fd43fa9cef
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/373601
Commit-Queue: Sam Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index f386972..a58b64c 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -4483,28 +4483,20 @@
         element.typeParameters,
       );
       node.nameScope = nameScope;
-      visitClassDeclarationInScope(node);
+      node.typeParameters?.accept(this);
+      node.extendsClause?.accept(this);
+      node.withClause?.accept(this);
+      node.implementsClause?.accept(this);
+      node.nativeClause?.accept(this);
 
       nameScope = InstanceScope(nameScope, element);
-      visitClassMembersInScope(node);
+      _visitDocumentationComment(node.documentationComment);
+      node.members.accept(this);
     } finally {
       nameScope = outerScope;
     }
   }
 
-  void visitClassDeclarationInScope(ClassDeclaration node) {
-    node.typeParameters?.accept(this);
-    node.extendsClause?.accept(this);
-    node.withClause?.accept(this);
-    node.implementsClause?.accept(this);
-    node.nativeClause?.accept(this);
-  }
-
-  void visitClassMembersInScope(ClassDeclarationImpl node) {
-    visitDocumentationComment(node.documentationComment);
-    node.members.accept(this);
-  }
-
   @override
   void visitClassTypeAlias(covariant ClassTypeAliasImpl node) {
     node.metadata.accept(this);
@@ -4525,7 +4517,7 @@
     // Note: we don't visit metadata because it's not inside the class type
     // alias's type parameter scope.  It was already visited in
     // [visitClassTypeAlias].
-    visitDocumentationComment(node.documentationComment);
+    _visitDocumentationComment(node.documentationComment);
     node.typeParameters?.accept(this);
     node.superclass.accept(this);
     node.withClause.accept(this);
@@ -4555,7 +4547,7 @@
           element,
         );
         node.initializers.accept(this);
-        visitDocumentationComment(node.documentationComment);
+        _visitDocumentationComment(node.documentationComment);
       } finally {
         nameScope = outerScope;
       }
@@ -4583,25 +4575,6 @@
     super.visitDeclaredIdentifier(node);
   }
 
-  /// Visits a documentation comment with a [DocImportScope] that encloses the
-  /// current [nameScope].
-  void visitDocumentationComment(CommentImpl? node) {
-    if (node == null) return;
-
-    Scope outerScope = nameScope;
-    Scope docImportInnerScope = _docImportScope.innerScope;
-    try {
-      _docImportScope.innerScope = nameScope;
-      nameScope = _docImportScope;
-
-      node.nameScope = nameScope;
-      node.accept(this);
-    } finally {
-      nameScope = outerScope;
-      _docImportScope.innerScope = docImportInnerScope;
-    }
-  }
-
   @override
   void visitDoStatement(DoStatement node) {
     ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
@@ -4630,27 +4603,19 @@
         element.typeParameters,
       );
       node.nameScope = nameScope;
-      visitEnumDeclarationInScope(node);
+      node.typeParameters?.accept(this);
+      node.withClause?.accept(this);
+      node.implementsClause?.accept(this);
 
       nameScope = InstanceScope(nameScope, element);
-      visitEnumMembersInScope(node);
+      _visitDocumentationComment(node.documentationComment);
+      node.constants.accept(this);
+      node.members.accept(this);
     } finally {
       nameScope = outerScope;
     }
   }
 
-  void visitEnumDeclarationInScope(EnumDeclaration node) {
-    node.typeParameters?.accept(this);
-    node.withClause?.accept(this);
-    node.implementsClause?.accept(this);
-  }
-
-  void visitEnumMembersInScope(covariant EnumDeclarationImpl node) {
-    visitDocumentationComment(node.documentationComment);
-    node.constants.accept(this);
-    node.members.accept(this);
-  }
-
   @override
   void visitExpressionFunctionBody(covariant ExpressionFunctionBodyImpl node) {
     node.nameScope = nameScope;
@@ -4684,7 +4649,7 @@
   }
 
   void visitExtensionMembersInScope(ExtensionDeclarationImpl node) {
-    visitDocumentationComment(node.documentationComment);
+    _visitDocumentationComment(node.documentationComment);
     node.members.accept(this);
   }
 
@@ -4707,7 +4672,7 @@
       node.implementsClause?.accept(this);
 
       nameScope = InstanceScope(nameScope, element);
-      visitDocumentationComment(node.documentationComment);
+      _visitDocumentationComment(node.documentationComment);
       node.members.accept(this);
     } finally {
       nameScope = outerScope;
@@ -4715,6 +4680,13 @@
   }
 
   @override
+  void visitFieldDeclaration(covariant FieldDeclarationImpl node) {
+    node.metadata.accept(this);
+    _visitDocumentationComment(node.documentationComment);
+    node.fields.accept(this);
+  }
+
+  @override
   void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
     //
     // We visit the iterator before the loop variable because the loop variable
@@ -4859,7 +4831,7 @@
       if (parent is FunctionDeclarationImpl) {
         // We have already created a function scope and don't need to do so again.
         super.visitFunctionExpression(node);
-        visitDocumentationComment(parent.documentationComment);
+        _visitDocumentationComment(parent.documentationComment);
         return;
       }
 
@@ -4897,7 +4869,7 @@
     node.parameters.accept(this);
     // Visiting the parameters added them to the scope as a side effect.  So it
     // is safe to visit the documentation comment now.
-    visitDocumentationComment(node.documentationComment);
+    _visitDocumentationComment(node.documentationComment);
   }
 
   @override
@@ -4922,7 +4894,7 @@
     // Note: we don't visit metadata because it's not inside the function typed
     // formal parameter's type parameter scope.  It was already visited in
     // [visitFunctionTypedFormalParameter].
-    visitDocumentationComment(node.documentationComment);
+    _visitDocumentationComment(node.documentationComment);
     node.returnType?.accept(this);
     node.typeParameters?.accept(this);
     node.parameters.accept(this);
@@ -4965,7 +4937,7 @@
             TypeParameterScope(nameScope, aliasedElement.typeParameters),
             aliasedElement.parameters);
       }
-      visitDocumentationComment(node.documentationComment);
+      _visitDocumentationComment(node.documentationComment);
     } finally {
       nameScope = outerScope;
     }
@@ -5022,7 +4994,7 @@
   @override
   void visitLibraryDirective(covariant LibraryDirectiveImpl node) {
     node.metadata.accept(this);
-    visitDocumentationComment(node.documentationComment);
+    _visitDocumentationComment(node.documentationComment);
   }
 
   @override
@@ -5031,30 +5003,24 @@
     node.metadata.accept(this);
     Scope outerScope = nameScope;
     try {
-      ExecutableElement element = node.declaredElement!;
+      var element = node.declaredElement!;
       nameScope = TypeParameterScope(
         nameScope,
         element.typeParameters,
       );
       node.nameScope = nameScope;
-      visitMethodDeclarationInScope(node);
+      node.returnType?.accept(this);
+      node.typeParameters?.accept(this);
+      node.parameters?.accept(this);
+      // Visiting the parameters added them to the scope as a side effect. So it
+      // is safe to visit the documentation comment now.
+      _visitDocumentationComment(node.documentationComment);
+      node.body.accept(this);
     } finally {
       nameScope = outerScope;
     }
   }
 
-  void visitMethodDeclarationInScope(MethodDeclarationImpl node) {
-    // Note: we don't visit metadata because it's not inside the method's type
-    // parameter scope.  It was already visited in [visitMethodDeclaration].
-    node.returnType?.accept(this);
-    node.typeParameters?.accept(this);
-    node.parameters?.accept(this);
-    // Visiting the parameters added them to the scope as a side effect.  So it
-    // is safe to visit the documentation comment now.
-    visitDocumentationComment(node.documentationComment);
-    node.body.accept(this);
-  }
-
   @override
   void visitMethodInvocation(MethodInvocation node) {
     // Only visit the method name if there's no real target (so this is an
@@ -5094,7 +5060,7 @@
   }
 
   void visitMixinMembersInScope(MixinDeclarationImpl node) {
-    visitDocumentationComment(node.documentationComment);
+    _visitDocumentationComment(node.documentationComment);
     node.members.accept(this);
   }
 
@@ -5262,6 +5228,14 @@
   }
 
   @override
+  void visitTopLevelVariableDeclaration(
+      covariant TopLevelVariableDeclarationImpl node) {
+    node.metadata.accept(this);
+    _visitDocumentationComment(node.documentationComment);
+    node.variables.accept(this);
+  }
+
+  @override
   void visitVariableDeclaration(VariableDeclaration node) {
     super.visitVariableDeclaration(node);
 
@@ -5360,6 +5334,25 @@
     }
   }
 
+  /// Visits a documentation comment with a [DocImportScope] that encloses the
+  /// current [nameScope].
+  void _visitDocumentationComment(CommentImpl? node) {
+    if (node == null) return;
+
+    Scope outerScope = nameScope;
+    Scope docImportInnerScope = _docImportScope.innerScope;
+    try {
+      _docImportScope.innerScope = nameScope;
+      nameScope = _docImportScope;
+
+      node.nameScope = nameScope;
+      node.accept(this);
+    } finally {
+      nameScope = outerScope;
+      _docImportScope.innerScope = docImportInnerScope;
+    }
+  }
+
   void _visitIf(IfElementOrStatementImpl node) {
     node.expression.accept(this);
 
diff --git a/pkg/analyzer/test/src/dart/resolution/comment_test.dart b/pkg/analyzer/test/src/dart/resolution/comment_test.dart
index 56e56d5..7ad7bb3 100644
--- a/pkg/analyzer/test/src/dart/resolution/comment_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/comment_test.dart
@@ -1719,6 +1719,28 @@
 ''');
   }
 
+  test_docImport_onField() async {
+    newFile('$testPackageLibPath/foo.dart', r'''
+class A {}
+''');
+    await assertNoErrorsInCode(r'''
+/// @docImport 'foo.dart';
+library;
+class C {
+  /// Text [A].
+  int x = 1;
+}
+''');
+
+    assertResolvedNodeText(findNode.commentReference('A]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: A
+    staticElement: package:test/foo.dart::@class::A
+    staticType: null
+''');
+  }
+
   test_docImport_onLibrary() async {
     newFile('$testPackageLibPath/foo.dart', r'''
 class A {}
@@ -1739,6 +1761,26 @@
 ''');
   }
 
+  test_docImport_onTopLevelVariable() async {
+    newFile('$testPackageLibPath/foo.dart', r'''
+class A {}
+''');
+    await assertNoErrorsInCode(r'''
+/// @docImport 'foo.dart';
+library;
+/// Text [A].
+int x = 1;
+''');
+
+    assertResolvedNodeText(findNode.commentReference('A]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: A
+    staticElement: package:test/foo.dart::@class::A
+    staticType: null
+''');
+  }
+
   test_docImport_topLevelFunction() async {
     newFile('$testPackageLibPath/foo.dart', r'''
 void foo() {}