Build new elements for GenericFunctionType in DeclarationResolver.

It seems that we don't really need to have the same elements for them
for resynthesizing element model and resolved AST.

R=brianwilkerson@google.com

Change-Id: I05efdf90671f596f2f8b85a3808eb2c4c87ea8c1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105003
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/generated/declaration_resolver.dart b/pkg/analyzer/lib/src/generated/declaration_resolver.dart
index e336eb9..d8abec8 100644
--- a/pkg/analyzer/lib/src/generated/declaration_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/declaration_resolver.dart
@@ -32,13 +32,6 @@
   /// element model.
   ElementWalker _walker;
 
-  /// Is `true` if the current [ClassDeclaration] has a const constructor.
-  bool _hasConstConstructor = false;
-
-  /// The number of [GenericFunctionType] nodes that we encountered so far.
-  /// We use it to request the corresponding resolved node.
-  int _nextGenericFunctionTypeId = 0;
-
   DeclarationResolver();
 
   /// Resolve the declarations within the given compilation [unit] to the
@@ -86,14 +79,6 @@
 
   @override
   void visitClassDeclaration(ClassDeclaration node) {
-    _hasConstConstructor = false;
-    for (var member in node.members) {
-      if (member is ConstructorDeclaration && member.constKeyword != null) {
-        _hasConstConstructor = true;
-        break;
-      }
-    }
-
     ClassElement element = _match(node.name, _walker.getClass());
     _walk(new ElementWalker.forClass(element), () {
       super.visitClassDeclaration(node);
@@ -203,10 +188,6 @@
     super.visitFieldDeclaration(node);
     FieldElement firstFieldElement = node.fields.variables[0].declaredElement;
     resolveMetadata(node, node.metadata, firstFieldElement);
-    if (node.fields.isConst ||
-        !node.isStatic && node.fields.isFinal && _hasConstConstructor) {
-      _consumeGenericFunctionTypeIds(node.fields);
-    }
   }
 
   @override
@@ -289,19 +270,20 @@
 
   @override
   void visitGenericFunctionType(GenericFunctionType node) {
+    if (AnalysisDriver.useSummary2 && _enclosingUnit.linkedContext != null) {
+      var builder = new LocalElementBuilder(ElementHolder(), _enclosingUnit);
+      node.accept(builder);
+
+      var nodeImpl = node as GenericFunctionTypeImpl;
+      _enclosingUnit.encloseElement(
+        nodeImpl.declaredElement as GenericFunctionTypeElementImpl,
+      );
+      return;
+    }
     if (_walker.elementBuilder != null) {
       _walker.elementBuilder.visitGenericFunctionType(node);
     } else {
-      Element element;
-      if (AnalysisDriver.useSummary2 && _enclosingUnit.linkedContext != null) {
-        var id = _nextGenericFunctionTypeId++;
-        var context = _enclosingUnit.linkedContext;
-        var linkedNode = context.getGenericFunctionType(id);
-        element = linkedNode.declaredElement;
-        (node as GenericFunctionTypeImpl).declaredElement = element;
-      } else {
-        element = node.type?.element;
-      }
+      var element = node.type?.element;
       if (element is GenericFunctionTypeElement) {
         _setGenericFunctionType(node.returnType, element.returnType);
         _walk(new ElementWalker.forGenericFunctionType(element), () {
@@ -454,9 +436,6 @@
     super.visitTopLevelVariableDeclaration(node);
     VariableElement firstElement = node.variables.variables[0].declaredElement;
     resolveMetadata(node, node.metadata, firstElement);
-    if (node.variables.isConst) {
-      _consumeGenericFunctionTypeIds(node.variables);
-    }
   }
 
   @override
@@ -508,14 +487,6 @@
     }
   }
 
-  /// See [_ConsumeGenericFunctionTypeIdsVisitor].
-  void _consumeGenericFunctionTypeIds(VariableDeclarationList node) {
-    if (AnalysisDriver.useSummary2) {
-      var visitor = _ConsumeGenericFunctionTypeIdsVisitor(this);
-      node.variables.accept(visitor);
-    }
-  }
-
   /// Updates [identifier] to point to [element], after ensuring that the
   /// element has the expected name.
   ///
@@ -991,24 +962,6 @@
   static bool _isNotSynthetic(Element e) => !e.isSynthetic;
 }
 
-/// For consistency we set identifiers for [GenericFunctionType]s in constant
-/// variable initializers, and instance final fields of classes with constant
-/// constructors. However [DeclarationResolver] does not visit these
-/// initializers, in builds separate local elements. We still need to consume
-/// them to ensure that identifiers expected by the element model, and by
-/// [DeclarationResolver] match.
-class _ConsumeGenericFunctionTypeIdsVisitor extends RecursiveAstVisitor<void> {
-  final DeclarationResolver resolver;
-
-  _ConsumeGenericFunctionTypeIdsVisitor(this.resolver);
-
-  @override
-  void visitGenericFunctionType(GenericFunctionType node) {
-    resolver._nextGenericFunctionTypeId++;
-    super.visitGenericFunctionType(node);
-  }
-}
-
 class _ElementMismatchException extends AnalysisException {
   /// Creates an exception to refer to the given [compilationUnit], [element],
   /// and [cause].
diff --git a/pkg/analyzer/lib/src/summary/format.dart b/pkg/analyzer/lib/src/summary/format.dart
index d9bbbf1..043be91 100644
--- a/pkg/analyzer/lib/src/summary/format.dart
+++ b/pkg/analyzer/lib/src/summary/format.dart
@@ -17212,7 +17212,6 @@
 class LinkedNodeUnitBuilder extends Object
     with _LinkedNodeUnitMixin
     implements idl.LinkedNodeUnit {
-  List<LinkedNodeBuilder> _genericFunctionTypes;
   bool _isNNBD;
   bool _isSynthetic;
   List<int> _lineStarts;
@@ -17221,20 +17220,6 @@
   String _uriStr;
 
   @override
-  List<LinkedNodeBuilder> get genericFunctionTypes =>
-      _genericFunctionTypes ??= <LinkedNodeBuilder>[];
-
-  /// All generic function types in the unit - in generic type aliases, or used
-  /// directly as type annotations.
-  ///
-  /// They are requested in two cases: when we are reading a node that contains
-  /// them (e.g. a return type of a method), or when we run over unresolved
-  /// AST in declaration resolver.
-  set genericFunctionTypes(List<LinkedNodeBuilder> value) {
-    this._genericFunctionTypes = value;
-  }
-
-  @override
   bool get isNNBD => _isNNBD ??= false;
 
   set isNNBD(bool value) {
@@ -17279,15 +17264,13 @@
   }
 
   LinkedNodeUnitBuilder(
-      {List<LinkedNodeBuilder> genericFunctionTypes,
-      bool isNNBD,
+      {bool isNNBD,
       bool isSynthetic,
       List<int> lineStarts,
       LinkedNodeBuilder node,
       UnlinkedTokensBuilder tokens,
       String uriStr})
-      : _genericFunctionTypes = genericFunctionTypes,
-        _isNNBD = isNNBD,
+      : _isNNBD = isNNBD,
         _isSynthetic = isSynthetic,
         _lineStarts = lineStarts,
         _node = node,
@@ -17296,7 +17279,6 @@
 
   /// Flush [informative] data recursively.
   void flushInformative() {
-    _genericFunctionTypes?.forEach((b) => b.flushInformative());
     _lineStarts = null;
     _node?.flushInformative();
     _tokens?.flushInformative();
@@ -17310,27 +17292,14 @@
     signature.addBool(this._node != null);
     this._node?.collectApiSignature(signature);
     signature.addBool(this._isSynthetic == true);
-    if (this._genericFunctionTypes == null) {
-      signature.addInt(0);
-    } else {
-      signature.addInt(this._genericFunctionTypes.length);
-      for (var x in this._genericFunctionTypes) {
-        x?.collectApiSignature(signature);
-      }
-    }
     signature.addBool(this._isNNBD == true);
   }
 
   fb.Offset finish(fb.Builder fbBuilder) {
-    fb.Offset offset_genericFunctionTypes;
     fb.Offset offset_lineStarts;
     fb.Offset offset_node;
     fb.Offset offset_tokens;
     fb.Offset offset_uriStr;
-    if (!(_genericFunctionTypes == null || _genericFunctionTypes.isEmpty)) {
-      offset_genericFunctionTypes = fbBuilder.writeList(
-          _genericFunctionTypes.map((b) => b.finish(fbBuilder)).toList());
-    }
     if (!(_lineStarts == null || _lineStarts.isEmpty)) {
       offset_lineStarts = fbBuilder.writeListUint32(_lineStarts);
     }
@@ -17344,11 +17313,8 @@
       offset_uriStr = fbBuilder.writeString(_uriStr);
     }
     fbBuilder.startTable();
-    if (offset_genericFunctionTypes != null) {
-      fbBuilder.addOffset(5, offset_genericFunctionTypes);
-    }
     if (_isNNBD == true) {
-      fbBuilder.addBool(6, true);
+      fbBuilder.addBool(5, true);
     }
     if (_isSynthetic == true) {
       fbBuilder.addBool(3, true);
@@ -17385,7 +17351,6 @@
 
   _LinkedNodeUnitImpl(this._bc, this._bcOffset);
 
-  List<idl.LinkedNode> _genericFunctionTypes;
   bool _isNNBD;
   bool _isSynthetic;
   List<int> _lineStarts;
@@ -17394,16 +17359,8 @@
   String _uriStr;
 
   @override
-  List<idl.LinkedNode> get genericFunctionTypes {
-    _genericFunctionTypes ??=
-        const fb.ListReader<idl.LinkedNode>(const _LinkedNodeReader())
-            .vTableGet(_bc, _bcOffset, 5, const <idl.LinkedNode>[]);
-    return _genericFunctionTypes;
-  }
-
-  @override
   bool get isNNBD {
-    _isNNBD ??= const fb.BoolReader().vTableGet(_bc, _bcOffset, 6, false);
+    _isNNBD ??= const fb.BoolReader().vTableGet(_bc, _bcOffset, 5, false);
     return _isNNBD;
   }
 
@@ -17444,9 +17401,6 @@
   @override
   Map<String, Object> toJson() {
     Map<String, Object> _result = <String, Object>{};
-    if (genericFunctionTypes.isNotEmpty)
-      _result["genericFunctionTypes"] =
-          genericFunctionTypes.map((_value) => _value.toJson()).toList();
     if (isNNBD != false) _result["isNNBD"] = isNNBD;
     if (isSynthetic != false) _result["isSynthetic"] = isSynthetic;
     if (lineStarts.isNotEmpty) _result["lineStarts"] = lineStarts;
@@ -17458,7 +17412,6 @@
 
   @override
   Map<String, Object> toMap() => {
-        "genericFunctionTypes": genericFunctionTypes,
         "isNNBD": isNNBD,
         "isSynthetic": isSynthetic,
         "lineStarts": lineStarts,
diff --git a/pkg/analyzer/lib/src/summary/format.fbs b/pkg/analyzer/lib/src/summary/format.fbs
index 69d782b..4ff5313 100644
--- a/pkg/analyzer/lib/src/summary/format.fbs
+++ b/pkg/analyzer/lib/src/summary/format.fbs
@@ -2036,15 +2036,7 @@
 
 /// Information about a single library in a [LinkedNodeLibrary].
 table LinkedNodeUnit {
-  /// All generic function types in the unit - in generic type aliases, or used
-  /// directly as type annotations.
-  ///
-  /// They are requested in two cases: when we are reading a node that contains
-  /// them (e.g. a return type of a method), or when we run over unresolved
-  /// AST in declaration resolver.
-  genericFunctionTypes:[LinkedNode] (id: 5);
-
-  isNNBD:bool (id: 6);
+  isNNBD:bool (id: 5);
 
   isSynthetic:bool (id: 3);
 
diff --git a/pkg/analyzer/lib/src/summary/idl.dart b/pkg/analyzer/lib/src/summary/idl.dart
index 5ad8160..1dc7354 100644
--- a/pkg/analyzer/lib/src/summary/idl.dart
+++ b/pkg/analyzer/lib/src/summary/idl.dart
@@ -2036,16 +2036,7 @@
 
 /// Information about a single library in a [LinkedNodeLibrary].
 abstract class LinkedNodeUnit extends base.SummaryClass {
-  /// All generic function types in the unit - in generic type aliases, or used
-  /// directly as type annotations.
-  ///
-  /// They are requested in two cases: when we are reading a node that contains
-  /// them (e.g. a return type of a method), or when we run over unresolved
-  /// AST in declaration resolver.
   @Id(5)
-  List<LinkedNode> get genericFunctionTypes;
-
-  @Id(6)
   bool get isNNBD;
 
   @Id(3)
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
index 723c8a3..864b976 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
@@ -52,67 +52,6 @@
 
   InterfaceType get _stringType => _unitContext.typeProvider.stringType;
 
-  /// This method is invoked by [LinkedUnitContext] to finish reading.
-  void readGenericFunctionTypeFinish(
-    LinkedNode data,
-    GenericFunctionType node,
-  ) {
-    var typeParameterListData = data.genericFunctionType_typeParameters;
-    if (typeParameterListData != null) {
-      var dataList = typeParameterListData.typeParameterList_typeParameters;
-      var typeParameters = node.typeParameters.typeParameters;
-      for (var i = 0; i < dataList.length; ++i) {
-        var data = dataList[i];
-        var node = typeParameters[i];
-        node.bound = _readNode(data.typeParameter_bound);
-      }
-    }
-    node.returnType = readNode(data.genericFunctionType_returnType);
-    node.parameters = _readNode(data.genericFunctionType_formalParameters);
-  }
-
-  /// This method is invoked by [LinkedUnitContext] to perform shallow reading.
-  ///
-  /// It reads [TypeParameter] names, and creates [GenericFunctionType] node,
-  /// so that [LinkedUnitContext] can create elements for these nodes.
-  ///
-  /// But we cannot read the return type and formal parameters yet, until the
-  /// corresponding elements are created.
-  GenericFunctionType readGenericFunctionTypeShallow(LinkedNode data) {
-    TypeParameterList typeParameterList;
-    var typeParameterListData = data.genericFunctionType_typeParameters;
-    if (typeParameterListData != null) {
-      var dataList = typeParameterListData.typeParameterList_typeParameters;
-      var typeParameters = List<TypeParameter>(dataList.length);
-      for (var i = 0; i < dataList.length; ++i) {
-        var data = dataList[i];
-        typeParameters[i] = astFactory.typeParameter(
-          _readNode(data.annotatedNode_comment),
-          _readNodeList(data.annotatedNode_metadata),
-          _declaredIdentifier(data),
-          data.typeParameter_bound != null ? _Tokens.EXTENDS : null,
-          null,
-        );
-      }
-      typeParameterList = astFactory.typeParameterList(
-        _Tokens.LT,
-        typeParameters,
-        _Tokens.GT,
-      );
-    }
-
-    GenericFunctionTypeImpl node = astFactory.genericFunctionType(
-      null,
-      _Tokens.FUNCTION,
-      typeParameterList,
-      null,
-      question:
-          AstBinaryFlags.hasQuestion(data.flags) ? _Tokens.QUESTION : null,
-    );
-    node.type = _readType(data.genericFunctionType_type);
-    return node;
-  }
-
   AstNode readNode(LinkedNode data) {
     timerAstBinaryReader.start();
     try {
@@ -846,7 +785,58 @@
 
   GenericFunctionType _read_genericFunctionType(LinkedNode data) {
     var id = data.genericFunctionType_id;
-    return _unitContext.getGenericFunctionType(id);
+
+    // Read type parameters, without bounds, to avoid forward references.
+    TypeParameterList typeParameterList;
+    var typeParameterListData = data.genericFunctionType_typeParameters;
+    if (typeParameterListData != null) {
+      var dataList = typeParameterListData.typeParameterList_typeParameters;
+      var typeParameters = List<TypeParameter>(dataList.length);
+      for (var i = 0; i < dataList.length; ++i) {
+        var data = dataList[i];
+        typeParameters[i] = astFactory.typeParameter(
+          _readNode(data.annotatedNode_comment),
+          _readNodeList(data.annotatedNode_metadata),
+          _declaredIdentifier(data),
+          data.typeParameter_bound != null ? _Tokens.EXTENDS : null,
+          null,
+        );
+      }
+      typeParameterList = astFactory.typeParameterList(
+        _Tokens.LT,
+        typeParameters,
+        _Tokens.GT,
+      );
+    }
+
+    GenericFunctionTypeImpl node = astFactory.genericFunctionType(
+      null,
+      _Tokens.FUNCTION,
+      typeParameterList,
+      null,
+      question:
+          AstBinaryFlags.hasQuestion(data.flags) ? _Tokens.QUESTION : null,
+    );
+    node.type = _readType(data.genericFunctionType_type);
+
+    // Create the node element, so now type parameter elements are available.
+    LazyAst.setGenericFunctionTypeId(node, id);
+    _unitContext.createGenericFunctionTypeElement(id, node);
+
+    // Finish reading.
+    if (typeParameterListData != null) {
+      var dataList = typeParameterListData.typeParameterList_typeParameters;
+      var typeParameters = typeParameterList.typeParameters;
+      for (var i = 0; i < dataList.length; ++i) {
+        var data = dataList[i];
+        var node = typeParameters[i];
+        node.bound = _readNode(data.typeParameter_bound);
+      }
+    }
+    node.returnType = readNode(data.genericFunctionType_returnType);
+    node.parameters = _readNode(data.genericFunctionType_formalParameters);
+
+    return node;
   }
 
   GenericTypeAlias _read_genericTypeAlias(LinkedNode data) {
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
index 8588d04..ea3ab89 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
@@ -28,9 +28,6 @@
 class AstBinaryWriter extends ThrowingAstVisitor<LinkedNodeBuilder> {
   final LinkingBundleContext _linkingContext;
 
-  /// The list stored [GenericFunctionType]s, as visited in depth-first order.
-  final List<LinkedNodeBuilder> genericFunctionTypes = [];
-
   /// Is `true` if the current [ClassDeclaration] has a const constructor,
   /// so initializers of final fields should be written.
   bool _hasConstConstructor = false;
@@ -682,10 +679,9 @@
   LinkedNodeBuilder visitGenericFunctionType(GenericFunctionType node) {
     var id = LazyAst.getGenericFunctionTypeId(node);
     assert(id != null);
-    assert(genericFunctionTypes.length == id);
-    genericFunctionTypes.add(null);
 
     var builder = LinkedNodeBuilder.genericFunctionType(
+      genericFunctionType_id: id,
       genericFunctionType_returnType: node.returnType?.accept(this),
       genericFunctionType_typeParameters: node.typeParameters?.accept(this),
       genericFunctionType_formalParameters: node.parameters.accept(this),
@@ -696,12 +692,7 @@
     );
     _writeActualReturnType(builder, node);
 
-    builder.genericFunctionType_id = id;
-    genericFunctionTypes[id] = builder;
-
-    return LinkedNodeBuilder.genericFunctionType(
-      genericFunctionType_id: id,
-    );
+    return builder;
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/summary2/link.dart b/pkg/analyzer/lib/src/summary2/link.dart
index 7d9f6f5..784b60e 100644
--- a/pkg/analyzer/lib/src/summary2/link.dart
+++ b/pkg/analyzer/lib/src/summary2/link.dart
@@ -192,7 +192,6 @@
             lineStarts: unit.lineInfo.lineStarts,
             node: unitLinkedNode,
             isNNBD: unit.featureSet.isEnabled(Feature.non_nullable),
-            genericFunctionTypes: writer.genericFunctionTypes,
           ),
         );
       }
diff --git a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
index 9ce9558..3e8d48e 100644
--- a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
@@ -5,7 +5,6 @@
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
@@ -148,14 +147,6 @@
       return _function(enclosing, reference);
     }
 
-    if (parentName == '@genericFunctionType') {
-      CompilationUnitElementImpl enclosing = elementOfReference(parent2);
-      var context = enclosing.linkedContext;
-      var id = int.parse(reference.name);
-      GenericFunctionTypeImpl node = context.getGenericFunctionType(id);
-      return node.declaredElement as GenericFunctionTypeElementImpl;
-    }
-
     if (parentName == '@getter' || parentName == '@setter') {
       var enclosing = elementOfReference(parent2);
       return _accessor(enclosing, reference);
diff --git a/pkg/analyzer/lib/src/summary2/linked_unit_context.dart b/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
index 31e301b..9f7385e 100644
--- a/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
@@ -28,10 +28,6 @@
   final bool isSynthetic;
   final LinkedNodeUnit data;
 
-  /// This list is filled lazily with [GenericFunctionType] nodes as they
-  /// are requested by [getGenericFunctionType].
-  List<GenericFunctionType> _genericFunctionTypeNodeList;
-
   AstBinaryReader _astReader;
 
   CompilationUnit _unit;
@@ -57,12 +53,6 @@
     _astReader = AstBinaryReader(this);
     _astReader.isLazy = unit == null;
 
-    if (data != null) {
-      _genericFunctionTypeNodeList = List<GenericFunctionType>(
-        data.genericFunctionTypes.length,
-      );
-    }
-
     _unit = unit;
     _hasDirectivesRead = _unit != null;
   }
@@ -114,6 +104,17 @@
     return _unit;
   }
 
+  void createGenericFunctionTypeElement(int id, GenericFunctionTypeImpl node) {
+    var containerRef = this.reference.getChild('@genericFunctionType');
+    var reference = containerRef.getChild('$id');
+    var element = GenericFunctionTypeElementImpl.forLinkedNode(
+      this.reference.element,
+      reference,
+      node,
+    );
+    node.declaredElement = element;
+  }
+
   /// Return the [LibraryElement] referenced in the [node].
   LibraryElement directiveLibrary(UriBasedDirective node) {
     var uriStr = LazyDirective.getSelectedUri(node);
@@ -337,28 +338,6 @@
     }
   }
 
-  GenericFunctionTypeImpl getGenericFunctionType(int id) {
-    GenericFunctionTypeImpl node = _genericFunctionTypeNodeList[id];
-    if (node == null) {
-      var data = this.data.genericFunctionTypes[id];
-      node = _astReader.readGenericFunctionTypeShallow(data);
-      LazyAst.setGenericFunctionTypeId(node, id);
-      _genericFunctionTypeNodeList[id] = node;
-
-      var containerRef = this.reference.getChild('@genericFunctionType');
-      var reference = containerRef.getChild('$id');
-      var element = GenericFunctionTypeElementImpl.forLinkedNode(
-        this.reference.element,
-        reference,
-        node,
-      );
-      node.declaredElement = element;
-
-      _astReader.readGenericFunctionTypeFinish(data, node);
-    }
-    return node;
-  }
-
   Reference getGenericFunctionTypeReference(GenericFunctionType node) {
     var containerRef = reference.getChild('@genericFunctionType');
     var id = LazyAst.getGenericFunctionTypeId(node);
diff --git a/pkg/analyzer/lib/src/test_utilities/find_element.dart b/pkg/analyzer/lib/src/test_utilities/find_element.dart
index f8ff22c..bf87a5f 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_element.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_element.dart
@@ -498,4 +498,13 @@
     }
     throw StateError('Not found: $name');
   }
+
+  TopLevelVariableElement topVar(String name) {
+    for (var variable in definingUnit.topLevelVariables) {
+      if (variable.name == name) {
+        return variable;
+      }
+    }
+    throw StateError('Not found: $name');
+  }
 }