Refactor and add tests for creating an ElementMatcher for a node

The code that was moved contains some additional fixes. I'm happy to
break this up into two CLs if you'd prefer.

Change-Id: I4e2407d28b238ff75934fcf92a411d5fe0b94585
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/179364
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
index 946989a..524cdaa 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
@@ -6,14 +6,11 @@
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
-import 'package:analysis_server/src/services/correction/fix/data_driven/element_kind.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_manager.dart';
-import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart' show LibraryElement;
-import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:meta/meta.dart';
@@ -36,16 +33,11 @@
         importedUris.add(Uri.parse(uri));
       }
     }
-    var components = _computeComponents();
-    if (components == null) {
-      // If we couldn't compute the components it's because the node doesn't
-      // represent an element that can be transformed.
+    var matcher = ElementMatcher.forNode(node);
+    if (matcher == null) {
+      // The node doesn't represent an element that can be transformed.
       return;
     }
-    var matcher = ElementMatcher(
-        importedUris: importedUris,
-        components: components,
-        kinds: _kindsForNode(node));
     for (var set in _availableTransformSetsForLibrary(library)) {
       for (var transform
           in set.transformsFor(matcher, applyingBulkFixes: applyingBulkFixes)) {
@@ -63,174 +55,6 @@
     return TransformSetManager.instance.forLibrary(library);
   }
 
-  /// Return the components for the element associated with the given [node] by
-  /// looking at the parent of the [node].
-  List<String> _componentsFromParent(AstNode node) {
-    var parent = node.parent;
-    if (parent is ArgumentList) {
-      parent = parent.parent;
-    }
-    if (parent is Annotation) {
-      return [parent.constructorName?.name ?? '', parent.name.name];
-    } else if (parent is ExtensionOverride) {
-      return [parent.extensionName.name];
-    } else if (parent is InstanceCreationExpression) {
-      var constructorName = parent.constructorName;
-      return [constructorName.name?.name ?? '', constructorName.type.name.name];
-    } else if (parent is MethodInvocation) {
-      var target = _nameOfTarget(parent.realTarget);
-      if (target != null) {
-        return [parent.methodName.name, target];
-      }
-      var ancestor = parent.parent;
-      while (ancestor != null) {
-        if (ancestor is ClassOrMixinDeclaration) {
-          return [parent.methodName.name, ancestor.name.name];
-        } else if (ancestor is ExtensionDeclaration) {
-          return [parent.methodName.name, ancestor.name.name];
-        }
-        ancestor = ancestor.parent;
-      }
-      return [parent.methodName.name];
-    } else if (parent is RedirectingConstructorInvocation) {
-      var ancestor = parent.parent;
-      if (ancestor is ConstructorDeclaration) {
-        return [parent.constructorName?.name ?? '', ancestor.returnType.name];
-      }
-    } else if (parent is SuperConstructorInvocation) {
-      var ancestor = parent.parent;
-      if (ancestor is ConstructorDeclaration) {
-        return [parent.constructorName?.name ?? '', ancestor.returnType.name];
-      }
-    }
-    return null;
-  }
-
-  /// Return the components of the path of the element associated with the
-  /// diagnostic. The components are ordered from the most local to the most
-  /// global. For example, for a constructor this would be the name of the
-  /// constructor followed by the name of the class in which the constructor is
-  /// declared (with an empty string for the unnamed constructor).
-  List<String> _computeComponents() {
-    var node = this.node;
-    if (node is SimpleIdentifier) {
-      var parent = node.parent;
-      if (parent is Label && parent.parent is NamedExpression) {
-        // The parent of the named expression is an argument list. Because we
-        // don't represent parameters as elements, the element we need to match
-        // against is the invocation containing those arguments.
-        return _componentsFromParent(parent.parent.parent);
-      } else if (parent is TypeName && parent.parent is ConstructorName) {
-        return ['', node.name];
-      } else if (parent is MethodInvocation) {
-        return _componentsFromParent(node);
-      }
-      return [node.name];
-    } else if (node is PrefixedIdentifier) {
-      var parent = node.parent;
-      if (parent is TypeName && parent.parent is ConstructorName) {
-        return ['', node.identifier.name];
-      }
-      return [node.identifier.name];
-    } else if (node is ConstructorName) {
-      return [node.name.name];
-    } else if (node is NamedType) {
-      return [node.name.name];
-    } else if (node is TypeArgumentList) {
-      return _componentsFromParent(node);
-    } else if (node is ArgumentList) {
-      return _componentsFromParent(node);
-    } else if (node?.parent is ArgumentList) {
-      return _componentsFromParent(node.parent);
-    }
-    return null;
-  }
-
-  List<ElementKind> _kindsForNode(AstNode node, {AstNode child}) {
-    if (node is ConstructorName) {
-      return const [ElementKind.constructorKind];
-    } else if (node is ExtensionOverride) {
-      return const [ElementKind.extensionKind];
-    } else if (node is InstanceCreationExpression) {
-      return const [ElementKind.constructorKind];
-    } else if (node is Label) {
-      var argumentList = node.parent.parent;
-      return _kindsForNode(argumentList.parent, child: argumentList);
-    } else if (node is MethodInvocation) {
-      assert(child != null);
-      if (node.target == child) {
-        return const [
-          ElementKind.classKind,
-          ElementKind.enumKind,
-          ElementKind.mixinKind
-        ];
-      } else if (node.realTarget != null) {
-        return const [ElementKind.constructorKind, ElementKind.methodKind];
-      }
-      return const [
-        ElementKind.classKind,
-        ElementKind.extensionKind,
-        ElementKind.functionKind,
-        ElementKind.methodKind
-      ];
-    } else if (node is NamedType) {
-      var parent = node.parent;
-      if (parent is ConstructorName && parent.name == null) {
-        return const [ElementKind.classKind, ElementKind.constructorKind];
-      }
-      return const [
-        ElementKind.classKind,
-        ElementKind.enumKind,
-        ElementKind.mixinKind,
-        ElementKind.typedefKind
-      ];
-    } else if (node is PrefixedIdentifier) {
-      if (node.prefix == child) {
-        return const [
-          ElementKind.classKind,
-          ElementKind.enumKind,
-          ElementKind.extensionKind,
-          ElementKind.mixinKind,
-          ElementKind.typedefKind
-        ];
-      }
-      return const [
-        ElementKind.fieldKind,
-        ElementKind.getterKind,
-        ElementKind.setterKind
-      ];
-    } else if (node is PropertyAccess) {
-      return const [ElementKind.getterKind, ElementKind.setterKind];
-    } else if (node is SimpleIdentifier) {
-      return _kindsForNode(node.parent, child: node);
-    }
-    return null;
-  }
-
-  /// Return the name of the class associated with the given [target].
-  String _nameOfTarget(Expression target) {
-    if (target is SimpleIdentifier) {
-      var type = target.staticType;
-      if (type != null) {
-        if (type is InterfaceType) {
-          return type.element.name;
-        } else if (type.isDynamic) {
-          // The name is likely to be undefined.
-          return target.name;
-        }
-        return null;
-      }
-      return target.name;
-    } else if (target != null) {
-      var type = target.staticType;
-      if (type is InterfaceType) {
-        return type.element.name;
-      }
-      return null;
-    }
-    return null;
-  }
-
   /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
   static DataDriven newInstance() => DataDriven();
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
index a938d2c..d07b075 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
@@ -4,6 +4,10 @@
 
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_kind.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart'
+    show ClassElement, ExtensionElement;
+import 'package:analyzer/dart/element/type.dart';
 import 'package:meta/meta.dart';
 
 /// An object that can be used to determine whether an element is appropriate
@@ -89,4 +93,246 @@
     }
     return false;
   }
+
+  /// Return an element matcher that will match the element that is, or should
+  /// be, associated with the given [node], or `null` if there is no appropriate
+  /// matcher for the node.
+  static ElementMatcher forNode(AstNode node) {
+    if (node == null) {
+      return null;
+    }
+    var importedUris = _importElementsForNode(node);
+    if (importedUris == null) {
+      return null;
+    }
+    var components = _componentsForNode(node);
+    if (components == null) {
+      return null;
+    }
+    return ElementMatcher(
+        importedUris: importedUris,
+        components: components,
+        kinds: _kindsForNode(node));
+  }
+
+  /// Return the components of the path of the element associated with the given
+  /// [node]. The components are ordered from the most local to the most global.
+  /// For example, for a constructor this would be the name of the constructor
+  /// followed by the name of the class in which the constructor is declared
+  /// (with an empty string for the unnamed constructor).
+  static List<String> _componentsForNode(AstNode node) {
+    if (node is SimpleIdentifier) {
+      var parent = node.parent;
+      if (parent is Label && parent.parent is NamedExpression) {
+        // The parent of the named expression is an argument list. Because we
+        // don't represent parameters as elements, the element we need to match
+        // against is the invocation containing those arguments.
+        return _componentsFromParent(parent.parent.parent);
+      } else if (parent is TypeName && parent.parent is ConstructorName) {
+        return ['', node.name];
+      } else if (parent is MethodDeclaration && node == parent.name) {
+        return [node.name];
+      } else if ((parent is MethodInvocation && node == parent.methodName) ||
+          (parent is PrefixedIdentifier && node == parent.identifier) ||
+          (parent is PropertyAccess && node == parent.propertyName)) {
+        return _componentsFromParent(node);
+      }
+      return _componentsFromIdentifier(node);
+    } else if (node is PrefixedIdentifier) {
+      var parent = node.parent;
+      if (parent is TypeName && parent.parent is ConstructorName) {
+        return ['', node.identifier.name];
+      }
+      return [node.identifier.name];
+    } else if (node is ConstructorName) {
+      return [node.name.name];
+    } else if (node is NamedType) {
+      return [node.name.name];
+    } else if (node is TypeArgumentList) {
+      return _componentsFromParent(node);
+    } else if (node is ArgumentList) {
+      return _componentsFromParent(node);
+    } else if (node?.parent is ArgumentList) {
+      return _componentsFromParent(node.parent);
+    }
+    return null;
+  }
+
+  /// Return the components associated with the [identifier] when there is no
+  /// contextual information.
+  static List<String> _componentsFromIdentifier(SimpleIdentifier identifier) {
+    var element = identifier.staticElement;
+    if (element == null) {
+      var parent = identifier.parent;
+      if (parent is AssignmentExpression && identifier == parent.leftHandSide) {
+        element = parent.writeElement;
+      }
+    }
+    if (element != null) {
+      var enclosingElement = element.enclosingElement;
+      if (enclosingElement is ClassElement ||
+          enclosingElement is ExtensionElement) {
+        return [identifier.name, enclosingElement.name];
+      }
+    }
+    return [identifier.name];
+  }
+
+  /// Return the components for the element associated with the given [node] by
+  /// looking at the parent of the [node].
+  static List<String> _componentsFromParent(AstNode node) {
+    var parent = node.parent;
+    if (parent is ArgumentList) {
+      parent = parent.parent;
+    }
+    if (parent is Annotation) {
+      return [parent.constructorName?.name ?? '', parent.name.name];
+    } else if (parent is ExtensionOverride) {
+      return [parent.extensionName.name];
+    } else if (parent is InstanceCreationExpression) {
+      var constructorName = parent.constructorName;
+      return [constructorName.name?.name ?? '', constructorName.type.name.name];
+    } else if (parent is MethodInvocation) {
+      var methodName = parent.methodName;
+      var targetName = _nameOfTarget(parent.realTarget);
+      if (targetName != null) {
+        return [methodName.name, targetName];
+      }
+      return _componentsFromIdentifier(methodName);
+    } else if (parent is PrefixedIdentifier) {
+      var identifier = parent.identifier;
+      var targetName = _nameOfTarget(parent.prefix);
+      if (targetName != null) {
+        return [identifier.name, targetName];
+      }
+      return _componentsFromIdentifier(identifier);
+    } else if (parent is PropertyAccess) {
+      var propertyName = parent.propertyName;
+      var targetName = _nameOfTarget(parent.realTarget);
+      if (targetName != null) {
+        return [propertyName.name, targetName];
+      }
+      return _componentsFromIdentifier(propertyName);
+    } else if (parent is RedirectingConstructorInvocation) {
+      var ancestor = parent.parent;
+      if (ancestor is ConstructorDeclaration) {
+        return [parent.constructorName?.name ?? '', ancestor.returnType.name];
+      }
+    } else if (parent is SuperConstructorInvocation) {
+      var ancestor = parent.parent;
+      if (ancestor is ConstructorDeclaration) {
+        return [parent.constructorName?.name ?? '', ancestor.returnType.name];
+      }
+    }
+    return null;
+  }
+
+  /// Return the URIs of the imports in the library containing the [node], or
+  /// `null` if the imports can't be determined.
+  static List<Uri> _importElementsForNode(AstNode node) {
+    var root = node.root;
+    if (root is! CompilationUnit) {
+      return null;
+    }
+    var importedUris = <Uri>[];
+    var library = (root as CompilationUnit).declaredElement.library;
+    for (var importElement in library.imports) {
+      // TODO(brianwilkerson) Filter based on combinators to help avoid making
+      //  invalid suggestions.
+      var uri = importElement.importedLibrary?.source?.uri;
+      if (uri != null) {
+        // The [uri] is `null` if the literal string is not a valid URI.
+        importedUris.add(uri);
+      }
+    }
+    return importedUris;
+  }
+
+  /// Return the kinds of elements that could reasonably be referenced at the
+  /// location of the [node]. If [child] is no `null` then the [node] is a
+  /// parent of the original node.
+  static List<ElementKind> _kindsForNode(AstNode node, {AstNode child}) {
+    if (node is ConstructorName) {
+      return const [ElementKind.constructorKind];
+    } else if (node is ExtensionOverride) {
+      return const [ElementKind.extensionKind];
+    } else if (node is InstanceCreationExpression) {
+      return const [ElementKind.constructorKind];
+    } else if (node is Label) {
+      var argumentList = node.parent.parent;
+      return _kindsForNode(argumentList.parent, child: argumentList);
+    } else if (node is MethodInvocation) {
+      assert(child != null);
+      if (node.target == child) {
+        return const [
+          ElementKind.classKind,
+          ElementKind.enumKind,
+          ElementKind.mixinKind
+        ];
+      } else if (node.realTarget != null) {
+        return const [ElementKind.constructorKind, ElementKind.methodKind];
+      }
+      return const [
+        ElementKind.classKind,
+        ElementKind.extensionKind,
+        ElementKind.functionKind,
+        ElementKind.methodKind
+      ];
+    } else if (node is NamedType) {
+      var parent = node.parent;
+      if (parent is ConstructorName && parent.name == null) {
+        return const [ElementKind.classKind, ElementKind.constructorKind];
+      }
+      return const [
+        ElementKind.classKind,
+        ElementKind.enumKind,
+        ElementKind.mixinKind,
+        ElementKind.typedefKind
+      ];
+    } else if (node is PrefixedIdentifier) {
+      if (node.prefix == child) {
+        return const [
+          ElementKind.classKind,
+          ElementKind.enumKind,
+          ElementKind.extensionKind,
+          ElementKind.mixinKind,
+          ElementKind.typedefKind
+        ];
+      }
+      return const [
+        ElementKind.fieldKind,
+        ElementKind.getterKind,
+        ElementKind.setterKind
+      ];
+    } else if (node is PropertyAccess) {
+      return const [ElementKind.getterKind, ElementKind.setterKind];
+    } else if (node is SimpleIdentifier) {
+      return _kindsForNode(node.parent, child: node);
+    }
+    return null;
+  }
+
+  /// Return the name of the class associated with the given [target].
+  static String _nameOfTarget(Expression target) {
+    if (target is SimpleIdentifier) {
+      var type = target.staticType;
+      if (type != null) {
+        if (type is InterfaceType) {
+          return type.element.name;
+        } else if (type.isDynamic) {
+          // The name is likely to be undefined.
+          return target.name;
+        }
+        return null;
+      }
+      return target.name;
+    } else if (target != null) {
+      var type = target.staticType;
+      if (type is InterfaceType) {
+        return type.element.name;
+      }
+      return null;
+    }
+    return null;
+  }
 }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart
new file mode 100644
index 0000000..89ec9da
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart
@@ -0,0 +1,404 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/fix/data_driven/element_kind.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
+import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'data_driven_test_support.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ElementMatcherComponentAndKindTest);
+    defineReflectiveTests(ElementMatcherImportsTest);
+  });
+}
+
+abstract class AbstractElementMatcherTest extends DataDrivenFixProcessorTest {
+  void _assertMatcher(String search,
+      {List<String> expectedComponents,
+      List<ElementKind> expectedKinds,
+      List<String> expectedUris}) {
+    var node = findNodeAtString(search);
+    var matcher = ElementMatcher.forNode(node);
+    if (expectedUris != null) {
+      expect(matcher.importedUris,
+          unorderedEquals(expectedUris.map((uri) => Uri.parse(uri))));
+    }
+    if (expectedComponents != null) {
+      expect(matcher.components, expectedComponents);
+    }
+    if (expectedKinds != null) {
+      expect(matcher.validKinds, expectedKinds);
+    }
+  }
+}
+
+@reflectiveTest
+class ElementMatcherComponentAndKindTest extends AbstractElementMatcherTest {
+  /// The kinds that are expected where a getter or setter is allowed.
+  static List<ElementKind> accessorKinds = [
+    ElementKind.fieldKind,
+    ElementKind.getterKind,
+    ElementKind.setterKind,
+  ];
+
+  /// The kinds that are expected where an invocation is allowed.
+  static List<ElementKind> invocationKinds = [
+    ElementKind.classKind,
+    ElementKind.extensionKind,
+    ElementKind.functionKind,
+    ElementKind.methodKind,
+  ];
+
+  /// The kinds that are expected where a method or constructor is allowed.
+  static List<ElementKind> methodKinds = [
+    ElementKind.constructorKind,
+    ElementKind.methodKind
+  ];
+
+  /// The kinds that are expected where a type is allowed.
+  static List<ElementKind> typeKinds = [
+    ElementKind.classKind,
+    ElementKind.enumKind,
+    ElementKind.mixinKind,
+    ElementKind.typedefKind,
+  ];
+
+  @failingTest
+  Future<void> test_binaryExpression_resolved() async {
+    // This test fails because we don't yet support operators.
+    await resolveTestCode('''
+void f(int x, int y) {
+  x + y;
+}
+''');
+    _assertMatcher('+',
+        expectedComponents: ['+', 'int'],
+        expectedKinds: [ElementKind.methodKind]);
+  }
+
+  @failingTest
+  Future<void> test_binaryExpression_unresolved() async {
+    // This test fails because we don't yet support operators.
+    await resolveTestCode('''
+void f(C c1, C c2) {
+  c1 + c2;
+}
+class C {}
+''');
+    _assertMatcher('+',
+        expectedComponents: ['+', 'C'],
+        expectedKinds: [ElementKind.methodKind]);
+  }
+
+  Future<void> test_getter_withoutTarget_resolved() async {
+    await resolveTestCode('''
+class C {
+  String get g => '';
+  void m() {
+    g;
+  }
+}
+''');
+    _assertMatcher('g;', expectedComponents: ['g', 'C']);
+  }
+
+  Future<void> test_getter_withoutTarget_unresolved() async {
+    await resolveTestCode('''
+class C {
+  void m() {
+    foo;
+  }
+}
+''');
+    _assertMatcher('foo', expectedComponents: ['foo']);
+  }
+
+  Future<void> test_getter_withTarget_resolved() async {
+    await resolveTestCode('''
+void f(String s) {
+  s.length;
+}
+''');
+    _assertMatcher('length',
+        expectedComponents: ['length', 'String'], expectedKinds: accessorKinds);
+  }
+
+  Future<void> test_getter_withTarget_unresolved() async {
+    await resolveTestCode('''
+void f(String s) {
+  s.foo;
+}
+''');
+    _assertMatcher('foo',
+        expectedComponents: ['foo', 'String'], expectedKinds: accessorKinds);
+  }
+
+  Future<void> test_identifier_propertyAccess() async {
+    await resolveTestCode('''
+void f() {
+  s.length;
+}
+''');
+    // TODO(brianwilkerson) Several of these kinds don't seem to be appropriate,
+    //  so we might want to narrow down the list.
+    _assertMatcher('s', expectedComponents: [
+      's'
+    ], expectedKinds: [
+      ElementKind.classKind,
+      ElementKind.enumKind,
+      ElementKind.extensionKind,
+      ElementKind.mixinKind,
+      ElementKind.typedefKind,
+    ]);
+  }
+
+  Future<void> test_method_withoutTarget_resolved() async {
+    await resolveTestCode('''
+class C {
+  void m(int i) {}
+  void m2() {
+    m(0);
+  }
+}
+''');
+    _assertMatcher('m(0)',
+        expectedComponents: ['m', 'C'], expectedKinds: invocationKinds);
+  }
+
+  Future<void> test_method_withoutTarget_unresolved() async {
+    await resolveTestCode('''
+class C {
+  void m() {
+    foo();
+  }
+}
+''');
+    _assertMatcher('foo',
+        expectedComponents: ['foo'], expectedKinds: invocationKinds);
+  }
+
+  Future<void> test_method_withTarget_resolved() async {
+    await resolveTestCode('''
+void f(String s) {
+  s.substring(2);
+}
+''');
+    _assertMatcher('substring',
+        expectedComponents: ['substring', 'String'],
+        expectedKinds: methodKinds);
+  }
+
+  Future<void> test_method_withTarget_unresolved() async {
+    await resolveTestCode('''
+void f(String s) {
+  s.foo(2);
+}
+''');
+    _assertMatcher('foo',
+        expectedComponents: ['foo', 'String'], expectedKinds: methodKinds);
+  }
+
+  Future<void> test_setter_withoutTarget_resolved() async {
+    await resolveTestCode('''
+class C {
+  set s(String s) {}
+  void m() {
+    s = '';
+  }
+}
+''');
+    _assertMatcher('s =', expectedComponents: ['s', 'C']);
+  }
+
+  Future<void> test_setter_withoutTarget_unresolved() async {
+    await resolveTestCode('''
+class C {
+  void m() {
+    foo = '';
+  }
+}
+''');
+    _assertMatcher('foo', expectedComponents: ['foo']);
+  }
+
+  Future<void> test_setter_withTarget_resolved() async {
+    await resolveTestCode('''
+void f(C c) {
+  c.s = '';
+}
+class C {
+  set s(String s) {}
+}
+''');
+    _assertMatcher('s =',
+        expectedComponents: ['s', 'C'], expectedKinds: accessorKinds);
+  }
+
+  Future<void> test_setter_withTarget_unresolved() async {
+    await resolveTestCode('''
+void f(String s) {
+  s.foo = '';
+}
+''');
+    _assertMatcher('foo',
+        expectedComponents: ['foo', 'String'], expectedKinds: accessorKinds);
+  }
+
+  Future<void> test_type_field_resolved() async {
+    await resolveTestCode('''
+class C {
+  String s = '';
+}
+''');
+    _assertMatcher('String',
+        expectedComponents: ['String'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_field_unresolved() async {
+    await resolveTestCode('''
+class C {
+  Foo s = '';
+}
+''');
+    _assertMatcher('Foo',
+        expectedComponents: ['Foo'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_localVariable_resolved() async {
+    await resolveTestCode('''
+void f() {
+  String s = '';
+}
+''');
+    _assertMatcher('String',
+        expectedComponents: ['String'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_localVariable_unresolved() async {
+    await resolveTestCode('''
+void f() {
+  Foo s = '';
+}
+''');
+    _assertMatcher('Foo',
+        expectedComponents: ['Foo'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_method_resolved() async {
+    await resolveTestCode('''
+class C {
+  String m() => '';
+}
+''');
+    _assertMatcher('String',
+        expectedComponents: ['String'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_method_unresolved() async {
+    await resolveTestCode('''
+class C {
+  Foo m() => '';
+}
+''');
+    _assertMatcher('Foo',
+        expectedComponents: ['Foo'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_parameter_resolved() async {
+    await resolveTestCode('''
+void f(String s) {}
+''');
+    _assertMatcher('String',
+        expectedComponents: ['String'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_parameter_unresolved() async {
+    await resolveTestCode('''
+void f(Foo s) {}
+''');
+    _assertMatcher('Foo',
+        expectedComponents: ['Foo'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_topLevelFunction_resolved() async {
+    await resolveTestCode('''
+String f() => '';
+''');
+    _assertMatcher('String',
+        expectedComponents: ['String'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_topLevelFunction_unresolved() async {
+    await resolveTestCode('''
+Foo f() => '';
+''');
+    _assertMatcher('Foo',
+        expectedComponents: ['Foo'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_topLevelVariable_resolved() async {
+    await resolveTestCode('''
+String s = '';
+''');
+    _assertMatcher('String',
+        expectedComponents: ['String'], expectedKinds: typeKinds);
+  }
+
+  Future<void> test_type_topLevelVariable_unresolved() async {
+    await resolveTestCode('''
+Foo s = '';
+''');
+    _assertMatcher('Foo',
+        expectedComponents: ['Foo'], expectedKinds: typeKinds);
+  }
+}
+
+@reflectiveTest
+class ElementMatcherImportsTest extends AbstractElementMatcherTest {
+  Future<void> test_imports_noImports() async {
+    await resolveTestCode('''
+String s = '';
+''');
+    _assertMatcher('s', expectedUris: ['dart:core']);
+  }
+
+  Future<void> test_imports_package() async {
+    var packageRootPath = '$workspaceRootPath/other';
+    newFile('$packageRootPath/lib/other.dart', content: '');
+    writeTestPackageConfig(
+        config: PackageConfigFileBuilder()
+          ..add(name: 'other', rootPath: packageRootPath));
+
+    await resolveTestCode('''
+import 'package:other/other.dart';
+
+String s = '';
+''');
+    _assertMatcher('s',
+        expectedUris: ['dart:core', 'package:other/other.dart']);
+  }
+
+  Future<void> test_imports_relative() async {
+    addSource('$testPackageLibPath/a.dart', '');
+    await resolveTestCode('''
+import 'a.dart';
+
+String s = '';
+''');
+    _assertMatcher('s', expectedUris: ['dart:core', 'package:test/a.dart']);
+  }
+
+  Future<void> test_imports_sdkLibraries() async {
+    await resolveTestCode('''
+import 'dart:math';
+
+int f(int x, int y) => max(x, y);
+''');
+    _assertMatcher('f', expectedUris: ['dart:core', 'dart:math']);
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
index 4eb8242..22ee1a1 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
@@ -8,6 +8,7 @@
 import 'code_fragment_parser_test.dart' as code_fragment_parser;
 import 'code_template_test.dart' as code_template;
 import 'diagnostics/test_all.dart' as diagnostics;
+import 'element_matcher_test.dart' as element_matcher;
 import 'end_to_end_test.dart' as end_to_end;
 import 'flutter_use_case_test.dart' as flutter_use_case;
 import 'modify_parameters_test.dart' as modify_parameters;
@@ -22,6 +23,7 @@
     code_fragment_parser.main();
     code_template.main();
     diagnostics.main();
+    element_matcher.main();
     end_to_end.main();
     flutter_use_case.main();
     modify_parameters.main();