Version 2.17.0-166.0.dev
Merge commit '589baa47920822dea07cc19a54e2a9e088affbf8' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
index 827a088..36aff2e 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
@@ -11,9 +11,15 @@
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analysis_server/src/plugin/result_merger.dart';
import 'package:analysis_server/src/protocol_server.dart' show NavigationTarget;
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol;
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
+import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
import 'package:analyzer_plugin/utilities/navigation/navigation_dart.dart';
import 'package:collection/collection.dart';
@@ -47,14 +53,16 @@
Future<AnalysisNavigationParams> getServerResult(
bool supportsLocationLink, String path, int offset) async {
- final collector =
- NavigationCollectorImpl(collectCodeLocations: supportsLocationLink);
+ final collector = NavigationCollectorImpl();
final result = await server.getResolvedUnit(path);
final unit = result?.unit;
if (unit != null) {
computeDartNavigation(
server.resourceProvider, collector, unit, offset, 0);
+ if (supportsLocationLink) {
+ _updateTargetsWithCodeLocations(collector);
+ }
collector.createRegions();
}
@@ -171,6 +179,50 @@
return otherResults.isNotEmpty ? otherResults : results;
}
+ /// Get the location of the code (excluding leading doc comments) for this element.
+ protocol.Location? _getCodeLocation(Element element) {
+ var codeElement = element;
+ // For synthetic getters created for fields, we need to access the associated
+ // variable to get the codeOffset/codeLength.
+ if (codeElement.isSynthetic && codeElement is PropertyAccessorElementImpl) {
+ final variable = codeElement.variable;
+ if (variable is ElementImpl) {
+ codeElement = variable as ElementImpl;
+ }
+ }
+
+ // Read the main codeOffset from the element. This may include doc comments
+ // but will give the correct end position.
+ int? codeOffset, codeLength;
+ if (codeElement is ElementImpl) {
+ codeOffset = codeElement.codeOffset;
+ codeLength = codeElement.codeLength;
+ }
+
+ if (codeOffset == null || codeLength == null) {
+ return null;
+ }
+
+ // Read the declaration so we can get the offset after the doc comments.
+ // TODO(dantup): Skip this for parts (getParsedLibrary will throw), but find
+ // a better solution.
+ final declaration = _parsedDeclaration(codeElement);
+ var node = declaration?.node;
+ if (node is VariableDeclaration) {
+ node = node.parent;
+ }
+ if (node is AnnotatedNode) {
+ var offsetAfterDocs = node.firstTokenAfterCommentAndMetadata.offset;
+
+ // Reduce the length by the difference between the end of docs and the start.
+ codeLength -= offsetAfterDocs - codeOffset;
+ codeOffset = offsetAfterDocs;
+ }
+
+ return AnalyzerConverter()
+ .locationFromElement(element, offset: codeOffset, length: codeLength);
+ }
+
Location? _toLocation(
AnalysisNavigationParams mergedResults, NavigationTarget target) {
final targetFilePath = mergedResults.files[target.fileIndex];
@@ -191,4 +243,34 @@
region, sourceLineInfo, targetFilePath, target, targetLineInfo)
: null;
}
+
+ void _updateTargetsWithCodeLocations(NavigationCollectorImpl collector) {
+ for (var targetToUpdate in collector.targetsToUpdate) {
+ var codeLocation = _getCodeLocation(targetToUpdate.element);
+ if (codeLocation != null) {
+ targetToUpdate.target
+ ..codeOffset = codeLocation.offset
+ ..codeLength = codeLocation.length;
+ }
+ }
+ }
+
+ static ElementDeclarationResult? _parsedDeclaration(Element element) {
+ var session = element.session;
+ if (session == null) {
+ return null;
+ }
+
+ var libraryPath = element.library?.source.fullName;
+ if (libraryPath == null) {
+ return null;
+ }
+
+ var parsedLibrary = session.getParsedLibrary(libraryPath);
+ if (parsedLibrary is! ParsedLibraryResult) {
+ return null;
+ }
+
+ return parsedLibrary.getElementDeclaration(element);
+ }
}
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index d0f199e..0f8b8ce 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -711,6 +711,8 @@
status: needsEvaluation
CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT:
status: needsEvaluation
+CompileTimeErrorCode.NON_FINAL_FIELD_IN_ENUM:
+ status: needsEvaluation
CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR:
status: needsEvaluation
CompileTimeErrorCode.NON_GENERATIVE_IMPLICIT_CONSTRUCTOR:
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index 8f78367..d5e5c71 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -241,6 +241,9 @@
if (node is CompilationUnit) {
return member;
}
+ if (node is EnumDeclaration) {
+ return member;
+ }
member = node;
}
return null;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
index 163572e..b33e29a 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
@@ -435,8 +435,13 @@
return validateCreateFunction(searchEngine, libraryElement, name);
}
// method of class
+ ClassElement? classElement;
if (parent is ClassDeclaration) {
- var classElement = parent.declaredElement!;
+ classElement = parent.declaredElement!;
+ } else if (parent is EnumDeclaration) {
+ classElement = parent.declaredElement!;
+ }
+ if (classElement != null) {
return validateCreateMethod(searchEngine,
AnalysisSessionHelper(resolveResult.session), classElement, name);
}
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
index 4d55c6c..8065f1f 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
@@ -13,6 +13,7 @@
import 'package:analysis_server/src/services/refactoring/visible_ranges_computer.dart';
import 'package:analysis_server/src/services/search/hierarchy.dart';
import 'package:analysis_server/src/services/search/search_engine.dart';
+import 'package:analysis_server/src/utilities/strings.dart';
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
@@ -149,12 +150,15 @@
// check if there is a member with "newName" in the same ClassElement
for (var newNameMember in getChildren(elementClass, name)) {
result.addError(
- format(
- "Class '{0}' already declares {1} with name '{2}'.",
- elementClass.displayName,
- getElementKindName(newNameMember),
- name),
- newLocation_fromElement(newNameMember));
+ format(
+ "{0} '{1}' already declares {2} with name '{3}'.",
+ capitalize(elementClass.kind.displayName),
+ elementClass.displayName,
+ getElementKindName(newNameMember),
+ name,
+ ),
+ newLocation_fromElement(newNameMember),
+ );
}
}
diff --git a/pkg/analysis_server/test/analysis/get_hover_test.dart b/pkg/analysis_server/test/analysis/get_hover_test.dart
index 6d19ce9..440b1b4 100644
--- a/pkg/analysis_server/test/analysis/get_hover_test.dart
+++ b/pkg/analysis_server/test/analysis/get_hover_test.dart
@@ -67,38 +67,7 @@
await createProject();
}
- Future<void> test_class_declaration() async {
- addTestFile('''
-class A<E> {}
-class I1<K, V> {}
-class I2<E> {}
-class M1 {}
-class M2<E> {}
-class B<T> extends A<T> with M1, M2<int> implements I1<int, String>, I2 {}
-''');
- var hover = await prepareHover('B<T>');
- expect(hover.containingClassDescription, null);
- expect(
- hover.elementDescription,
- 'class B<T> extends A<T> with M1, M2<int> '
- 'implements I1<int, String>, I2<dynamic>');
- expect(hover.staticType, isNull);
- expect(hover.propagatedType, isNull);
- }
-
- Future<void> test_class_declaration_abstract() async {
- addTestFile('''
-class A {}
-abstract class B extends A {}
-''');
- var hover = await prepareHover('B extends');
- expect(hover.containingClassDescription, null);
- expect(hover.elementDescription, 'abstract class B extends A');
- expect(hover.staticType, isNull);
- expect(hover.propagatedType, isNull);
- }
-
- Future<void> test_constructor_named() async {
+ Future<void> test_class_constructor_named() async {
addTestFile('''
library my.library;
class A {
@@ -129,7 +98,7 @@
}
}
- Future<void> test_constructor_noKeyword_const() async {
+ Future<void> test_class_constructor_noKeyword_const() async {
addTestFile('''
library my.library;
class A {
@@ -156,7 +125,7 @@
expect(hover.parameter, isNull);
}
- Future<void> test_constructor_noKeyword_new() async {
+ Future<void> test_class_constructor_noKeyword_new() async {
addTestFile('''
library my.library;
class A {}
@@ -181,7 +150,7 @@
expect(hover.parameter, isNull);
}
- Future<void> test_constructor_synthetic() async {
+ Future<void> test_class_constructor_synthetic() async {
addTestFile('''
library my.library;
class A {
@@ -207,7 +176,7 @@
expect(hover.parameter, isNull);
}
- Future<void> test_constructor_synthetic_withTypeArgument() async {
+ Future<void> test_class_constructor_synthetic_withTypeArgument() async {
addTestFile('''
library my.library;
class A<T> {}
@@ -249,7 +218,7 @@
}
}
- Future<void> test_constructorReference_named() async {
+ Future<void> test_class_constructorReference_named() async {
addTestFile('''
class A<T> {
/// doc aaa
@@ -276,7 +245,7 @@
expect(hover.parameter, isNull);
}
- Future<void> test_constructorReference_unnamed_declared() async {
+ Future<void> test_class_constructorReference_unnamed_declared() async {
addTestFile('''
class A<T> {
/// doc aaa
@@ -303,7 +272,7 @@
expect(hover.parameter, isNull);
}
- Future<void> test_constructorReference_unnamed_declared_new() async {
+ Future<void> test_class_constructorReference_unnamed_declared_new() async {
addTestFile('''
class A<T> {
/// doc aaa
@@ -330,7 +299,7 @@
expect(hover.parameter, isNull);
}
- Future<void> test_constructorReference_unnamed_synthetic() async {
+ Future<void> test_class_constructorReference_unnamed_synthetic() async {
addTestFile('''
class A<T> {}
@@ -353,6 +322,269 @@
expect(hover.parameter, isNull);
}
+ Future<void> test_class_declaration() async {
+ addTestFile('''
+class A<E> {}
+class I1<K, V> {}
+class I2<E> {}
+class M1 {}
+class M2<E> {}
+class B<T> extends A<T> with M1, M2<int> implements I1<int, String>, I2 {}
+''');
+ var hover = await prepareHover('B<T>');
+ expect(hover.containingClassDescription, null);
+ expect(
+ hover.elementDescription,
+ 'class B<T> extends A<T> with M1, M2<int> '
+ 'implements I1<int, String>, I2<dynamic>');
+ expect(hover.staticType, isNull);
+ expect(hover.propagatedType, isNull);
+ }
+
+ Future<void> test_class_declaration_abstract() async {
+ addTestFile('''
+class A {}
+abstract class B extends A {}
+''');
+ var hover = await prepareHover('B extends');
+ expect(hover.containingClassDescription, null);
+ expect(hover.elementDescription, 'abstract class B extends A');
+ expect(hover.staticType, isNull);
+ expect(hover.propagatedType, isNull);
+ }
+
+ Future<void> test_class_getter_synthetic() async {
+ addTestFile('''
+library my.library;
+class A {
+ /// doc aaa
+ /// doc bbb
+ String fff;
+}
+void f(A a) {
+ print(a.fff);
+}
+''');
+ var hover = await prepareHover('fff);');
+ // element
+ expect(hover.containingLibraryName, 'bin/test.dart');
+ expect(hover.containingLibraryPath, testFile);
+ expect(hover.containingClassDescription, 'A');
+ expect(hover.dartdoc, '''doc aaa\ndoc bbb''');
+ expect(hover.elementDescription, 'String fff');
+ expect(hover.elementKind, 'field');
+ // types
+ expect(hover.staticType, 'String');
+ expect(hover.propagatedType, isNull);
+ }
+
+ Future<void> test_class_method_declaration() async {
+ addTestFile('''
+library my.library;
+class A {
+ /// doc aaa
+ /// doc bbb
+ List<String> mmm(int a, String b) {
+ }
+}
+''');
+ var hover = await prepareHover('mmm(int a');
+ // element
+ expect(hover.containingLibraryName, 'bin/test.dart');
+ expect(hover.containingLibraryPath, testFile);
+ expect(hover.containingClassDescription, 'A');
+ expect(hover.dartdoc, '''doc aaa\ndoc bbb''');
+ expect(hover.elementDescription, 'List<String> mmm(int a, String b)');
+ expect(hover.elementKind, 'method');
+ // types
+ expect(hover.staticType, isNull);
+ expect(hover.propagatedType, isNull);
+ // no parameter
+ expect(hover.parameter, isNull);
+ }
+
+ Future<void> test_class_method_reference() async {
+ addTestFile('''
+library my.library;
+class A {
+ List<String> mmm(int a, String b) {
+ }
+}
+void f(A a) {
+ a.mmm(42, 'foo');
+}
+''');
+ var hover = await prepareHover('mm(42, ');
+ // range
+ expect(hover.offset, findOffset('mmm(42, '));
+ expect(hover.length, 'mmm'.length);
+ // element
+ expect(hover.containingLibraryName, 'bin/test.dart');
+ expect(hover.containingLibraryPath, testFile);
+ expect(hover.elementDescription, 'List<String> mmm(int a, String b)');
+ expect(hover.elementKind, 'method');
+ expect(hover.isDeprecated, isFalse);
+ // types
+ expect(hover.staticType, 'List<String> Function(int, String)');
+ expect(hover.propagatedType, isNull);
+ // no parameter
+ expect(hover.parameter, isNull);
+ }
+
+ Future<void> test_class_method_reference_deprecated() async {
+ addTestFile('''
+class A {
+ @deprecated
+ static void test() {}
+}
+void f() {
+ A.test();
+}
+''');
+ var hover = await prepareHover('test();');
+ // element
+ expect(hover.containingLibraryPath, testFile);
+ expect(hover.elementDescription, 'void test()');
+ expect(hover.elementKind, 'method');
+ expect(hover.isDeprecated, isTrue);
+ }
+
+ Future<void> test_class_method_reference_genericMethod() async {
+ addTestFile('''
+library my.library;
+
+abstract class Stream<T> {
+ Stream<S> transform<S>(StreamTransformer<T, S> streamTransformer);
+}
+abstract class StreamTransformer<T, S> {}
+
+f(Stream<int> s) {
+ s.transform(null);
+}
+''');
+ var hover = await prepareHover('nsform(n');
+ // range
+ expect(hover.offset, findOffset('transform(n'));
+ expect(hover.length, 'transform'.length);
+ // element
+ expect(hover.containingLibraryName, 'bin/test.dart');
+ expect(hover.containingLibraryPath, testFile);
+ expect(hover.elementDescription,
+ 'Stream<S> transform<S>(StreamTransformer<int, S> streamTransformer)');
+ expect(hover.elementKind, 'method');
+ expect(hover.isDeprecated, isFalse);
+ // types
+ expect(hover.staticType,
+ 'Stream<dynamic> Function(StreamTransformer<int, dynamic>)');
+ expect(hover.propagatedType, isNull);
+ // no parameter
+ expect(hover.parameter, isNull);
+ }
+
+ Future<void> test_class_setter_hasDocumentation() async {
+ addTestFile('''
+class A {
+ /// getting
+ int get foo => 42;
+ /// setting
+ set foo(int x) {}
+}
+void f(A a) {
+ a.foo = 123;
+}
+''');
+ var hover = await prepareHover('foo = ');
+ expect(hover.containingClassDescription, 'A');
+ expect(hover.dartdoc, '''setting''');
+ expect(hover.elementDescription, 'void set foo(int x)');
+ expect(hover.elementKind, 'setter');
+ }
+
+ Future<void> test_class_setter_noDocumentation() async {
+ addTestFile('''
+class A {
+ /// getting
+ int get foo => 42;
+ set foo(int x) {}
+}
+void f(A a) {
+ a.foo = 123;
+}
+''');
+ var hover = await prepareHover('foo = ');
+ expect(hover.containingClassDescription, 'A');
+ expect(hover.dartdoc, '''getting''');
+ expect(hover.elementDescription, 'void set foo(int x)');
+ expect(hover.elementKind, 'setter');
+ }
+
+ Future<void> test_class_setter_super_hasDocumentation() async {
+ addTestFile('''
+class A {
+ /// pgetting
+ int get foo => 42;
+ /// psetting
+ set foo(int x) {}
+}
+class B extends A {
+ /// getting
+ int get foo => 42;
+ set foo(int x) {}
+}
+void f(B b) {
+ b.foo = 123;
+}
+''');
+ var hover = await prepareHover('foo = ');
+ expect(hover.containingClassDescription, 'B');
+ expect(hover.dartdoc, '''psetting\n\nCopied from `A`.''');
+ expect(hover.elementDescription, 'void set foo(int x)');
+ expect(hover.elementKind, 'setter');
+ }
+
+ Future<void> test_class_setter_super_noDocumentation() async {
+ addTestFile('''
+class A {
+ /// pgetting
+ int get foo => 42;
+ set foo(int x) {}
+}
+class B extends A {
+ int get foo => 42;
+ set foo(int x) {}
+}
+void f(B b) {
+ b.foo = 123;
+}
+''');
+ var hover = await prepareHover('foo = ');
+ expect(hover.containingClassDescription, 'B');
+ expect(hover.dartdoc, '''pgetting\n\nCopied from `A`.''');
+ expect(hover.elementDescription, 'void set foo(int x)');
+ expect(hover.elementKind, 'setter');
+ }
+
+ @failingTest
+ Future<void> test_class_setter_super_noSetter() async {
+ addTestFile('''
+class A {
+ /// pgetting
+ int get foo => 42;
+}
+class B extends A {
+ set foo(int x) {}
+}
+void f(B b) {
+ b.foo = 123;
+}
+''');
+ var hover = await prepareHover('foo = ');
+ expect(hover.containingClassDescription, 'B');
+ expect(hover.dartdoc, '''pgetting''');
+ expect(hover.elementDescription, 'void set foo(int x)');
+ expect(hover.elementKind, 'setter');
+ }
+
Future<void> test_dartdoc_block() async {
addTestFile('''
/**
@@ -453,6 +685,131 @@
expect(hover.propagatedType, isNull);
}
+ Future<void> test_enum_getter() async {
+ addTestFile('''
+library my.library;
+enum E {
+ v;
+ /// doc aaa
+ /// doc bbb
+ int get foo => 0;
+}
+void f(E e) {
+ print(e.foo);
+}
+''');
+ var hover = await prepareHover('foo);');
+ // element
+ expect(hover.containingLibraryName, 'bin/test.dart');
+ expect(hover.containingLibraryPath, testFile);
+ expect(hover.containingClassDescription, 'E');
+ expect(hover.dartdoc, '''doc aaa\ndoc bbb''');
+ expect(hover.elementDescription, 'int get foo');
+ expect(hover.elementKind, 'getter');
+ }
+
+ Future<void> test_enum_getter_synthetic() async {
+ addTestFile('''
+library my.library;
+enum E {
+ v;
+ /// doc aaa
+ /// doc bbb
+ final String fff;
+}
+void f(E e) {
+ print(e.fff);
+}
+''');
+ var hover = await prepareHover('fff);');
+ // element
+ expect(hover.containingLibraryName, 'bin/test.dart');
+ expect(hover.containingLibraryPath, testFile);
+ expect(hover.containingClassDescription, 'E');
+ expect(hover.dartdoc, '''doc aaa\ndoc bbb''');
+ expect(hover.elementDescription, 'String fff');
+ expect(hover.elementKind, 'field');
+ // types
+ expect(hover.staticType, 'String');
+ expect(hover.propagatedType, isNull);
+ }
+
+ Future<void> test_enum_method_declaration() async {
+ addTestFile('''
+library my.library;
+enum E {
+ v;
+ /// doc aaa
+ /// doc bbb
+ List<String> mmm(int a, String b) {
+ }
+}
+''');
+ var hover = await prepareHover('mmm(int a');
+ // element
+ expect(hover.containingLibraryName, 'bin/test.dart');
+ expect(hover.containingLibraryPath, testFile);
+ expect(hover.containingClassDescription, 'E');
+ expect(hover.dartdoc, '''doc aaa\ndoc bbb''');
+ expect(hover.elementDescription, 'List<String> mmm(int a, String b)');
+ expect(hover.elementKind, 'method');
+ // types
+ expect(hover.staticType, isNull);
+ expect(hover.propagatedType, isNull);
+ // no parameter
+ expect(hover.parameter, isNull);
+ }
+
+ Future<void> test_enum_method_reference() async {
+ addTestFile('''
+library my.library;
+enum E {
+ v;
+ List<String> mmm(int a, String b) {
+ }
+}
+void f(E e) {
+ e.mmm(42, 'foo');
+}
+''');
+ var hover = await prepareHover('mm(42, ');
+ // range
+ expect(hover.offset, findOffset('mmm(42, '));
+ expect(hover.length, 'mmm'.length);
+ // element
+ expect(hover.containingLibraryName, 'bin/test.dart');
+ expect(hover.containingLibraryPath, testFile);
+ expect(hover.containingClassDescription, 'E');
+ expect(hover.elementDescription, 'List<String> mmm(int a, String b)');
+ expect(hover.elementKind, 'method');
+ expect(hover.isDeprecated, isFalse);
+ // types
+ expect(hover.staticType, 'List<String> Function(int, String)');
+ expect(hover.propagatedType, isNull);
+ // no parameter
+ expect(hover.parameter, isNull);
+ }
+
+ Future<void> test_enum_setter_hasDocumentation() async {
+ addTestFile('''
+enum E {
+ v;
+ /// getting
+ int get foo => 42;
+ /// setting
+ set foo(int x) {}
+}
+void f(E e) {
+ e.foo = 123;
+}
+''');
+ var hover = await prepareHover('foo = ');
+ expect(hover.containingClassDescription, 'E');
+ expect(hover.dartdoc, '''setting''');
+ expect(hover.elementDescription, 'void set foo(int x)');
+ expect(hover.elementKind, 'setter');
+ }
+
Future<void> test_extensionDeclaration() async {
addTestFile('''
class A {}
@@ -586,31 +943,6 @@
expect(hover.parameter, isNull);
}
- Future<void> test_getter_synthetic() async {
- addTestFile('''
-library my.library;
-class A {
- /// doc aaa
- /// doc bbb
- String fff;
-}
-void f(A a) {
- print(a.fff);
-}
-''');
- var hover = await prepareHover('fff);');
- // element
- expect(hover.containingLibraryName, 'bin/test.dart');
- expect(hover.containingLibraryPath, testFile);
- expect(hover.containingClassDescription, 'A');
- expect(hover.dartdoc, '''doc aaa\ndoc bbb''');
- expect(hover.elementDescription, 'String fff');
- expect(hover.elementKind, 'field');
- // types
- expect(hover.staticType, 'String');
- expect(hover.propagatedType, isNull);
- }
-
Future<void> test_integerLiteral() async {
addTestFile('''
void f() {
@@ -726,109 +1058,6 @@
expect(hover.propagatedType, null);
}
- Future<void> test_method_declaration() async {
- addTestFile('''
-library my.library;
-class A {
- /// doc aaa
- /// doc bbb
- List<String> mmm(int a, String b) {
- }
-}
-''');
- var hover = await prepareHover('mmm(int a');
- // element
- expect(hover.containingLibraryName, 'bin/test.dart');
- expect(hover.containingLibraryPath, testFile);
- expect(hover.containingClassDescription, 'A');
- expect(hover.dartdoc, '''doc aaa\ndoc bbb''');
- expect(hover.elementDescription, 'List<String> mmm(int a, String b)');
- expect(hover.elementKind, 'method');
- // types
- expect(hover.staticType, isNull);
- expect(hover.propagatedType, isNull);
- // no parameter
- expect(hover.parameter, isNull);
- }
-
- Future<void> test_method_reference() async {
- addTestFile('''
-library my.library;
-class A {
- List<String> mmm(int a, String b) {
- }
-}
-void f(A a) {
- a.mmm(42, 'foo');
-}
-''');
- var hover = await prepareHover('mm(42, ');
- // range
- expect(hover.offset, findOffset('mmm(42, '));
- expect(hover.length, 'mmm'.length);
- // element
- expect(hover.containingLibraryName, 'bin/test.dart');
- expect(hover.containingLibraryPath, testFile);
- expect(hover.elementDescription, 'List<String> mmm(int a, String b)');
- expect(hover.elementKind, 'method');
- expect(hover.isDeprecated, isFalse);
- // types
- expect(hover.staticType, 'List<String> Function(int, String)');
- expect(hover.propagatedType, isNull);
- // no parameter
- expect(hover.parameter, isNull);
- }
-
- Future<void> test_method_reference_deprecated() async {
- addTestFile('''
-class A {
- @deprecated
- static void test() {}
-}
-void f() {
- A.test();
-}
-''');
- var hover = await prepareHover('test();');
- // element
- expect(hover.containingLibraryPath, testFile);
- expect(hover.elementDescription, 'void test()');
- expect(hover.elementKind, 'method');
- expect(hover.isDeprecated, isTrue);
- }
-
- Future<void> test_method_reference_genericMethod() async {
- addTestFile('''
-library my.library;
-
-abstract class Stream<T> {
- Stream<S> transform<S>(StreamTransformer<T, S> streamTransformer);
-}
-abstract class StreamTransformer<T, S> {}
-
-f(Stream<int> s) {
- s.transform(null);
-}
-''');
- var hover = await prepareHover('nsform(n');
- // range
- expect(hover.offset, findOffset('transform(n'));
- expect(hover.length, 'transform'.length);
- // element
- expect(hover.containingLibraryName, 'bin/test.dart');
- expect(hover.containingLibraryPath, testFile);
- expect(hover.elementDescription,
- 'Stream<S> transform<S>(StreamTransformer<int, S> streamTransformer)');
- expect(hover.elementKind, 'method');
- expect(hover.isDeprecated, isFalse);
- // types
- expect(hover.staticType,
- 'Stream<dynamic> Function(StreamTransformer<int, dynamic>)');
- expect(hover.propagatedType, isNull);
- // no parameter
- expect(hover.parameter, isNull);
- }
-
Future<void> test_mixin_declaration() async {
addTestFile('''
mixin A on B, C implements D, E {}
@@ -1002,110 +1231,6 @@
expect(hover.staticType, 'int');
}
- Future<void> test_setter_hasDocumentation() async {
- addTestFile('''
-class A {
- /// getting
- int get foo => 42;
- /// setting
- set foo(int x) {}
-}
-void f(A a) {
- a.foo = 123;
-}
-''');
- var hover = await prepareHover('foo = ');
- expect(hover.containingClassDescription, 'A');
- expect(hover.dartdoc, '''setting''');
- expect(hover.elementDescription, 'void set foo(int x)');
- expect(hover.elementKind, 'setter');
- }
-
- Future<void> test_setter_noDocumentation() async {
- addTestFile('''
-class A {
- /// getting
- int get foo => 42;
- set foo(int x) {}
-}
-void f(A a) {
- a.foo = 123;
-}
-''');
- var hover = await prepareHover('foo = ');
- expect(hover.containingClassDescription, 'A');
- expect(hover.dartdoc, '''getting''');
- expect(hover.elementDescription, 'void set foo(int x)');
- expect(hover.elementKind, 'setter');
- }
-
- Future<void> test_setter_super_hasDocumentation() async {
- addTestFile('''
-class A {
- /// pgetting
- int get foo => 42;
- /// psetting
- set foo(int x) {}
-}
-class B extends A {
- /// getting
- int get foo => 42;
- set foo(int x) {}
-}
-void f(B b) {
- b.foo = 123;
-}
-''');
- var hover = await prepareHover('foo = ');
- expect(hover.containingClassDescription, 'B');
- expect(hover.dartdoc, '''psetting\n\nCopied from `A`.''');
- expect(hover.elementDescription, 'void set foo(int x)');
- expect(hover.elementKind, 'setter');
- }
-
- Future<void> test_setter_super_noDocumentation() async {
- addTestFile('''
-class A {
- /// pgetting
- int get foo => 42;
- set foo(int x) {}
-}
-class B extends A {
- int get foo => 42;
- set foo(int x) {}
-}
-void f(B b) {
- b.foo = 123;
-}
-''');
- var hover = await prepareHover('foo = ');
- expect(hover.containingClassDescription, 'B');
- expect(hover.dartdoc, '''pgetting\n\nCopied from `A`.''');
- expect(hover.elementDescription, 'void set foo(int x)');
- expect(hover.elementKind, 'setter');
- }
-
- @failingTest
- Future<void> test_setter_super_noSetter() async {
- addTestFile('''
-class A {
- /// pgetting
- int get foo => 42;
-}
-class B extends A {
- set foo(int x) {}
-}
-void f(B b) {
- b.foo = 123;
-}
-''');
- var hover = await prepareHover('foo = ');
- expect(hover.containingClassDescription, 'B');
- expect(hover.dartdoc, '''pgetting''');
- expect(hover.elementDescription, 'void set foo(int x)');
- expect(hover.elementKind, 'setter');
- }
-
Future<void> test_simpleIdentifier_typedef_functionType() async {
addTestFile('''
typedef A = void Function(int);
diff --git a/pkg/analysis_server/test/search/type_hierarchy_test.dart b/pkg/analysis_server/test/search/type_hierarchy_test.dart
index 2ed436f..c0d0616 100644
--- a/pkg/analysis_server/test/search/type_hierarchy_test.dart
+++ b/pkg/analysis_server/test/search/type_hierarchy_test.dart
@@ -371,6 +371,44 @@
]);
}
+ Future<void> test_class_fromField_toMixinGetter() async {
+ addTestFile('''
+abstract class A {
+ var test = 1;
+}
+class Mixin {
+ get test => 2;
+}
+class B extends A with Mixin {}
+''');
+ var items = await _getTypeHierarchy('test = 1;');
+ var itemA = items.firstWhere((e) => e.classElement.name == 'A');
+ var itemB = items.firstWhere((e) => e.classElement.name == 'B');
+ var memberA = itemA.memberElement!;
+ var memberB = itemB.memberElement!;
+ expect(memberA.location!.offset, findOffset('test = 1;'));
+ expect(memberB.location!.offset, findOffset('test => 2;'));
+ }
+
+ Future<void> test_class_fromField_toMixinSetter() async {
+ addTestFile('''
+abstract class A {
+ var test = 1;
+}
+class Mixin {
+ set test(m) {}
+}
+class B extends A with Mixin {}
+''');
+ var items = await _getTypeHierarchy('test = 1;');
+ var itemA = items.firstWhere((e) => e.classElement.name == 'A');
+ var itemB = items.firstWhere((e) => e.classElement.name == 'B');
+ var memberA = itemA.memberElement!;
+ var memberB = itemB.memberElement!;
+ expect(memberA.location!.offset, findOffset('test = 1;'));
+ expect(memberB.location!.offset, findOffset('test(m) {}'));
+ }
+
Future<void> test_class_implementsTypes() async {
addTestFile('''
class MA {}
@@ -432,6 +470,484 @@
]);
}
+ Future<void> test_class_member_fromField_toField() async {
+ addTestFile('''
+class A {
+ var test = 1;
+}
+class B extends A {
+ var test = 2;
+}
+''');
+
+ void checkItems(List<TypeHierarchyItem> items) {
+ var itemA = items.firstWhere((e) => e.classElement.name == 'A');
+ var itemB = items.firstWhere((e) => e.classElement.name == 'B');
+ var memberA = itemA.memberElement!;
+ var memberB = itemB.memberElement!;
+ expect(memberA.location!.offset, findOffset('test = 1;'));
+ expect(memberB.location!.offset, findOffset('test = 2;'));
+ }
+
+ checkItems(await _getTypeHierarchy('test = 1;'));
+ checkItems(await _getTypeHierarchy('test = 2;'));
+ }
+
+ Future<void> test_class_member_fromField_toGetter() async {
+ addTestFile('''
+class A {
+ get test => 1;
+}
+class B extends A {
+ var test = 2;
+}
+''');
+
+ void checkItems(List<TypeHierarchyItem> items) {
+ var itemA = items.firstWhere((e) => e.classElement.name == 'A');
+ var itemB = items.firstWhere((e) => e.classElement.name == 'B');
+ var memberA = itemA.memberElement!;
+ var memberB = itemB.memberElement!;
+ expect(memberA.location!.offset, findOffset('test => 1'));
+ expect(memberB.location!.offset, findOffset('test = 2;'));
+ }
+
+ checkItems(await _getTypeHierarchy('test => 1;'));
+ checkItems(await _getTypeHierarchy('test = 2;'));
+ }
+
+ Future<void> test_class_member_fromField_toSetter() async {
+ addTestFile('''
+class A {
+ set test(a) {}
+}
+class B extends A {
+ var test = 2;
+}
+''');
+
+ void checkItems(List<TypeHierarchyItem> items) {
+ var itemA = items.firstWhere((e) => e.classElement.name == 'A');
+ var itemB = items.firstWhere((e) => e.classElement.name == 'B');
+ var memberA = itemA.memberElement!;
+ var memberB = itemB.memberElement!;
+ expect(memberA.location!.offset, findOffset('test(a) {}'));
+ expect(memberB.location!.offset, findOffset('test = 2;'));
+ }
+
+ checkItems(await _getTypeHierarchy('test(a) {}'));
+ checkItems(await _getTypeHierarchy('test = 2;'));
+ }
+
+ Future<void> test_class_member_fromFinalField_toGetter() async {
+ addTestFile('''
+class A {
+ get test => 1;
+}
+class B extends A {
+ final test = 2;
+}
+''');
+
+ void checkItems(List<TypeHierarchyItem> items) {
+ var itemA = items.firstWhere((e) => e.classElement.name == 'A');
+ var itemB = items.firstWhere((e) => e.classElement.name == 'B');
+ var memberA = itemA.memberElement!;
+ var memberB = itemB.memberElement!;
+ expect(memberA.location!.offset, findOffset('test => 1'));
+ expect(memberB.location!.offset, findOffset('test = 2;'));
+ }
+
+ checkItems(await _getTypeHierarchy('test => 1;'));
+ checkItems(await _getTypeHierarchy('test = 2;'));
+ }
+
+ Future<void> test_class_member_fromFinalField_toSetter() async {
+ addTestFile('''
+class A {
+ set test(x) {}
+}
+class B extends A {
+ final test = 2;
+}
+''');
+ var items = await _getTypeHierarchy('test = 2;');
+ var itemA = items.firstWhere((e) => e.classElement.name == 'A');
+ var itemB = items.firstWhere((e) => e.classElement.name == 'B');
+ expect(itemA.memberElement, isNull);
+ expect(itemB.memberElement!.location!.offset, findOffset('test = 2;'));
+ }
+
+ Future<void> test_class_member_getter() async {
+ addTestFile('''
+class A {
+ get test => null; // in A
+}
+class B extends A {
+ get test => null; // in B
+}
+class C extends B {
+}
+class D extends C {
+ get test => null; // in D
+}
+''');
+ var items = await _getTypeHierarchy('test => null; // in B');
+ var itemB = items[0];
+ var itemA = items[itemB.superclass!];
+ var itemC = items[itemB.subclasses[0]];
+ var itemD = items[itemC.subclasses[0]];
+ expect(itemA.classElement.name, 'A');
+ expect(itemB.classElement.name, 'B');
+ expect(itemC.classElement.name, 'C');
+ expect(itemD.classElement.name, 'D');
+ expect(itemA.memberElement!.location!.offset,
+ findOffset('test => null; // in A'));
+ expect(itemB.memberElement!.location!.offset,
+ findOffset('test => null; // in B'));
+ expect(itemC.memberElement, isNull);
+ expect(itemD.memberElement!.location!.offset,
+ findOffset('test => null; // in D'));
+ }
+
+ Future<void> test_class_member_method() async {
+ addTestFile('''
+class A {
+ test() {} // in A
+}
+class B extends A {
+ test() {} // in B
+}
+class C extends B {
+}
+class D extends C {
+ test() {} // in D
+}
+''');
+ var items = await _getTypeHierarchy('test() {} // in B');
+ var itemB = items[0];
+ var itemA = items[itemB.superclass!];
+ var itemC = items[itemB.subclasses[0]];
+ var itemD = items[itemC.subclasses[0]];
+ expect(itemA.classElement.name, 'A');
+ expect(itemB.classElement.name, 'B');
+ expect(itemC.classElement.name, 'C');
+ expect(itemD.classElement.name, 'D');
+ expect(
+ itemA.memberElement!.location!.offset, findOffset('test() {} // in A'));
+ expect(
+ itemB.memberElement!.location!.offset, findOffset('test() {} // in B'));
+ expect(itemC.memberElement, isNull);
+ expect(
+ itemD.memberElement!.location!.offset, findOffset('test() {} // in D'));
+ }
+
+ Future<void> test_class_member_method_private_differentLib() async {
+ newFile(join(testFolder, 'lib.dart'), content: r'''
+import 'test.dart';
+class A {
+ void _m() {}
+}
+class C extends B {
+ void _m() {}
+}
+''');
+ addTestFile('''
+import 'lib.dart';
+class B extends A {
+ _m() {} // in B
+}
+class D extends C {
+ _m() {} // in D
+}
+''');
+ var items = await _getTypeHierarchy('_m() {} // in B');
+ var itemB = items[0];
+ var itemA = items[itemB.superclass!];
+ var itemC = items[itemB.subclasses[0]];
+ var itemD = items[itemC.subclasses[0]];
+ expect(itemB.classElement.name, 'B');
+ expect(itemA.classElement.name, 'A');
+ expect(itemC.classElement.name, 'C');
+ expect(itemD.classElement.name, 'D');
+ expect(itemA.memberElement, isNull);
+ expect(itemC.memberElement, isNull);
+ expect(itemB.memberElement, isNotNull);
+ expect(itemD.memberElement, isNotNull);
+ }
+
+ Future<void> test_class_member_method_private_sameLib() async {
+ addTestFile('''
+class A {
+ _m() {} // in A
+}
+class B extends A {
+ _m() {} // in B
+}
+class C extends B {
+ _m() {} // in C
+}
+''');
+ var items = await _getTypeHierarchy('_m() {} // in B');
+ var itemB = items[0];
+ var itemA = items[itemB.superclass!];
+ var itemC = items[itemB.subclasses[0]];
+ expect(itemA.classElement.name, 'A');
+ expect(itemB.classElement.name, 'B');
+ expect(itemC.classElement.name, 'C');
+ expect(
+ itemA.memberElement!.location!.offset, findOffset('_m() {} // in A'));
+ expect(
+ itemB.memberElement!.location!.offset, findOffset('_m() {} // in B'));
+ expect(
+ itemC.memberElement!.location!.offset, findOffset('_m() {} // in C'));
+ }
+
+ Future<void> test_class_member_ofMixin2_method() async {
+ addTestFile('''
+class M1 {
+ void test() {} // in M1
+}
+class M2 {
+ void test() {} // in M2
+}
+class D1 extends Object with M1 {}
+class D2 extends Object with M1, M2 {}
+class D3 extends Object with M2, M1 {}
+class D4 extends Object with M2, M1 {
+ void test() {} // in D4
+}
+''');
+ var items = await _getTypeHierarchy('test() {} // in M1');
+ var itemM1 = items.firstWhere((e) => e.classElement.name == 'M1');
+ var item1 = items.firstWhere((e) => e.classElement.name == 'D1');
+ var item2 = items.firstWhere((e) => e.classElement.name == 'D2');
+ var item3 = items.firstWhere((e) => e.classElement.name == 'D3');
+ var item4 = items.firstWhere((e) => e.classElement.name == 'D4');
+ expect(itemM1, isNotNull);
+ expect(item1, isNotNull);
+ expect(item2, isNotNull);
+ expect(item3, isNotNull);
+ expect(item4, isNotNull);
+ // D1 does not override
+ {
+ var member1 = item1.memberElement;
+ expect(member1, isNull);
+ }
+ // D2 mixes-in M2 last, which overrides
+ {
+ var member2 = item2.memberElement!;
+ expect(member2.location!.offset, findOffset('test() {} // in M2'));
+ }
+ // D3 mixes-in M1 last and does not override itself
+ {
+ var member3 = item3.memberElement;
+ expect(member3, isNull);
+ }
+ // D4 mixes-in M1 last, but it also overrides
+ {
+ var member4 = item4.memberElement!;
+ expect(member4.location!.offset, findOffset('test() {} // in D4'));
+ }
+ }
+
+ Future<void> test_class_member_ofMixin_getter() async {
+ addTestFile('''
+abstract class Base {
+ get test; // in Base
+}
+class Mixin {
+ get test => null; // in Mixin
+}
+class Derived1 extends Base with Mixin {}
+class Derived2 extends Base {
+ get test => null; // in Derived2
+}
+''');
+ var items = await _getTypeHierarchy('test; // in Base');
+ var itemBase = items.firstWhere((e) => e.classElement.name == 'Base');
+ var item1 = items.firstWhere((e) => e.classElement.name == 'Derived1');
+ var item2 = items.firstWhere((e) => e.classElement.name == 'Derived2');
+ var memberBase = itemBase.memberElement!;
+ var member1 = item1.memberElement!;
+ var member2 = item2.memberElement!;
+ expect(memberBase.location!.offset, findOffset('test; // in Base'));
+ expect(member1.location!.offset, findOffset('test => null; // in Mixin'));
+ expect(
+ member2.location!.offset, findOffset('test => null; // in Derived2'));
+ }
+
+ Future<void> test_class_member_ofMixin_method() async {
+ addTestFile('''
+abstract class Base {
+ void test(); // in Base
+}
+class Mixin {
+ void test() {} // in Mixin
+}
+class Derived1 extends Base with Mixin {}
+class Derived2 extends Base {
+ void test() {} // in Derived2
+}
+''');
+ var items = await _getTypeHierarchy('test(); // in Base');
+ var itemBase = items.firstWhere((e) => e.classElement.name == 'Base');
+ var item1 = items.firstWhere((e) => e.classElement.name == 'Derived1');
+ var item2 = items.firstWhere((e) => e.classElement.name == 'Derived2');
+ var memberBase = itemBase.memberElement!;
+ var member1 = item1.memberElement!;
+ var member2 = item2.memberElement!;
+ expect(memberBase.location!.offset, findOffset('test(); // in Base'));
+ expect(member1.location!.offset, findOffset('test() {} // in Mixin'));
+ expect(member2.location!.offset, findOffset('test() {} // in Derived2'));
+ }
+
+ Future<void> test_class_member_ofMixin_setter() async {
+ addTestFile('''
+abstract class Base {
+ set test(x); // in Base
+}
+class Mixin {
+ set test(x) {} // in Mixin
+}
+class Derived1 extends Base with Mixin {}
+class Derived2 extends Base {
+ set test(x) {} // in Derived2
+}
+''');
+ var items = await _getTypeHierarchy('test(x); // in Base');
+ var itemBase = items.firstWhere((e) => e.classElement.name == 'Base');
+ var item1 = items.firstWhere((e) => e.classElement.name == 'Derived1');
+ var item2 = items.firstWhere((e) => e.classElement.name == 'Derived2');
+ var memberBase = itemBase.memberElement!;
+ var member1 = item1.memberElement!;
+ var member2 = item2.memberElement!;
+ expect(memberBase.location!.offset, findOffset('test(x); // in Base'));
+ expect(member1.location!.offset, findOffset('test(x) {} // in Mixin'));
+ expect(member2.location!.offset, findOffset('test(x) {} // in Derived2'));
+ }
+
+ Future<void> test_class_member_ofSuperclassConstraint_getter() async {
+ addTestFile('''
+class A {
+ get test => 0; // in A
+}
+
+mixin M on A {
+ get test => 0; // in M
+}
+''');
+ var items = await _getTypeHierarchy('test => 0; // in A');
+
+ var inA = items.firstWhere((e) => e.classElement.name == 'A');
+ var inM = items.firstWhere((e) => e.classElement.name == 'M');
+
+ _assertMember(inA, 'test => 0; // in A');
+ _assertMember(inM, 'test => 0; // in M');
+ }
+
+ Future<void> test_class_member_ofSuperclassConstraint_method() async {
+ addTestFile('''
+class A {
+ void test() {} // in A
+}
+
+mixin M on A {
+ void test() {} // in M
+}
+''');
+ var items = await _getTypeHierarchy('test() {} // in A');
+
+ var inA = items.firstWhere((e) => e.classElement.name == 'A');
+ var inM = items.firstWhere((e) => e.classElement.name == 'M');
+
+ _assertMember(inA, 'test() {} // in A');
+ _assertMember(inM, 'test() {} // in M');
+ }
+
+ Future<void> test_class_member_ofSuperclassConstraint_setter() async {
+ addTestFile('''
+class A {
+ set test(x) {} // in A
+}
+
+mixin M on A {
+ set test(x) {} // in M
+}
+''');
+ var items = await _getTypeHierarchy('test(x) {} // in A');
+
+ var inA = items.firstWhere((e) => e.classElement.name == 'A');
+ var inM = items.firstWhere((e) => e.classElement.name == 'M');
+
+ _assertMember(inA, 'test(x) {} // in A');
+ _assertMember(inM, 'test(x) {} // in M');
+ }
+
+ Future<void> test_class_member_operator() async {
+ addTestFile('''
+class A {
+ operator ==(x) => null; // in A
+}
+class B extends A {
+ operator ==(x) => null; // in B
+}
+class C extends B {
+}
+class D extends C {
+ operator ==(x) => null; // in D
+}
+''');
+ var items = await _getTypeHierarchy('==(x) => null; // in B');
+ var itemB = items[0];
+ var itemA = items[itemB.superclass!];
+ var itemC = items[itemB.subclasses[0]];
+ var itemD = items[itemC.subclasses[0]];
+ expect(itemA.classElement.name, 'A');
+ expect(itemB.classElement.name, 'B');
+ expect(itemC.classElement.name, 'C');
+ expect(itemD.classElement.name, 'D');
+ expect(itemA.memberElement!.location!.offset,
+ findOffset('==(x) => null; // in A'));
+ expect(itemB.memberElement!.location!.offset,
+ findOffset('==(x) => null; // in B'));
+ expect(itemC.memberElement, isNull);
+ expect(itemD.memberElement!.location!.offset,
+ findOffset('==(x) => null; // in D'));
+ }
+
+ Future<void> test_class_member_setter() async {
+ addTestFile('''
+class A {
+ set test(x) {} // in A
+}
+class B extends A {
+ set test(x) {} // in B
+}
+class C extends B {
+}
+class D extends C {
+ set test(x) {} // in D
+}
+''');
+ var items = await _getTypeHierarchy('test(x) {} // in B');
+ var itemB = items[0];
+ var itemA = items[itemB.superclass!];
+ var itemC = items[itemB.subclasses[0]];
+ var itemD = items[itemC.subclasses[0]];
+ expect(itemA.classElement.name, 'A');
+ expect(itemB.classElement.name, 'B');
+ expect(itemC.classElement.name, 'C');
+ expect(itemD.classElement.name, 'D');
+ expect(itemA.memberElement!.location!.offset,
+ findOffset('test(x) {} // in A'));
+ expect(itemB.memberElement!.location!.offset,
+ findOffset('test(x) {} // in B'));
+ expect(itemC.memberElement, isNull);
+ expect(itemD.memberElement!.location!.offset,
+ findOffset('test(x) {} // in D'));
+ }
+
Future<void> test_class_order() async {
addTestFile('''
class A {}
@@ -542,6 +1058,76 @@
]);
}
+ Future<void> test_class_superOnly() async {
+ addTestFile('''
+class A {}
+class B {}
+class C extends A implements B {}
+class D extends C {}
+''');
+ var items = await _getTypeHierarchy('C extends', superOnly: true);
+ expect(_toJson(items), [
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'C',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 1,
+ 'interfaces': [3],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'A',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 2,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'Object',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'B',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 2,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ }
+ ]);
+ }
+
+ Future<void> test_class_superOnly_fileDoesNotExist() async {
+ var request = SearchGetTypeHierarchyParams(
+ convertPath('/does/not/exist.dart'), 0,
+ superOnly: true)
+ .toRequest(requestId);
+ var response = await serverChannel.sendRequest(request);
+ var items =
+ SearchGetTypeHierarchyResult.fromResponse(response).hierarchyItems;
+ expect(items, isNull);
+ }
+
Future<void> test_class_withTypes() async {
addTestFile('''
class MA {}
@@ -603,549 +1189,57 @@
]);
}
- Future<void> test_fromField_toMixinGetter() async {
+ Future<void> test_enum_displayName() async {
addTestFile('''
-abstract class A {
- var test = 1;
-}
-class Mixin {
- get test => 2;
-}
-class B extends A with Mixin {}
-''');
- var items = await _getTypeHierarchy('test = 1;');
- var itemA = items.firstWhere((e) => e.classElement.name == 'A');
- var itemB = items.firstWhere((e) => e.classElement.name == 'B');
- var memberA = itemA.memberElement!;
- var memberB = itemB.memberElement!;
- expect(memberA.location!.offset, findOffset('test = 1;'));
- expect(memberB.location!.offset, findOffset('test => 2;'));
- }
+mixin M<T> {}
- Future<void> test_fromField_toMixinSetter() async {
- addTestFile('''
-abstract class A {
- var test = 1;
-}
-class Mixin {
- set test(m) {}
-}
-class B extends A with Mixin {}
-''');
- var items = await _getTypeHierarchy('test = 1;');
- var itemA = items.firstWhere((e) => e.classElement.name == 'A');
- var itemB = items.firstWhere((e) => e.classElement.name == 'B');
- var memberA = itemA.memberElement!;
- var memberB = itemB.memberElement!;
- expect(memberA.location!.offset, findOffset('test = 1;'));
- expect(memberB.location!.offset, findOffset('test(m) {}'));
- }
-
- Future<void> test_member_fromField_toField() async {
- addTestFile('''
-class A {
- var test = 1;
-}
-class B extends A {
- var test = 2;
+enum E with M<int> {
+ v;
}
''');
+ var items = await _getTypeHierarchy('E with');
- void checkItems(List<TypeHierarchyItem> items) {
- var itemA = items.firstWhere((e) => e.classElement.name == 'A');
- var itemB = items.firstWhere((e) => e.classElement.name == 'B');
- var memberA = itemA.memberElement!;
- var memberB = itemB.memberElement!;
- expect(memberA.location!.offset, findOffset('test = 1;'));
- expect(memberB.location!.offset, findOffset('test = 2;'));
- }
-
- checkItems(await _getTypeHierarchy('test = 1;'));
- checkItems(await _getTypeHierarchy('test = 2;'));
- }
-
- Future<void> test_member_fromField_toGetter() async {
- addTestFile('''
-class A {
- get test => 1;
-}
-class B extends A {
- var test = 2;
-}
-''');
-
- void checkItems(List<TypeHierarchyItem> items) {
- var itemA = items.firstWhere((e) => e.classElement.name == 'A');
- var itemB = items.firstWhere((e) => e.classElement.name == 'B');
- var memberA = itemA.memberElement!;
- var memberB = itemB.memberElement!;
- expect(memberA.location!.offset, findOffset('test => 1'));
- expect(memberB.location!.offset, findOffset('test = 2;'));
- }
-
- checkItems(await _getTypeHierarchy('test => 1;'));
- checkItems(await _getTypeHierarchy('test = 2;'));
- }
-
- Future<void> test_member_fromField_toSetter() async {
- addTestFile('''
-class A {
- set test(a) {}
-}
-class B extends A {
- var test = 2;
-}
-''');
-
- void checkItems(List<TypeHierarchyItem> items) {
- var itemA = items.firstWhere((e) => e.classElement.name == 'A');
- var itemB = items.firstWhere((e) => e.classElement.name == 'B');
- var memberA = itemA.memberElement!;
- var memberB = itemB.memberElement!;
- expect(memberA.location!.offset, findOffset('test(a) {}'));
- expect(memberB.location!.offset, findOffset('test = 2;'));
- }
-
- checkItems(await _getTypeHierarchy('test(a) {}'));
- checkItems(await _getTypeHierarchy('test = 2;'));
- }
-
- Future<void> test_member_fromFinalField_toGetter() async {
- addTestFile('''
-class A {
- get test => 1;
-}
-class B extends A {
- final test = 2;
-}
-''');
-
- void checkItems(List<TypeHierarchyItem> items) {
- var itemA = items.firstWhere((e) => e.classElement.name == 'A');
- var itemB = items.firstWhere((e) => e.classElement.name == 'B');
- var memberA = itemA.memberElement!;
- var memberB = itemB.memberElement!;
- expect(memberA.location!.offset, findOffset('test => 1'));
- expect(memberB.location!.offset, findOffset('test = 2;'));
- }
-
- checkItems(await _getTypeHierarchy('test => 1;'));
- checkItems(await _getTypeHierarchy('test = 2;'));
- }
-
- Future<void> test_member_fromFinalField_toSetter() async {
- addTestFile('''
-class A {
- set test(x) {}
-}
-class B extends A {
- final test = 2;
-}
-''');
- var items = await _getTypeHierarchy('test = 2;');
- var itemA = items.firstWhere((e) => e.classElement.name == 'A');
- var itemB = items.firstWhere((e) => e.classElement.name == 'B');
- expect(itemA.memberElement, isNull);
- expect(itemB.memberElement!.location!.offset, findOffset('test = 2;'));
- }
-
- Future<void> test_member_getter() async {
- addTestFile('''
-class A {
- get test => null; // in A
-}
-class B extends A {
- get test => null; // in B
-}
-class C extends B {
-}
-class D extends C {
- get test => null; // in D
-}
-''');
- var items = await _getTypeHierarchy('test => null; // in B');
var itemB = items[0];
+ expect(itemB.classElement.name, 'E');
+
var itemA = items[itemB.superclass!];
- var itemC = items[itemB.subclasses[0]];
- var itemD = items[itemC.subclasses[0]];
- expect(itemA.classElement.name, 'A');
- expect(itemB.classElement.name, 'B');
- expect(itemC.classElement.name, 'C');
- expect(itemD.classElement.name, 'D');
- expect(itemA.memberElement!.location!.offset,
- findOffset('test => null; // in A'));
- expect(itemB.memberElement!.location!.offset,
- findOffset('test => null; // in B'));
- expect(itemC.memberElement, isNull);
- expect(itemD.memberElement!.location!.offset,
- findOffset('test => null; // in D'));
+ expect(itemA.classElement.name, 'Enum');
+ expect(itemA.displayName, isNull);
+
+ expect(itemB.mixins, hasLength(1));
+ var itemM = items[itemB.mixins[0]];
+ expect(itemM.classElement.name, 'M');
+ expect(itemM.displayName, 'M<int>');
}
- Future<void> test_member_method() async {
- addTestFile('''
-class A {
- test() {} // in A
-}
-class B extends A {
- test() {} // in B
-}
-class C extends B {
-}
-class D extends C {
- test() {} // in D
-}
-''');
- var items = await _getTypeHierarchy('test() {} // in B');
- var itemB = items[0];
- var itemA = items[itemB.superclass!];
- var itemC = items[itemB.subclasses[0]];
- var itemD = items[itemC.subclasses[0]];
- expect(itemA.classElement.name, 'A');
- expect(itemB.classElement.name, 'B');
- expect(itemC.classElement.name, 'C');
- expect(itemD.classElement.name, 'D');
- expect(
- itemA.memberElement!.location!.offset, findOffset('test() {} // in A'));
- expect(
- itemB.memberElement!.location!.offset, findOffset('test() {} // in B'));
- expect(itemC.memberElement, isNull);
- expect(
- itemD.memberElement!.location!.offset, findOffset('test() {} // in D'));
- }
-
- Future<void> test_member_method_private_differentLib() async {
- newFile(join(testFolder, 'lib.dart'), content: r'''
-import 'test.dart';
-class A {
- void _m() {}
-}
-class C extends B {
- void _m() {}
-}
-''');
- addTestFile('''
-import 'lib.dart';
-class B extends A {
- _m() {} // in B
-}
-class D extends C {
- _m() {} // in D
-}
-''');
- var items = await _getTypeHierarchy('_m() {} // in B');
- var itemB = items[0];
- var itemA = items[itemB.superclass!];
- var itemC = items[itemB.subclasses[0]];
- var itemD = items[itemC.subclasses[0]];
- expect(itemB.classElement.name, 'B');
- expect(itemA.classElement.name, 'A');
- expect(itemC.classElement.name, 'C');
- expect(itemD.classElement.name, 'D');
- expect(itemA.memberElement, isNull);
- expect(itemC.memberElement, isNull);
- expect(itemB.memberElement, isNotNull);
- expect(itemD.memberElement, isNotNull);
- }
-
- Future<void> test_member_method_private_sameLib() async {
- addTestFile('''
-class A {
- _m() {} // in A
-}
-class B extends A {
- _m() {} // in B
-}
-class C extends B {
- _m() {} // in C
-}
-''');
- var items = await _getTypeHierarchy('_m() {} // in B');
- var itemB = items[0];
- var itemA = items[itemB.superclass!];
- var itemC = items[itemB.subclasses[0]];
- expect(itemA.classElement.name, 'A');
- expect(itemB.classElement.name, 'B');
- expect(itemC.classElement.name, 'C');
- expect(
- itemA.memberElement!.location!.offset, findOffset('_m() {} // in A'));
- expect(
- itemB.memberElement!.location!.offset, findOffset('_m() {} // in B'));
- expect(
- itemC.memberElement!.location!.offset, findOffset('_m() {} // in C'));
- }
-
- Future<void> test_member_ofMixin2_method() async {
- addTestFile('''
-class M1 {
- void test() {} // in M1
-}
-class M2 {
- void test() {} // in M2
-}
-class D1 extends Object with M1 {}
-class D2 extends Object with M1, M2 {}
-class D3 extends Object with M2, M1 {}
-class D4 extends Object with M2, M1 {
- void test() {} // in D4
-}
-''');
- var items = await _getTypeHierarchy('test() {} // in M1');
- var itemM1 = items.firstWhere((e) => e.classElement.name == 'M1');
- var item1 = items.firstWhere((e) => e.classElement.name == 'D1');
- var item2 = items.firstWhere((e) => e.classElement.name == 'D2');
- var item3 = items.firstWhere((e) => e.classElement.name == 'D3');
- var item4 = items.firstWhere((e) => e.classElement.name == 'D4');
- expect(itemM1, isNotNull);
- expect(item1, isNotNull);
- expect(item2, isNotNull);
- expect(item3, isNotNull);
- expect(item4, isNotNull);
- // D1 does not override
- {
- var member1 = item1.memberElement;
- expect(member1, isNull);
- }
- // D2 mixes-in M2 last, which overrides
- {
- var member2 = item2.memberElement!;
- expect(member2.location!.offset, findOffset('test() {} // in M2'));
- }
- // D3 mixes-in M1 last and does not override itself
- {
- var member3 = item3.memberElement;
- expect(member3, isNull);
- }
- // D4 mixes-in M1 last, but it also overrides
- {
- var member4 = item4.memberElement!;
- expect(member4.location!.offset, findOffset('test() {} // in D4'));
- }
- }
-
- Future<void> test_member_ofMixin_getter() async {
- addTestFile('''
-abstract class Base {
- get test; // in Base
-}
-class Mixin {
- get test => null; // in Mixin
-}
-class Derived1 extends Base with Mixin {}
-class Derived2 extends Base {
- get test => null; // in Derived2
-}
-''');
- var items = await _getTypeHierarchy('test; // in Base');
- var itemBase = items.firstWhere((e) => e.classElement.name == 'Base');
- var item1 = items.firstWhere((e) => e.classElement.name == 'Derived1');
- var item2 = items.firstWhere((e) => e.classElement.name == 'Derived2');
- var memberBase = itemBase.memberElement!;
- var member1 = item1.memberElement!;
- var member2 = item2.memberElement!;
- expect(memberBase.location!.offset, findOffset('test; // in Base'));
- expect(member1.location!.offset, findOffset('test => null; // in Mixin'));
- expect(
- member2.location!.offset, findOffset('test => null; // in Derived2'));
- }
-
- Future<void> test_member_ofMixin_method() async {
- addTestFile('''
-abstract class Base {
- void test(); // in Base
-}
-class Mixin {
- void test() {} // in Mixin
-}
-class Derived1 extends Base with Mixin {}
-class Derived2 extends Base {
- void test() {} // in Derived2
-}
-''');
- var items = await _getTypeHierarchy('test(); // in Base');
- var itemBase = items.firstWhere((e) => e.classElement.name == 'Base');
- var item1 = items.firstWhere((e) => e.classElement.name == 'Derived1');
- var item2 = items.firstWhere((e) => e.classElement.name == 'Derived2');
- var memberBase = itemBase.memberElement!;
- var member1 = item1.memberElement!;
- var member2 = item2.memberElement!;
- expect(memberBase.location!.offset, findOffset('test(); // in Base'));
- expect(member1.location!.offset, findOffset('test() {} // in Mixin'));
- expect(member2.location!.offset, findOffset('test() {} // in Derived2'));
- }
-
- Future<void> test_member_ofMixin_setter() async {
- addTestFile('''
-abstract class Base {
- set test(x); // in Base
-}
-class Mixin {
- set test(x) {} // in Mixin
-}
-class Derived1 extends Base with Mixin {}
-class Derived2 extends Base {
- set test(x) {} // in Derived2
-}
-''');
- var items = await _getTypeHierarchy('test(x); // in Base');
- var itemBase = items.firstWhere((e) => e.classElement.name == 'Base');
- var item1 = items.firstWhere((e) => e.classElement.name == 'Derived1');
- var item2 = items.firstWhere((e) => e.classElement.name == 'Derived2');
- var memberBase = itemBase.memberElement!;
- var member1 = item1.memberElement!;
- var member2 = item2.memberElement!;
- expect(memberBase.location!.offset, findOffset('test(x); // in Base'));
- expect(member1.location!.offset, findOffset('test(x) {} // in Mixin'));
- expect(member2.location!.offset, findOffset('test(x) {} // in Derived2'));
- }
-
- Future<void> test_member_ofSuperclassConstraint_getter() async {
- addTestFile('''
-class A {
- get test => 0; // in A
-}
-
-mixin M on A {
- get test => 0; // in M
-}
-''');
- var items = await _getTypeHierarchy('test => 0; // in A');
-
- var inA = items.firstWhere((e) => e.classElement.name == 'A');
- var inM = items.firstWhere((e) => e.classElement.name == 'M');
-
- _assertMember(inA, 'test => 0; // in A');
- _assertMember(inM, 'test => 0; // in M');
- }
-
- Future<void> test_member_ofSuperclassConstraint_method() async {
- addTestFile('''
-class A {
- void test() {} // in A
-}
-
-mixin M on A {
- void test() {} // in M
-}
-''');
- var items = await _getTypeHierarchy('test() {} // in A');
-
- var inA = items.firstWhere((e) => e.classElement.name == 'A');
- var inM = items.firstWhere((e) => e.classElement.name == 'M');
-
- _assertMember(inA, 'test() {} // in A');
- _assertMember(inM, 'test() {} // in M');
- }
-
- Future<void> test_member_ofSuperclassConstraint_setter() async {
- addTestFile('''
-class A {
- set test(x) {} // in A
-}
-
-mixin M on A {
- set test(x) {} // in M
-}
-''');
- var items = await _getTypeHierarchy('test(x) {} // in A');
-
- var inA = items.firstWhere((e) => e.classElement.name == 'A');
- var inM = items.firstWhere((e) => e.classElement.name == 'M');
-
- _assertMember(inA, 'test(x) {} // in A');
- _assertMember(inM, 'test(x) {} // in M');
- }
-
- Future<void> test_member_operator() async {
- addTestFile('''
-class A {
- operator ==(x) => null; // in A
-}
-class B extends A {
- operator ==(x) => null; // in B
-}
-class C extends B {
-}
-class D extends C {
- operator ==(x) => null; // in D
-}
-''');
- var items = await _getTypeHierarchy('==(x) => null; // in B');
- var itemB = items[0];
- var itemA = items[itemB.superclass!];
- var itemC = items[itemB.subclasses[0]];
- var itemD = items[itemC.subclasses[0]];
- expect(itemA.classElement.name, 'A');
- expect(itemB.classElement.name, 'B');
- expect(itemC.classElement.name, 'C');
- expect(itemD.classElement.name, 'D');
- expect(itemA.memberElement!.location!.offset,
- findOffset('==(x) => null; // in A'));
- expect(itemB.memberElement!.location!.offset,
- findOffset('==(x) => null; // in B'));
- expect(itemC.memberElement, isNull);
- expect(itemD.memberElement!.location!.offset,
- findOffset('==(x) => null; // in D'));
- }
-
- Future<void> test_member_setter() async {
- addTestFile('''
-class A {
- set test(x) {} // in A
-}
-class B extends A {
- set test(x) {} // in B
-}
-class C extends B {
-}
-class D extends C {
- set test(x) {} // in D
-}
-''');
- var items = await _getTypeHierarchy('test(x) {} // in B');
- var itemB = items[0];
- var itemA = items[itemB.superclass!];
- var itemC = items[itemB.subclasses[0]];
- var itemD = items[itemC.subclasses[0]];
- expect(itemA.classElement.name, 'A');
- expect(itemB.classElement.name, 'B');
- expect(itemC.classElement.name, 'C');
- expect(itemD.classElement.name, 'D');
- expect(itemA.memberElement!.location!.offset,
- findOffset('test(x) {} // in A'));
- expect(itemB.memberElement!.location!.offset,
- findOffset('test(x) {} // in B'));
- expect(itemC.memberElement, isNull);
- expect(itemD.memberElement!.location!.offset,
- findOffset('test(x) {} // in D'));
- }
-
- Future<void> test_superOnly() async {
+ Future<void> test_enum_implements() async {
addTestFile('''
class A {}
class B {}
-class C extends A implements B {}
-class D extends C {}
+enum E implements A, B {
+ v;
+}
''');
- var items = await _getTypeHierarchy('C extends', superOnly: true);
+ var items = await _getTypeHierarchy('E implements');
expect(_toJson(items), [
{
'classElement': {
- 'kind': 'CLASS',
- 'name': 'C',
+ 'kind': 'ENUM',
+ 'name': 'E',
'location': anything,
'flags': 0
},
'superclass': 1,
- 'interfaces': [3],
+ 'interfaces': [3, 4],
'mixins': [],
'subclasses': []
},
{
'classElement': {
'kind': 'CLASS',
- 'name': 'A',
+ 'name': 'Enum',
'location': anything,
- 'flags': 0
+ 'flags': 1
},
'superclass': 2,
'interfaces': [],
@@ -1166,6 +1260,18 @@
{
'classElement': {
'kind': 'CLASS',
+ 'name': 'A',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 2,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
'name': 'B',
'location': anything,
'flags': 0
@@ -1178,15 +1284,179 @@
]);
}
- Future<void> test_superOnly_fileDoesNotExist() async {
- var request = SearchGetTypeHierarchyParams(
- convertPath('/does/not/exist.dart'), 0,
- superOnly: true)
- .toRequest(requestId);
- var response = await serverChannel.sendRequest(request);
- var items =
- SearchGetTypeHierarchyResult.fromResponse(response).hierarchyItems;
- expect(items, isNull);
+ Future<void> test_enum_member_getter() async {
+ addTestFile('''
+class A {
+ int get test => 0; // in A
+}
+class B extends A {
+ int get test => 0; // in B
+}
+class C extends B {
+}
+enum E implements C {
+ v;
+ int get test => 0; // in D
+}
+''');
+ var items = await _getTypeHierarchy('test => 0; // in B');
+ var itemB = items[0];
+ var itemA = items[itemB.superclass!];
+ var itemC = items[itemB.subclasses[0]];
+ var itemE = items[itemC.subclasses[0]];
+ expect(itemA.classElement.name, 'A');
+ expect(itemB.classElement.name, 'B');
+ expect(itemC.classElement.name, 'C');
+ expect(itemE.classElement.name, 'E');
+ expect(
+ itemA.memberElement!.location!.offset,
+ findOffset('test => 0; // in A'),
+ );
+ expect(
+ itemB.memberElement!.location!.offset,
+ findOffset('test => 0; // in B'),
+ );
+ expect(itemC.memberElement, isNull);
+ expect(
+ itemE.memberElement!.location!.offset,
+ findOffset('test => 0; // in D'),
+ );
+ }
+
+ Future<void> test_enum_member_method() async {
+ addTestFile('''
+class A {
+ void test() {} // in A
+}
+class B extends A {
+ void test() {} // in B
+}
+class C extends B {
+}
+enum E implements C {
+ v;
+ void test() {} // in E
+}
+''');
+ var items = await _getTypeHierarchy('test() {} // in B');
+ var itemB = items[0];
+ var itemA = items[itemB.superclass!];
+ var itemC = items[itemB.subclasses[0]];
+ var itemE = items[itemC.subclasses[0]];
+ expect(itemA.classElement.name, 'A');
+ expect(itemB.classElement.name, 'B');
+ expect(itemC.classElement.name, 'C');
+ expect(itemE.classElement.name, 'E');
+ expect(
+ itemA.memberElement!.location!.offset,
+ findOffset('test() {} // in A'),
+ );
+ expect(
+ itemB.memberElement!.location!.offset,
+ findOffset('test() {} // in B'),
+ );
+ expect(itemC.memberElement, isNull);
+ expect(
+ itemE.memberElement!.location!.offset,
+ findOffset('test() {} // in E'),
+ );
+ }
+
+ Future<void> test_enum_member_setter() async {
+ addTestFile('''
+class A {
+ set test(int x) {} // in A
+}
+class B extends A {
+ set test(int x) {} // in B
+}
+class C extends B {
+}
+enum E implements C {
+ v;
+ set test(int x) {} // in E
+}
+''');
+ var items = await _getTypeHierarchy('test(int x) {} // in B');
+ var itemB = items[0];
+ var itemA = items[itemB.superclass!];
+ var itemC = items[itemB.subclasses[0]];
+ var itemE = items[itemC.subclasses[0]];
+ expect(itemA.classElement.name, 'A');
+ expect(itemB.classElement.name, 'B');
+ expect(itemC.classElement.name, 'C');
+ expect(itemE.classElement.name, 'E');
+ expect(
+ itemA.memberElement!.location!.offset,
+ findOffset('test(int x) {} // in A'),
+ );
+ expect(
+ itemB.memberElement!.location!.offset,
+ findOffset('test(int x) {} // in B'),
+ );
+ expect(itemC.memberElement, isNull);
+ expect(
+ itemE.memberElement!.location!.offset,
+ findOffset('test(int x) {} // in E'),
+ );
+ }
+
+ Future<void> test_enum_with() async {
+ addTestFile('''
+mixin M {}
+enum E with M {
+ v;
+}
+''');
+ var items = await _getTypeHierarchy('E with');
+ expect(_toJson(items), [
+ {
+ 'classElement': {
+ 'kind': 'ENUM',
+ 'name': 'E',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 1,
+ 'interfaces': [],
+ 'mixins': [3],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'Enum',
+ 'location': anything,
+ 'flags': 1
+ },
+ 'superclass': 2,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'Object',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'MIXIN',
+ 'name': 'M',
+ 'location': anything,
+ 'flags': 1
+ },
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ }
+ ]);
}
void _assertMember(TypeHierarchyItem item, String search) {
diff --git a/pkg/analysis_server/test/services/refactoring/extract_method_test.dart b/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
index 98584b89..865fbcb 100644
--- a/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
@@ -11,15 +11,129 @@
void main() {
defineReflectiveSuite(() {
+ defineReflectiveTests(ExtractMethodEnumTest);
defineReflectiveTests(ExtractMethodTest);
});
}
@reflectiveTest
-class ExtractMethodTest extends RefactoringTest {
- @override
- late ExtractMethodRefactoringImpl refactoring;
+class ExtractMethodEnumTest extends _ExtractMethodTest {
+ Future<void> test_bad_conflict_method_alreadyDeclaresMethod() async {
+ await indexTestUnit('''
+enum E {
+ v;
+ void res() {}
+ void foo() {
+// start
+ print(0);
+// end
+ }
+}
+''');
+ _createRefactoringForStartEndComments();
+ return _assertConditionsError(
+ "Enum 'E' already declares method with name 'res'.");
+ }
+ Future<void> test_bad_conflict_method_shadowsSuperDeclaration() async {
+ await indexTestUnit('''
+mixin M {
+ void res() {}
+}
+
+enum E with M {
+ v;
+ void foo() {
+ res();
+// start
+ print(0);
+// end
+ }
+}
+''');
+ _createRefactoringForStartEndComments();
+ return _assertConditionsError("Created method will shadow method 'M.res'.");
+ }
+
+ Future<void> test_bad_conflict_topLevel_willHideInheritedMemberUsage() async {
+ await indexTestUnit('''
+mixin M {
+ void res() {}
+}
+
+enum E with M {
+ v;
+ void foo() {
+ res();
+ }
+}
+
+void f() {
+// start
+ print(0);
+// end
+}
+''');
+ _createRefactoringForStartEndComments();
+ return _assertConditionsError(
+ "Created function will shadow method 'M.res'.");
+ }
+
+ Future<void> test_singleExpression_method() async {
+ await indexTestUnit('''
+enum E {
+ v;
+ void foo() {
+ int a = 1 + 2;
+ }
+}
+''');
+ _createRefactoringForString('1 + 2');
+ // apply refactoring
+ return _assertSuccessfulRefactoring('''
+enum E {
+ v;
+ void foo() {
+ int a = res();
+ }
+
+ int res() => 1 + 2;
+}
+''');
+ }
+
+ Future<void> test_statements_method() async {
+ await indexTestUnit('''
+enum E {
+ v;
+ void foo() {
+// start
+ print(0);
+// end
+ }
+}
+''');
+ _createRefactoringForStartEndComments();
+ // apply refactoring
+ return _assertSuccessfulRefactoring('''
+enum E {
+ v;
+ void foo() {
+// start
+ res();
+// end
+ }
+
+ void res() {
+ print(0);
+ }
+}
+''');
+ }
+}
+
+@reflectiveTest
+class ExtractMethodTest extends _ExtractMethodTest {
Future<void> test_bad_assignmentLeftHandSide() async {
await indexTestUnit('''
void f() {
@@ -2877,6 +2991,11 @@
Completer<int> newCompleter() => null;
''');
}
+}
+
+class _ExtractMethodTest extends RefactoringTest {
+ @override
+ late ExtractMethodRefactoringImpl refactoring;
Future _assertConditionsError(String message) async {
var status = await refactoring.checkAllConditions();
diff --git a/pkg/analysis_server/test/services/refactoring/inline_method_test.dart b/pkg/analysis_server/test/services/refactoring/inline_method_test.dart
index 35d678c..e775831 100644
--- a/pkg/analysis_server/test/services/refactoring/inline_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/inline_method_test.dart
@@ -12,17 +12,86 @@
void main() {
defineReflectiveSuite(() {
+ defineReflectiveTests(InlineMethodEnumTest);
defineReflectiveTests(InlineMethodTest);
});
}
@reflectiveTest
-class InlineMethodTest extends RefactoringTest {
- @override
- late InlineMethodRefactoringImpl refactoring;
- bool? deleteSource;
- bool? inlineAll;
+class InlineMethodEnumTest extends _InlineMethodTest {
+ Future<void> test_getter_classMember_instance() async {
+ await indexTestUnit(r'''
+enum E {
+ v;
+ final int f = 0;
+ int get result => f + 1;
+}
+void f(E e) {
+ print(e.result);
+}
+''');
+ _createRefactoring('result =>');
+ // validate change
+ return _assertSuccessfulRefactoring(r'''
+enum E {
+ v;
+ final int f = 0;
+}
+void f(E e) {
+ print(e.f + 1);
+}
+''');
+ }
+ Future<void> test_getter_classMember_static() async {
+ await indexTestUnit(r'''
+enum E {
+ v;
+ static int get result => 1 + 2;
+}
+void f() {
+ print(E.result);
+}
+''');
+ _createRefactoring('result =>');
+ // validate change
+ return _assertSuccessfulRefactoring(r'''
+enum E {
+ v;
+}
+void f() {
+ print(1 + 2);
+}
+''');
+ }
+
+ Future<void> test_method_singleStatement() async {
+ await indexTestUnit(r'''
+enum E {
+ v;
+ void test() {
+ print(0);
+ }
+ void foo() {
+ test();
+ }
+}
+''');
+ _createRefactoring('test() {');
+ // validate change
+ return _assertSuccessfulRefactoring(r'''
+enum E {
+ v;
+ void foo() {
+ print(0);
+ }
+}
+''');
+ }
+}
+
+@reflectiveTest
+class InlineMethodTest extends _InlineMethodTest {
Future<void> test_access_FunctionElement() async {
await indexTestUnit(r'''
test(a, b) {
@@ -1749,6 +1818,13 @@
var a = 42;
''');
}
+}
+
+class _InlineMethodTest extends RefactoringTest {
+ @override
+ late InlineMethodRefactoringImpl refactoring;
+ bool? deleteSource;
+ bool? inlineAll;
Future _assertConditionsError(String message) async {
var status = await refactoring.checkAllConditions();
diff --git a/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart b/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart
index 6d0c321..524da05 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart
@@ -1102,7 +1102,7 @@
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
- "Class 'E' already declares method with name 'newName'.",
+ "Enum 'E' already declares method with name 'newName'.",
expectedContextSearch: 'newName() {} // existing');
}
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 61ddf10..1e78252 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -349,6 +349,7 @@
CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE,
CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY,
CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT,
+ CompileTimeErrorCode.NON_FINAL_FIELD_IN_ENUM,
CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR,
CompileTimeErrorCode.NON_GENERATIVE_IMPLICIT_CONSTRUCTOR,
CompileTimeErrorCode.NON_SYNC_FACTORY,
diff --git a/pkg/analyzer/lib/src/dart/element/greatest_lower_bound.dart b/pkg/analyzer/lib/src/dart/element/greatest_lower_bound.dart
index fdabfea..7e5b176 100644
--- a/pkg/analyzer/lib/src/dart/element/greatest_lower_bound.dart
+++ b/pkg/analyzer/lib/src/dart/element/greatest_lower_bound.dart
@@ -274,7 +274,7 @@
// The bounds of type parameters must be equal.
// Otherwise the result is `Never`.
var freshTypeFormalTypes =
- FunctionTypeImpl.relateTypeFormals(f, g, (t, s, _, __) => t == s);
+ FunctionTypeImpl.relateTypeFormals(f, g, (t, s) => t == s);
if (freshTypeFormalTypes == null) {
return NeverTypeImpl.instance;
}
diff --git a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
index 94dd226..55f46ff 100644
--- a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
+++ b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
@@ -724,7 +724,7 @@
// The bounds of type parameters must be equal.
// Otherwise the result is `Function`.
var freshTypeFormalTypes =
- FunctionTypeImpl.relateTypeFormals(f, g, (t, s, _, __) => t == s);
+ FunctionTypeImpl.relateTypeFormals(f, g, (t, s) => t == s);
if (freshTypeFormalTypes == null) {
return _interfaceTypeFunctionNone;
}
diff --git a/pkg/analyzer/lib/src/dart/element/subtype.dart b/pkg/analyzer/lib/src/dart/element/subtype.dart
index 4c8850c..50ec06d 100644
--- a/pkg/analyzer/lib/src/dart/element/subtype.dart
+++ b/pkg/analyzer/lib/src/dart/element/subtype.dart
@@ -351,8 +351,7 @@
}
// The bounds of type parameters must be equal.
- var freshTypeFormalTypes =
- FunctionTypeImpl.relateTypeFormals(f, g, (t, s, _, __) {
+ var freshTypeFormalTypes = FunctionTypeImpl.relateTypeFormals(f, g, (t, s) {
return isSubtypeOf(t, s) && isSubtypeOf(s, t);
});
if (freshTypeFormalTypes == null) {
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 94ec607..92ac1d9 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -180,8 +180,8 @@
// To test this, we instantiate both types with the same (unique) type
// variables, and see if the result is equal.
if (typeFormals.isNotEmpty) {
- var freshVariables = FunctionTypeImpl.relateTypeFormals(
- this, other, (t, s, _, __) => t == s);
+ var freshVariables =
+ FunctionTypeImpl.relateTypeFormals(this, other, (t, s) => t == s);
if (freshVariables == null) {
return false;
}
@@ -295,9 +295,7 @@
static List<TypeParameterType>? relateTypeFormals(
FunctionType f1,
FunctionType f2,
- bool Function(DartType bound2, DartType bound1,
- TypeParameterElement formal2, TypeParameterElement formal1)
- relation) {
+ bool Function(DartType bound2, DartType bound1) relation) {
List<TypeParameterElement> params1 = f1.typeFormals;
List<TypeParameterElement> params2 = f2.typeFormals;
return relateTypeFormals2(params1, params2, relation);
@@ -306,9 +304,7 @@
static List<TypeParameterType>? relateTypeFormals2(
List<TypeParameterElement> params1,
List<TypeParameterElement> params2,
- bool Function(DartType bound2, DartType bound1,
- TypeParameterElement formal2, TypeParameterElement formal1)
- relation) {
+ bool Function(DartType bound2, DartType bound1) relation) {
int count = params1.length;
if (params2.length != count) {
return null;
@@ -340,7 +336,7 @@
.substituteType(bound1);
bound2 = Substitution.fromPairs(variables2, variablesFresh)
.substituteType(bound2);
- if (!relation(bound2, bound1, p2, p1)) {
+ if (!relation(bound2, bound1)) {
return null;
}
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index ad9d61e..10bbfb6 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -10702,6 +10702,16 @@
);
/**
+ * No parameters.
+ */
+ static const CompileTimeErrorCode NON_FINAL_FIELD_IN_ENUM =
+ CompileTimeErrorCode(
+ 'NON_FINAL_FIELD_IN_ENUM',
+ "Enum can only declare final fields.",
+ correctionMessage: "Try making the field final.",
+ );
+
+ /**
* Parameters:
* 0: the non-generative constructor
*/
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 5d28080..c923c69 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -677,6 +677,7 @@
_checkForNotInitializedNonNullableStaticField(node);
_checkForWrongTypeParameterVarianceInField(node);
_checkForLateFinalFieldWithConstConstructor(node);
+ _checkForNonFinalFieldInEnum(node);
super.visitFieldDeclaration(node);
} finally {
_isInStaticVariableDeclaration = false;
@@ -3706,6 +3707,23 @@
CompileTimeErrorCode.NON_CONST_MAP_AS_EXPRESSION_STATEMENT, literal);
}
+ void _checkForNonFinalFieldInEnum(FieldDeclaration node) {
+ if (node.isStatic) return;
+
+ var variableList = node.fields;
+ if (variableList.isFinal) return;
+
+ var enclosingClass = _enclosingClass;
+ if (enclosingClass == null || !enclosingClass.isEnum) {
+ return;
+ }
+
+ errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.NON_FINAL_FIELD_IN_ENUM,
+ variableList.variables.first.name,
+ );
+ }
+
/// Verify that the given method [declaration] of operator `[]=`, has `void`
/// return type.
///
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 0269177..477c268 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -9073,6 +9073,10 @@
13.2 Expression Statements: It is a compile-time error if a non-constant
map literal that has no explicit type arguments appears in a place where a
statement is expected.
+ NON_FINAL_FIELD_IN_ENUM:
+ problemMessage: Enum can only declare final fields.
+ correctionMessage: Try making the field final.
+ comment: No parameters.
NON_GENERATIVE_CONSTRUCTOR:
problemMessage: "The generative constructor '{0}' is expected, but a factory was found."
correctionMessage: Try calling a different constructor of the superclass, or making the called constructor not be a factory constructor.
diff --git a/pkg/analyzer/test/src/diagnostics/non_final_field_in_enum_test.dart b/pkg/analyzer/test/src/diagnostics/non_final_field_in_enum_test.dart
new file mode 100644
index 0000000..e35f6d35
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/non_final_field_in_enum_test.dart
@@ -0,0 +1,37 @@
+// 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/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(NonFinalFieldInEnumTest);
+ });
+}
+
+@reflectiveTest
+class NonFinalFieldInEnumTest extends PubPackageResolutionTest {
+ test_instance_notFinal() async {
+ await assertErrorsInCode(r'''
+enum E {
+ v;
+ int foo = 0;
+}
+''', [
+ error(CompileTimeErrorCode.NON_FINAL_FIELD_IN_ENUM, 20, 3),
+ ]);
+ }
+
+ test_static_notFinal() async {
+ await assertNoErrorsInCode(r'''
+enum E {
+ v;
+ static int foo = 0;
+}
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/override_on_non_overriding_field_test.dart b/pkg/analyzer/test/src/diagnostics/override_on_non_overriding_field_test.dart
index 6e67586..5b5ba51 100644
--- a/pkg/analyzer/test/src/diagnostics/override_on_non_overriding_field_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/override_on_non_overriding_field_test.dart
@@ -71,10 +71,10 @@
enum E {
v;
@override
- int? foo;
+ final int foo = 0;
}
''', [
- error(HintCode.OVERRIDE_ON_NON_OVERRIDING_FIELD, 33, 3),
+ error(HintCode.OVERRIDE_ON_NON_OVERRIDING_FIELD, 38, 3),
]);
}
@@ -83,19 +83,15 @@
class A {
int get a => 0;
void set b(int _) {}
- int c = 0;
}
enum E implements A {
v;
@override
- final int a = 1;
+ int get a => 0;
@override
- int b = 0;
-
- @override
- int c = 0;
+ void set b(int _) {}
}
''');
}
@@ -110,10 +106,10 @@
enum E with M {
v;
@override
- final int a = 1;
+ int get a => 0;
@override
- int b = 0;
+ void set b(int _) {}
}
''');
}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 689de3e..8bfc4e8 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -529,6 +529,7 @@
import 'non_constant_map_value_test.dart' as non_constant_map_value;
import 'non_constant_set_element_test.dart' as non_constant_set_element;
import 'non_constant_type_argument_test.dart' as non_constant_type_argument;
+import 'non_final_field_in_enum_test.dart' as non_final_field_in_enum;
import 'non_generative_constructor_test.dart' as non_generative_constructor;
import 'non_generative_implicit_constructor_test.dart'
as non_generative_implicit_constructor;
@@ -1139,6 +1140,7 @@
non_constant_map_value_from_deferred_library.main();
non_constant_set_element.main();
non_constant_type_argument.main();
+ non_final_field_in_enum.main();
non_generative_constructor.main();
non_generative_implicit_constructor.main();
non_native_function_type_argument_to_pointer.main();
diff --git a/pkg/analyzer/test/src/diagnostics/values_declaration_in_enum_test.dart b/pkg/analyzer/test/src/diagnostics/values_declaration_in_enum_test.dart
index b66fed4..3deb2aa 100644
--- a/pkg/analyzer/test/src/diagnostics/values_declaration_in_enum_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/values_declaration_in_enum_test.dart
@@ -29,10 +29,10 @@
await assertErrorsInCode(r'''
enum E {
v;
- int values = 0;
+ final int values = 0;
}
''', [
- error(CompileTimeErrorCode.VALUES_DECLARATION_IN_ENUM, 20, 6),
+ error(CompileTimeErrorCode.VALUES_DECLARATION_IN_ENUM, 26, 6),
]);
}
diff --git a/pkg/analyzer_plugin/lib/src/utilities/navigation/navigation.dart b/pkg/analyzer_plugin/lib/src/utilities/navigation/navigation.dart
index 23709a3..da91052 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/navigation/navigation.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/navigation/navigation.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/element/element.dart' as analyzer;
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/source.dart' show SourceRange;
import 'package:analyzer_plugin/protocol/protocol_common.dart';
@@ -33,10 +34,8 @@
/// A concrete implementation of [NavigationCollector].
class NavigationCollectorImpl implements NavigationCollector {
- /// Whether the collector is collecting target code locations. Computers can
- /// skip computing these if this is false.
- @override
- final bool collectCodeLocations;
+ /// Each target which was created from an element is added here.
+ final List<TargetToUpdate> targetsToUpdate = [];
/// A list of navigation regions.
final List<NavigationRegion> regions = <NavigationRegion>[];
@@ -54,25 +53,24 @@
final Map<String, int> fileMap = <String, int>{};
- NavigationCollectorImpl({this.collectCodeLocations = false});
+ NavigationCollectorImpl();
@override
void addRange(
SourceRange range, ElementKind targetKind, Location targetLocation,
- {Location? targetCodeLocation}) {
+ {analyzer.Element? targetElement}) {
addRegion(range.offset, range.length, targetKind, targetLocation,
- targetCodeLocation: targetCodeLocation);
+ targetElement: targetElement);
}
@override
void addRegion(
int offset, int length, ElementKind targetKind, Location targetLocation,
- {Location? targetCodeLocation}) {
+ {analyzer.Element? targetElement}) {
var range = SourceRange(offset, length);
// add new target
var targets = regionMap.putIfAbsent(range, () => <int>[]);
- var targetIndex =
- _addTarget(targetKind, targetLocation, targetCodeLocation);
+ var targetIndex = _addTarget(targetKind, targetLocation, targetElement);
targets.add(targetIndex);
}
@@ -96,7 +94,8 @@
return index;
}
- int _addTarget(ElementKind kind, Location location, Location? codeLocation) {
+ int _addTarget(
+ ElementKind kind, Location location, analyzer.Element? element) {
var pair = Pair<ElementKind, Location>(kind, location);
var index = targetMap[pair];
if (index == null) {
@@ -104,12 +103,23 @@
var fileIndex = _addFile(file);
index = targets.length;
var target = NavigationTarget(kind, fileIndex, location.offset,
- location.length, location.startLine, location.startColumn,
- codeOffset: collectCodeLocations ? codeLocation?.offset : null,
- codeLength: collectCodeLocations ? codeLocation?.length : null);
+ location.length, location.startLine, location.startColumn);
targets.add(target);
targetMap[pair] = index;
+ if (element != null) {
+ targetsToUpdate.add(TargetToUpdate(element, target));
+ }
}
return index;
}
}
+
+/// The element and the navigation target created for it.
+///
+/// If code location feature is enabled, we update [target] using [element].
+class TargetToUpdate {
+ final analyzer.Element element;
+ final NavigationTarget target;
+
+ TargetToUpdate(this.element, this.target);
+}
diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation.dart
index 9e02f7a4f..1267087 100644
--- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation.dart
+++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/element/element.dart' as analyzer;
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer_plugin/protocol/protocol.dart';
@@ -26,21 +27,22 @@
///
/// Clients may not extend, implement or mix-in this class.
abstract class NavigationCollector {
- /// Whether the collector is collecting target code locations. Computers can
- /// skip computing these if this is false.
- bool get collectCodeLocations;
-
/// Record a new navigation region corresponding to the given [range] that
/// should navigate to the given [targetNameLocation].
void addRange(
SourceRange range, ElementKind targetKind, Location targetLocation,
- {Location targetCodeLocation});
+ {analyzer.Element? targetElement});
/// Record a new navigation region with the given [offset] and [length] that
/// should navigate to the given [targetNameLocation].
+ ///
+ /// In the most cases the [targetNameLocation] is already based on the
+ /// [targetElement], but in a few cases this method is invoked without an
+ /// element. The element is provided to associate it with the target, and
+ /// later update the target.
void addRegion(int offset, int length, ElementKind targetKind,
Location targetNameLocation,
- {Location? targetCodeLocation});
+ {analyzer.Element? targetElement});
}
/// An object used to produce navigation regions.
diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
index 2bd48bc..803d099 100644
--- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
@@ -2,7 +2,6 @@
// 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/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
@@ -92,12 +91,7 @@
return;
}
- var codeLocation = collector.collectCodeLocations
- ? _getCodeLocation(element, location, converter)
- : null;
-
- collector.addRegion(offset, length, kind, location,
- targetCodeLocation: codeLocation);
+ collector.addRegion(offset, length, kind, location, targetElement: element);
}
void _addRegion_nodeStart_nodeEnd(AstNode a, AstNode b, Element? element) {
@@ -121,51 +115,6 @@
_addRegion(offset, length, element);
}
- /// Get the location of the code (excluding leading doc comments) for this element.
- protocol.Location? _getCodeLocation(Element element,
- protocol.Location location, AnalyzerConverter converter) {
- var codeElement = element;
- // For synthetic getters created for fields, we need to access the associated
- // variable to get the codeOffset/codeLength.
- if (codeElement.isSynthetic && codeElement is PropertyAccessorElementImpl) {
- final variable = codeElement.variable;
- if (variable is ElementImpl) {
- codeElement = variable as ElementImpl;
- }
- }
-
- // Read the main codeOffset from the element. This may include doc comments
- // but will give the correct end position.
- int? codeOffset, codeLength;
- if (codeElement is ElementImpl) {
- codeOffset = codeElement.codeOffset;
- codeLength = codeElement.codeLength;
- }
-
- if (codeOffset == null || codeLength == null) {
- return null;
- }
-
- // Read the declaration so we can get the offset after the doc comments.
- // TODO(dantup): Skip this for parts (getParsedLibrary will throw), but find
- // a better solution.
- final declaration = _parsedDeclaration(codeElement);
- var node = declaration?.node;
- if (node is VariableDeclaration) {
- node = node.parent;
- }
- if (node is AnnotatedNode) {
- var offsetAfterDocs = node.firstTokenAfterCommentAndMetadata.offset;
-
- // Reduce the length by the difference between the end of docs and the start.
- codeLength -= (offsetAfterDocs - codeOffset);
- codeOffset = offsetAfterDocs;
- }
-
- return converter.locationFromElement(element,
- offset: codeOffset, length: codeLength);
- }
-
/// Checks if offset/length intersect with the range the user requested
/// navigation regions for.
///
@@ -185,25 +134,6 @@
}
return true;
}
-
- static ElementDeclarationResult? _parsedDeclaration(Element element) {
- var session = element.session;
- if (session == null) {
- return null;
- }
-
- var libraryPath = element.library?.source.fullName;
- if (libraryPath == null) {
- return null;
- }
-
- var parsedLibrary = session.getParsedLibrary(libraryPath);
- if (parsedLibrary is! ParsedLibraryResult) {
- return null;
- }
-
- return parsedLibrary.getElementDeclaration(element);
- }
}
class _DartNavigationComputerVisitor extends RecursiveAstVisitor<void> {
diff --git a/pkg/test_runner/lib/src/options.dart b/pkg/test_runner/lib/src/options.dart
index cd2c23c..db4f022 100644
--- a/pkg/test_runner/lib/src/options.dart
+++ b/pkg/test_runner/lib/src/options.dart
@@ -827,7 +827,8 @@
// Expand architectures.
var architectures = data["arch"] as String;
if (architectures == "all") {
- architectures = "ia32,x64,x64c,simarm,simarm64,simarm64c";
+ architectures =
+ "ia32,x64,x64c,simarm,simarm64,simarm64c,simriscv32,simriscv64";
}
for (var architectureName in architectures.split(",")) {
diff --git a/runtime/vm/base_isolate.h b/runtime/vm/base_isolate.h
index a8c09ee..0bfd061 100644
--- a/runtime/vm/base_isolate.h
+++ b/runtime/vm/base_isolate.h
@@ -21,12 +21,6 @@
class BaseIsolate {
public:
#if defined(DEBUG)
- void AssertCurrentThreadIsMutator() const;
-#else
- void AssertCurrentThreadIsMutator() const {}
-#endif // DEBUG
-
-#if defined(DEBUG)
static void AssertCurrent(BaseIsolate* isolate);
#endif
diff --git a/runtime/vm/dart_api_impl.h b/runtime/vm/dart_api_impl.h
index 73f681f..bb5eda5 100644
--- a/runtime/vm/dart_api_impl.h
+++ b/runtime/vm/dart_api_impl.h
@@ -334,7 +334,13 @@
#define START_NO_CALLBACK_SCOPE(thread) thread->IncrementNoCallbackScopeDepth()
// End a no Dart API call backs Scope.
-#define END_NO_CALLBACK_SCOPE(thread) thread->DecrementNoCallbackScopeDepth()
+#define END_NO_CALLBACK_SCOPE(thread) \
+ do { \
+ thread->DecrementNoCallbackScopeDepth(); \
+ if (thread->no_callback_scope_depth() == 0) { \
+ thread->heap()->CheckExternalGC(thread); \
+ } \
+ } while (false)
#define CHECK_CALLBACK_STATE(thread) \
if (thread->no_callback_scope_depth() != 0) { \
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 5691670..73c4f52 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -2808,6 +2808,34 @@
TestByteDataDirectAccess();
}
+static void NopCallback(void* isolate_callback_data, void* peer) {}
+
+TEST_CASE(DartAPI_ExternalAllocationDuringNoCallbackScope) {
+ Dart_Handle bytes = Dart_NewTypedData(Dart_TypedData_kUint8, 100);
+ EXPECT_VALID(bytes);
+
+ intptr_t gc_count_before = Thread::Current()->heap()->Collections(Heap::kNew);
+
+ Dart_TypedData_Type type;
+ void* data;
+ intptr_t len;
+ Dart_Handle result = Dart_TypedDataAcquireData(bytes, &type, &data, &len);
+ EXPECT_VALID(result);
+
+ Dart_WeakPersistentHandle weak =
+ Dart_NewWeakPersistentHandle(bytes, NULL, 100 * MB, NopCallback);
+ EXPECT_VALID(reinterpret_cast<Dart_Handle>(weak));
+
+ EXPECT_EQ(gc_count_before,
+ Thread::Current()->heap()->Collections(Heap::kNew));
+
+ result = Dart_TypedDataReleaseData(bytes);
+ EXPECT_VALID(result);
+
+ EXPECT_LT(gc_count_before,
+ Thread::Current()->heap()->Collections(Heap::kNew));
+}
+
static void ExternalTypedDataAccessTests(Dart_Handle obj,
Dart_TypedData_Type expected_type,
uint8_t data[],
@@ -2917,8 +2945,6 @@
EXPECT(value);
}
-static void NopCallback(void* isolate_callback_data, void* peer) {}
-
static void UnreachedCallback(void* isolate_callback_data, void* peer) {
UNREACHABLE();
}
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 286206f..feed760 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -149,30 +149,18 @@
}
void Heap::AllocatedExternal(intptr_t size, Space space) {
- ASSERT(Thread::Current()->no_safepoint_scope_depth() == 0);
if (space == kNew) {
- Isolate::Current()->AssertCurrentThreadIsMutator();
new_space_.AllocatedExternal(size);
- if (new_space_.ExternalInWords() <= (4 * new_space_.CapacityInWords())) {
- return;
- }
- // Attempt to free some external allocation by a scavenge. (If the total
- // remains above the limit, next external alloc will trigger another.)
- CollectGarbage(GCType::kScavenge, GCReason::kExternal);
- // Promotion may have pushed old space over its limit. Fall through for old
- // space GC check.
} else {
ASSERT(space == kOld);
old_space_.AllocatedExternal(size);
}
- if (old_space_.ReachedHardThreshold()) {
- if (last_gc_was_old_space_) {
- CollectNewSpaceGarbage(Thread::Current(), GCReason::kFull);
- }
- CollectGarbage(GCType::kMarkSweep, GCReason::kExternal);
+ Thread* thread = Thread::Current();
+ if (thread->no_callback_scope_depth() == 0) {
+ CheckExternalGC(thread);
} else {
- CheckStartConcurrentMarking(Thread::Current(), GCReason::kExternal);
+ // Check delayed until Dart_TypedDataRelease.
}
}
@@ -190,6 +178,27 @@
old_space_.AllocatedExternal(size);
}
+void Heap::CheckExternalGC(Thread* thread) {
+ ASSERT(thread->no_safepoint_scope_depth() == 0);
+ ASSERT(thread->no_callback_scope_depth() == 0);
+ if (new_space_.ExternalInWords() >= (4 * new_space_.CapacityInWords())) {
+ // Attempt to free some external allocation by a scavenge. (If the total
+ // remains above the limit, next external alloc will trigger another.)
+ CollectGarbage(GCType::kScavenge, GCReason::kExternal);
+ // Promotion may have pushed old space over its limit. Fall through for old
+ // space GC check.
+ }
+
+ if (old_space_.ReachedHardThreshold()) {
+ if (last_gc_was_old_space_) {
+ CollectNewSpaceGarbage(thread, GCReason::kFull);
+ }
+ CollectGarbage(GCType::kMarkSweep, GCReason::kExternal);
+ } else {
+ CheckStartConcurrentMarking(thread, GCReason::kExternal);
+ }
+}
+
bool Heap::Contains(uword addr) const {
return new_space_.Contains(addr) || old_space_.Contains(addr);
}
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h
index 121e075..59eae20 100644
--- a/runtime/vm/heap/heap.h
+++ b/runtime/vm/heap/heap.h
@@ -83,6 +83,7 @@
void FreedExternal(intptr_t size, Space space);
// Move external size from new to old space. Does not by itself trigger GC.
void PromotedExternal(intptr_t size);
+ void CheckExternalGC(Thread* thread);
// Heap contains the specified address.
bool Contains(uword addr) const;
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 46145f4..c6e70aa 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1656,11 +1656,6 @@
void BaseIsolate::AssertCurrent(BaseIsolate* isolate) {
ASSERT(isolate == Isolate::Current());
}
-
-void BaseIsolate::AssertCurrentThreadIsMutator() const {
- ASSERT(Isolate::Current() == this);
- ASSERT(Thread::Current()->IsMutatorThread());
-}
#endif // defined(DEBUG)
#if defined(DEBUG)
diff --git a/tools/VERSION b/tools/VERSION
index 0a5488c..6f1c666 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 165
+PRERELEASE 166
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/gn.py b/tools/gn.py
index b895393..2b43717 100755
--- a/tools/gn.py
+++ b/tools/gn.py
@@ -345,7 +345,7 @@
def ProcessOptions(args):
if args.arch == 'all':
- args.arch = 'ia32,x64,simarm,simarm64,x64c,simarm64c'
+ args.arch = 'ia32,x64,simarm,simarm64,x64c,simarm64c,simriscv32,simriscv64'
if args.mode == 'all':
args.mode = 'debug,release,product'
if args.os == 'all':