Version 2.19.0-330.0.dev
Merge 3e587da7f0f52c4f8b2b2af3c4c32e8056a9de25 into dev
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index d4cc40d..3bef64b 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -481,6 +481,10 @@
///
/// Clients may not extend, implement or mix-in this class.
abstract class Element implements AnalysisTarget {
+ /// A list of this element's children.
+ /// There is no guarantee of the order in which the children will be included.
+ List<Element> get children;
+
/// Return the analysis context in which this element is defined.
AnalysisContext get context;
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 6f57ad4..b241e8c 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -101,6 +101,16 @@
}
@override
+ List<Element> get children => [
+ ...super.children,
+ ...accessors,
+ ...fields,
+ ...constructors,
+ ...methods,
+ ...typeParameters,
+ ];
+
+ @override
String get displayName => name;
@override
@@ -384,16 +394,6 @@
}));
}
- @override
- void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- safelyVisitChildren(accessors, visitor);
- safelyVisitChildren(fields, visitor);
- safelyVisitChildren(constructors, visitor);
- safelyVisitChildren(methods, visitor);
- safelyVisitChildren(typeParameters, visitor);
- }
-
/// Return an iterable containing all of the implementations of a getter with
/// the given [getterName] that are defined in this class any any superclass
/// of this class (but not in interfaces).
@@ -1127,6 +1127,19 @@
}
@override
+ List<Element> get children => [
+ ...super.children,
+ ...accessors,
+ ...classes,
+ ...enums2,
+ ...extensions,
+ ...functions,
+ ...mixins2,
+ ...typeAliases,
+ ...topLevelVariables,
+ ];
+
+ @override
List<ClassElement> get classes {
return _classes;
}
@@ -1299,19 +1312,6 @@
this.linkedData = linkedData;
}
-
- @override
- void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- safelyVisitChildren(accessors, visitor);
- safelyVisitChildren(classes, visitor);
- safelyVisitChildren(enums2, visitor);
- safelyVisitChildren(extensions, visitor);
- safelyVisitChildren(functions, visitor);
- safelyVisitChildren(mixins2, visitor);
- safelyVisitChildren(typeAliases, visitor);
- safelyVisitChildren(topLevelVariables, visitor);
- }
}
/// A [FieldElement] for a 'const' or 'final' field that has an initializer.
@@ -2204,6 +2204,9 @@
reference?.element = this;
}
+ @override
+ List<Element> get children => const [];
+
/// The length of the element's code, or `null` if the element is synthetic.
int? get codeLength => _codeLength;
@@ -2675,13 +2678,6 @@
_metadataFlags = 0;
}
- /// Use the given [visitor] to visit all of the [children] in the given array.
- void safelyVisitChildren(List<Element> children, ElementVisitor visitor) {
- for (Element child in children) {
- child.accept(visitor);
- }
- }
-
/// Set the code range for this element.
void setCodeRange(int offset, int length) {
_codeOffset = offset;
@@ -2723,9 +2719,13 @@
return getDisplayString(withNullability: true);
}
+ /// Use the given [visitor] to visit all of the children of this element.
+ /// There is no guarantee of the order in which the children will be visited.
@override
void visitChildren(ElementVisitor visitor) {
- // There are no children to visit
+ for (Element child in children) {
+ child.accept(visitor);
+ }
}
/// Return flags that denote presence of a few specific annotations.
@@ -3026,6 +3026,13 @@
ExecutableElementImpl(String super.name, super.offset, {super.reference});
@override
+ List<Element> get children => [
+ ...super.children,
+ ...typeParameters,
+ ...parameters,
+ ];
+
+ @override
Element get enclosingElement => super.enclosingElement!;
@Deprecated('Use enclosingElement instead')
@@ -3198,13 +3205,6 @@
this.linkedData = linkedData;
}
-
- @override
- void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- safelyVisitChildren(typeParameters, visitor);
- safelyVisitChildren(parameters, visitor);
- }
}
/// A concrete implementation of an [ExtensionElement].
@@ -3244,6 +3244,15 @@
}
@override
+ List<Element> get children => [
+ ...super.children,
+ ...accessors,
+ ...fields,
+ ...methods,
+ ...typeParameters,
+ ];
+
+ @override
String get displayName => name ?? '';
@override
@@ -3386,15 +3395,6 @@
this.linkedData = linkedData;
}
-
- @override
- void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- safelyVisitChildren(accessors, visitor);
- safelyVisitChildren(fields, visitor);
- safelyVisitChildren(methods, visitor);
- safelyVisitChildren(typeParameters, visitor);
- }
}
/// A concrete implementation of a [FieldElement].
@@ -3582,6 +3582,13 @@
: super("", nameOffset);
@override
+ List<Element> get children => [
+ ...super.children,
+ ...typeParameters,
+ ...parameters,
+ ];
+
+ @override
String get identifier => '-';
@override
@@ -3657,13 +3664,6 @@
void appendTo(ElementDisplayStringBuilder builder) {
builder.writeGenericFunctionTypeElement(this);
}
-
- @override
- void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- safelyVisitChildren(typeParameters, visitor);
- safelyVisitChildren(parameters, visitor);
- }
}
/// This mixins is added to elements that can have cache completion data.
@@ -3896,6 +3896,13 @@
}
@override
+ List<Element> get children => [
+ ...super.children,
+ ...parts2,
+ ..._partUnits,
+ ];
+
+ @override
CompilationUnitElementImpl get enclosingUnit {
return _definingCompilationUnit;
}
@@ -4233,15 +4240,6 @@
return NullabilityEliminator.perform(typeProvider, type);
}
- @override
- void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- safelyVisitChildren(parts2, visitor);
- for (final partUnit in _partUnits) {
- partUnit.accept(visitor);
- }
- }
-
List<LibraryAugmentationElementImpl> _computeAugmentations() {
final result = <LibraryAugmentationElementImpl>[];
@@ -4445,6 +4443,14 @@
}
@override
+ List<Element> get children => [
+ ...super.children,
+ definingCompilationUnit,
+ ...libraryExports,
+ ...libraryImports,
+ ];
+
+ @override
CompilationUnitElementImpl get definingCompilationUnit =>
_definingCompilationUnit;
@@ -4517,14 +4523,6 @@
return _definingCompilationUnit.source;
}
- @override
- void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- _definingCompilationUnit.accept(visitor);
- safelyVisitChildren(libraryExports, visitor);
- safelyVisitChildren(libraryImports, visitor);
- }
-
void _readLinkedData();
static List<PrefixElement> buildPrefixesFromImports(
@@ -4891,6 +4889,9 @@
this.context, this.session, this.name, this.conflictingElements);
@override
+ List<Element> get children => const [];
+
+ @override
Element? get declaration => null;
@override
@@ -5077,9 +5078,13 @@
return buffer.toString();
}
+ /// Use the given [visitor] to visit all of the children of this element.
+ /// There is no guarantee of the order in which the children will be visited.
@override
void visitChildren(ElementVisitor visitor) {
- // There are no children to visit
+ for (Element child in children) {
+ child.accept(visitor);
+ }
}
}
@@ -5187,6 +5192,9 @@
}
@override
+ List<Element> get children => parameters;
+
+ @override
ParameterElement get declaration => this;
@override
@@ -5261,12 +5269,6 @@
void appendTo(ElementDisplayStringBuilder builder) {
builder.writeFormalParameter(this);
}
-
- @override
- void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- safelyVisitChildren(parameters, visitor);
- }
}
/// The parameter of an implicit setter.
diff --git a/pkg/analyzer/lib/src/dart/element/member.dart b/pkg/analyzer/lib/src/dart/element/member.dart
index 2ba5a6d..957dc0c 100644
--- a/pkg/analyzer/lib/src/dart/element/member.dart
+++ b/pkg/analyzer/lib/src/dart/element/member.dart
@@ -156,6 +156,9 @@
);
@override
+ List<Element> get children => parameters;
+
+ @override
ExecutableElement get declaration => super.declaration as ExecutableElement;
@override
@@ -226,12 +229,6 @@
builder.writeExecutableElement(this, displayName);
}
- @override
- void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- safelyVisitChildren(parameters, visitor);
- }
-
static ExecutableElement from2(
ExecutableElement element,
MapSubstitution substitution,
@@ -494,6 +491,9 @@
}
@override
+ List<Element> get children => const [];
+
+ @override
AnalysisContext get context => _declaration.context;
@override
@@ -648,14 +648,6 @@
bool isAccessibleIn2(LibraryElement library) =>
_declaration.isAccessibleIn2(library);
- /// Use the given [visitor] to visit all of the [children].
- void safelyVisitChildren(List<Element> children, ElementVisitor visitor) {
- // TODO(brianwilkerson) Make this private
- for (Element child in children) {
- child.accept(visitor);
- }
- }
-
@override
E? thisOrAncestorMatching<E extends Element>(
bool Function(Element) predicate,
@@ -672,9 +664,13 @@
return getDisplayString(withNullability: false);
}
+ /// Use the given [visitor] to visit all of the children of this element.
+ /// There is no guarantee of the order in which the children will be visited.
@override
void visitChildren(ElementVisitor visitor) {
- // There are no children to visit
+ for (Element child in children) {
+ child.accept(visitor);
+ }
}
/// If this member is a legacy view, erase nullability from the [type].
@@ -877,6 +873,9 @@
);
@override
+ List<Element> get children => parameters;
+
+ @override
ParameterElement get declaration => super.declaration as ParameterElement;
@override
@@ -932,12 +931,6 @@
builder.writeFormalParameter(this);
}
- @override
- void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- safelyVisitChildren(parameters, visitor);
- }
-
static ParameterElement from(
ParameterElement element, MapSubstitution substitution) {
TypeProviderImpl? typeProvider;
diff --git a/pkg/analyzer/lib/src/utilities/extensions/analysis_session.dart b/pkg/analyzer/lib/src/utilities/extensions/analysis_session.dart
new file mode 100644
index 0000000..c26412b
--- /dev/null
+++ b/pkg/analyzer/lib/src/utilities/extensions/analysis_session.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2022, 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:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/utilities/extensions/library_element.dart';
+
+extension AnalysisSessionExtension on AnalysisSession {
+ /// Locates the [Element] that [location] represents.
+ ///
+ /// Local elements such as variables inside functions cannot be found using
+ /// this method.
+ ///
+ /// Returns `null` if the element cannot be found.
+ Future<Element?> locateElement(ElementLocation location) async {
+ final components = location.components;
+ if (location.components.isEmpty) {
+ return null;
+ }
+
+ // The first component is the library which we'll use to start the search.
+ final libraryUri = components.first;
+ final result = await getLibraryByUri(libraryUri);
+ return result is LibraryElementResult
+ ? result.element.locateElement(location)
+ : null;
+ }
+}
diff --git a/pkg/analyzer/lib/src/utilities/extensions/library_element.dart b/pkg/analyzer/lib/src/utilities/extensions/library_element.dart
new file mode 100644
index 0000000..3f25901
--- /dev/null
+++ b/pkg/analyzer/lib/src/utilities/extensions/library_element.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2022, 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:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+
+extension LibraryElementExtension on LibraryElement {
+ /// Locates an [Element] in this library by its [ElementLocation].
+ ///
+ /// It is assumed that the first component of [location] matches this library.
+ ///
+ /// Local elements such as variables inside functions cannot be found using
+ /// this method.
+ Element? locateElement(ElementLocation location) =>
+ _locateElement(location, 0);
+}
+
+extension _ElementExtension on Element {
+ /// Locates an [Element] by its [ElementLocation] assuming this element
+ /// is a match up to [thisIndex].
+ Element? _locateElement(ElementLocation location, int thisIndex) {
+ final components = location.components;
+ RangeError.checkValidIndex(thisIndex, components);
+
+ final nextIndex = thisIndex + 1;
+ if (nextIndex == components.length) {
+ // This element matches the last component so return it.
+ return this;
+ }
+
+ // Search for a matching child.
+ final identifier = components[nextIndex];
+ for (final child in children) {
+ if ((child as ElementImpl).identifier == identifier) {
+ return child._locateElement(location, nextIndex);
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/pkg/analyzer/test/src/utilities/extensions/analysis_session_test.dart b/pkg/analyzer/test/src/utilities/extensions/analysis_session_test.dart
new file mode 100644
index 0000000..de3cd13
--- /dev/null
+++ b/pkg/analyzer/test/src/utilities/extensions/analysis_session_test.dart
@@ -0,0 +1,107 @@
+// Copyright (c) 2022, 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:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/dart/analysis/results.dart';
+import 'package:analyzer/src/test_utilities/test_code_format.dart';
+import 'package:analyzer/src/utilities/extensions/analysis_session.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(LocateElementTest);
+ });
+}
+
+/// Tests `locateElement()` on [AnalysisSession].
+///
+/// This extension method largely delegates to `LibraryElement.locateElement`
+/// which is tested more comprehensively in
+/// 'test/src/utilities/extensions/library_element_test.dart'.
+@reflectiveTest
+class LocateElementTest extends PubPackageResolutionTest {
+ late _MockAnalysisSession session;
+
+ File get testFile2 => getFile('$testPackageLibPath/test2.dart');
+
+ /// Create a library and (unless [addToSession] is `false`) add it to [session].
+ Future<LibraryElement> createLibrary(
+ String path,
+ String content, {
+ bool addToSession = true,
+ }) async {
+ final code = TestCode.parse(content);
+ newFile(path, code.code);
+ final library = (await resolveFile(path)).libraryElement;
+ if (addToSession) {
+ session.addLibrary(library);
+ }
+ return library;
+ }
+
+ /// Find class [name] in [library].
+ ClassElement findClass(LibraryElement library, String name) {
+ return library.definingCompilationUnit.getClass(name)!;
+ }
+
+ /// Locate the element referenced by [location] in [session].
+ Future<Element?> getElement(ElementLocation? location) =>
+ session.locateElement(location!);
+
+ @override
+ void setUp() {
+ super.setUp();
+ session = _MockAnalysisSession();
+ }
+
+ void test_elementInLibrary() async {
+ final libraryOne = await createLibrary(testFile.path, 'class C {}');
+ final libraryTwo = await createLibrary(testFile2.path, 'class C {}');
+ final classOne = findClass(libraryOne, 'C');
+ final classTwo = findClass(libraryTwo, 'C');
+
+ expect(await getElement(classOne.location!), classOne);
+ expect(await getElement(classTwo.location!), classTwo);
+ }
+
+ void test_invalid() async {
+ final library =
+ await createLibrary(testFile.path, 'class C {}', addToSession: false);
+ final class_ = findClass(library, 'C');
+
+ expect(await getElement(class_.location!), isNull);
+ }
+
+ void test_library() async {
+ final libraryOne = await createLibrary(testFile.path, 'class C {}');
+ final libraryTwo = await createLibrary(testFile2.path, 'class C {}');
+
+ expect(await getElement(libraryOne.location!), libraryOne);
+ expect(await getElement(libraryTwo.location!), libraryTwo);
+ }
+}
+
+class _MockAnalysisSession implements AnalysisSession {
+ final _libraries = <String, LibraryElement>{};
+
+ void addLibrary(LibraryElement library) =>
+ _libraries[library.identifier] = library;
+
+ @override
+ Future<SomeLibraryElementResult> getLibraryByUri(String uri) async {
+ final library = _libraries[uri];
+ return library != null
+ ? LibraryElementResultImpl(library)
+ : CannotResolveUriResult();
+ }
+
+ @override
+ dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+}
diff --git a/pkg/analyzer/test/src/utilities/extensions/library_element_test.dart b/pkg/analyzer/test/src/utilities/extensions/library_element_test.dart
new file mode 100644
index 0000000..147381d
--- /dev/null
+++ b/pkg/analyzer/test/src/utilities/extensions/library_element_test.dart
@@ -0,0 +1,189 @@
+// Copyright (c) 2022, 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:analyzer/src/dart/ast/element_locator.dart';
+import 'package:analyzer/src/test_utilities/test_code_format.dart';
+import 'package:analyzer/src/utilities/extensions/ast.dart';
+import 'package:analyzer/src/utilities/extensions/library_element.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(LocateElementTest);
+ });
+}
+
+@reflectiveTest
+class LocateElementTest extends PubPackageResolutionTest {
+ Future<void> assertLocation(String content) async {
+ final code = TestCode.parse(content);
+ await resolveTestCode(code.code);
+
+ // Get the element we'll be searching for from the marker in code.
+ final node = result.unit.nodeCovering(offset: code.position.offset);
+ final expectedElement = ElementLocator.locate(node)!;
+
+ // Verify locating the element using its location finds the same element.
+ final actualElement =
+ result.libraryElement.locateElement(expectedElement.location!);
+ expect(actualElement, expectedElement);
+ }
+
+ void test_class() async {
+ await assertLocation('''
+class C^ {}
+''');
+ }
+
+ void test_class_const() async {
+ await assertLocation('''
+class C {
+ static const ^c = '';
+}
+''');
+ }
+
+ void test_class_constructor() async {
+ await assertLocation('''
+class C {
+ C.named() {}
+ ^C() {}
+}
+''');
+ }
+
+ void test_class_constructor_named() async {
+ await assertLocation('''
+class C {
+ C.named() {}
+}
+
+class C2 {
+ C.nam^ed() {}
+}
+''');
+ }
+
+ void test_class_field() async {
+ await assertLocation('''
+class C {
+ int f = 0;
+}
+class C2 {
+ int f^ = 0;
+}
+''');
+ }
+
+ void test_class_getter() async {
+ await assertLocation('''
+class C {
+ String get s => '';
+}
+class C2 {
+ String get s^ => '';
+}
+''');
+ }
+
+ void test_class_setter() async {
+ await assertLocation('''
+class C {
+ set s(String a) {}
+}
+class C2 {
+ set ^s(String a) {}
+}
+''');
+ }
+
+ void test_const() async {
+ await assertLocation('''
+const ^c = '';
+''');
+ }
+
+ void test_enum() async {
+ await assertLocation('''
+enum ^E {}
+''');
+ }
+
+ void test_enum_const2() async {
+ await assertLocation('''
+enum E {
+ o^ne(1);
+ final int n;
+ const E(this.n);
+}
+''');
+ }
+
+ void test_enum_constructor() async {
+ await assertLocation('''
+enum E {
+ final int n;
+ const ^E(this.n);
+}
+''');
+ }
+
+ void test_extension() async {
+ await assertLocation('''
+extension on int {}
+exten^sion on String {}
+extension on int {}
+''');
+ }
+
+ void test_extension_named() async {
+ await assertLocation('''
+extension StringEx^tension on String {}
+''');
+ }
+
+ void test_getter() async {
+ await assertLocation('''
+String get ^g => '';
+''');
+ }
+
+ void test_method() async {
+ await assertLocation('''
+class C {
+ void m() {}
+}
+class C2 {
+ void ^m() {}
+}
+''');
+ }
+
+ void test_mixin() async {
+ await assertLocation('''
+mixin ^M {}
+''');
+ }
+
+ void test_setter() async {
+ await assertLocation('''
+Set f^(String v) => '';
+''');
+ }
+
+ void test_topLevelVariable() async {
+ await assertLocation('''
+int ^a = 1;
+''');
+ }
+
+ void test_typedef() async {
+ await assertLocation('''
+typedef ^S = String;
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/utilities/extensions/test_all.dart b/pkg/analyzer/test/src/utilities/extensions/test_all.dart
index b1db4c9..2e04529 100644
--- a/pkg/analyzer/test/src/utilities/extensions/test_all.dart
+++ b/pkg/analyzer/test/src/utilities/extensions/test_all.dart
@@ -4,16 +4,20 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
+import 'analysis_session_test.dart' as analysis_session;
import 'ast_test.dart' as ast;
import 'collection_test.dart' as collection;
+import 'library_element_test.dart' as library_element;
import 'object_test.dart' as object;
import 'stream_test.dart' as stream;
import 'string_test.dart' as string;
main() {
defineReflectiveSuite(() {
+ analysis_session.main();
ast.main();
collection.main();
+ library_element.main();
object.main();
stream.main();
string.main();
diff --git a/tools/VERSION b/tools/VERSION
index e3ffaba..7a33e67 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 19
PATCH 0
-PRERELEASE 329
+PRERELEASE 330
PRERELEASE_PATCH 0