mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
Extension type. Updates and tests for search.
Change-Id: I17c5c9eb5237ba6f090cab974d4419e46b584ac8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/323504 Reviewed-by: Phil Quitslund <pquitslund@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
d4261d12ff
commit
702cca5a18
|
@ -1370,6 +1370,21 @@ void f() {
|
|||
assertHasResult(SearchResultKind.REFERENCE, 'int b');
|
||||
}
|
||||
|
||||
Future<void> test_typeReference_extensionType() async {
|
||||
addTestFile('''
|
||||
extension type A(int it) {}
|
||||
|
||||
extension type B(int it) implements A {}
|
||||
|
||||
void f(A a) {}
|
||||
''');
|
||||
await findElementReferences(search: 'A(int it)', false);
|
||||
expect(searchElement!.kind, ElementKind.EXTENSION_TYPE);
|
||||
expect(results, hasLength(2));
|
||||
assertHasResult(SearchResultKind.REFERENCE, 'A {}');
|
||||
assertHasResult(SearchResultKind.REFERENCE, 'A a) {}');
|
||||
}
|
||||
|
||||
Future<void> test_typeReference_typeAlias_functionType() async {
|
||||
addTestFile('''
|
||||
typedef F = Function();
|
||||
|
|
|
@ -224,6 +224,72 @@ enum B {
|
|||
assertHasDeclaration(ElementKind.SETTER, 'B');
|
||||
}
|
||||
|
||||
Future<void> test_extensionType_methodGetter() async {
|
||||
addTestFile('''
|
||||
extension type A(int it) {
|
||||
void foo() {}
|
||||
void bar() {}
|
||||
}
|
||||
extension type B(int it) {
|
||||
int get foo => 0;
|
||||
}
|
||||
''');
|
||||
await findMemberDeclarations('foo');
|
||||
expect(results, hasLength(2));
|
||||
assertHasDeclaration(ElementKind.METHOD, 'A');
|
||||
assertHasDeclaration(ElementKind.GETTER, 'B');
|
||||
}
|
||||
|
||||
Future<void> test_extensionType_methodGetterSetter() async {
|
||||
addTestFile('''
|
||||
extension type A(int it) {
|
||||
void foo() {}
|
||||
void bar() {}
|
||||
}
|
||||
extension type B(int it) {
|
||||
int get foo => 0;
|
||||
set foo(int _) {}
|
||||
}
|
||||
''');
|
||||
await findMemberDeclarations('foo');
|
||||
expect(results, hasLength(3));
|
||||
assertHasDeclaration(ElementKind.METHOD, 'A');
|
||||
assertHasDeclaration(ElementKind.GETTER, 'B');
|
||||
assertHasDeclaration(ElementKind.SETTER, 'B');
|
||||
}
|
||||
|
||||
Future<void> test_extensionType_methodMethod() async {
|
||||
addTestFile('''
|
||||
extension type A(int it) {
|
||||
void foo() {}
|
||||
void bar() {}
|
||||
}
|
||||
extension type B(int it) {
|
||||
void foo() {}
|
||||
}
|
||||
''');
|
||||
await findMemberDeclarations('foo');
|
||||
expect(results, hasLength(2));
|
||||
assertHasDeclaration(ElementKind.METHOD, 'A');
|
||||
assertHasDeclaration(ElementKind.METHOD, 'B');
|
||||
}
|
||||
|
||||
Future<void> test_extensionType_methodSetter() async {
|
||||
addTestFile('''
|
||||
extension type A(int it) {
|
||||
void foo() {}
|
||||
void bar() {}
|
||||
}
|
||||
extension type B(int it) {
|
||||
set foo(int _) {}
|
||||
}
|
||||
''');
|
||||
await findMemberDeclarations('foo');
|
||||
expect(results, hasLength(2));
|
||||
assertHasDeclaration(ElementKind.METHOD, 'A');
|
||||
assertHasDeclaration(ElementKind.SETTER, 'B');
|
||||
}
|
||||
|
||||
Future<void> test_localVariable() async {
|
||||
addTestFile('''
|
||||
class A {
|
||||
|
|
|
@ -201,6 +201,60 @@ void whenResolved(A a, B b) {
|
|||
b.foo(2);
|
||||
}
|
||||
|
||||
void whenUnresolved(a, b) {
|
||||
a.foo(10);
|
||||
b.foo(20);
|
||||
}
|
||||
''');
|
||||
await findMemberReferences('foo');
|
||||
assertNoResult(SearchResultKind.INVOCATION, 'foo(1)');
|
||||
assertNoResult(SearchResultKind.INVOCATION, 'foo(2)');
|
||||
assertHasRef(SearchResultKind.INVOCATION, 'foo(10)', true);
|
||||
assertHasRef(SearchResultKind.INVOCATION, 'foo(20)', true);
|
||||
}
|
||||
|
||||
Future<void> test_extensionType_fields_implicit() async {
|
||||
addTestFile('''
|
||||
extension type A(int it) {
|
||||
int get foo => 0;
|
||||
}
|
||||
|
||||
extension type B(int it) {
|
||||
int get foo => 0;
|
||||
}
|
||||
|
||||
void whenResolved(A a, B b) {
|
||||
a.foo; // resolved A
|
||||
b.foo; // resolved B
|
||||
}
|
||||
|
||||
void whenUnresolved(a, b) {
|
||||
a.foo; // unresolved A
|
||||
b.foo; // unresolved B
|
||||
}
|
||||
''');
|
||||
await findMemberReferences('foo');
|
||||
assertNoResult(SearchResultKind.READ, 'foo; // resolved A');
|
||||
assertNoResult(SearchResultKind.READ, 'foo; // resolved B');
|
||||
assertHasRef(SearchResultKind.READ, 'foo; // unresolved A', true);
|
||||
assertHasRef(SearchResultKind.READ, 'foo; // unresolved B', true);
|
||||
}
|
||||
|
||||
Future<void> test_extensionType_methods() async {
|
||||
addTestFile('''
|
||||
extension type A(int it) {
|
||||
void foo() {}
|
||||
}
|
||||
|
||||
extension type B(int it) {
|
||||
void foo() {}
|
||||
}
|
||||
|
||||
void whenResolved(A a, B b) {
|
||||
a.foo(1);
|
||||
b.foo(2);
|
||||
}
|
||||
|
||||
void whenUnresolved(a, b) {
|
||||
a.foo(10);
|
||||
b.foo(20);
|
||||
|
|
|
@ -87,6 +87,14 @@ extension MyExtension on int {}
|
|||
assertHasDeclaration(ElementKind.EXTENSION, 'MyExtension');
|
||||
}
|
||||
|
||||
Future<void> test_extensionTypeDeclaration() async {
|
||||
addTestFile('''
|
||||
extension type MyExtensionType(int it) {}
|
||||
''');
|
||||
await findTopLevelDeclarations('My*');
|
||||
assertHasDeclaration(ElementKind.EXTENSION_TYPE, 'MyExtensionType');
|
||||
}
|
||||
|
||||
Future<void> test_invalidRegex() async {
|
||||
var result = await findTopLevelDeclarations('[A');
|
||||
expect(result, const TypeMatcher<RequestError>());
|
||||
|
|
|
@ -242,6 +242,23 @@ class C extends B {}
|
|||
_assertContainsClass(subtypes, 'C');
|
||||
}
|
||||
|
||||
Future<void> test_searchAllSubtypes_extensionType() async {
|
||||
await resolveTestCode('''
|
||||
class A {}
|
||||
extension type B(int it) implements A {}
|
||||
extension type C(int it) implements A {}
|
||||
''');
|
||||
|
||||
var element = findElement.class_('A');
|
||||
|
||||
var subtypes = <InterfaceElement>{};
|
||||
await searchEngine.appendAllSubtypes(
|
||||
element, subtypes, OperationPerformanceImpl("<root>"));
|
||||
expect(subtypes, hasLength(2));
|
||||
_assertContainsClass(subtypes, 'B');
|
||||
_assertContainsClass(subtypes, 'C');
|
||||
}
|
||||
|
||||
Future<void> test_searchAllSubtypes_mixin() async {
|
||||
await resolveTestCode('''
|
||||
class T {}
|
||||
|
@ -444,6 +461,29 @@ enum E {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> test_searchReferences_extensionType() async {
|
||||
final code = '''
|
||||
extension type A(int it) {}
|
||||
|
||||
void f(A a) {}
|
||||
''';
|
||||
await resolveTestCode(code);
|
||||
|
||||
final element = findElement.extensionType('A');
|
||||
final matches = await searchEngine.searchReferences(element);
|
||||
expect(
|
||||
matches,
|
||||
unorderedEquals([
|
||||
predicate((SearchMatch m) {
|
||||
return m.kind == MatchKind.REFERENCE &&
|
||||
identical(m.element, findElement.parameter('a')) &&
|
||||
m.sourceRange.offset == code.indexOf('A a) {}') &&
|
||||
m.sourceRange.length == 'A'.length;
|
||||
}),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void>
|
||||
test_searchReferences_parameter_ofConstructor_super_named() async {
|
||||
var code = '''
|
||||
|
|
|
@ -38,6 +38,9 @@ DefinedNames computeDefinedNames(CompilationUnit unit) {
|
|||
}
|
||||
member.members.forEach(appendClassMemberName);
|
||||
}
|
||||
if (member is ExtensionTypeDeclaration) {
|
||||
member.members.forEach(appendClassMemberName);
|
||||
}
|
||||
if (member is MixinDeclaration) {
|
||||
member.members.forEach(appendClassMemberName);
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ import 'package:analyzer/src/utilities/uri_cache.dart';
|
|||
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
|
||||
class AnalysisDriver implements AnalysisDriverGeneric {
|
||||
/// The version of data format, should be incremented on every format change.
|
||||
static const int DATA_VERSION = 299;
|
||||
static const int DATA_VERSION = 300;
|
||||
|
||||
/// The number of exception contexts allowed to write. Once this field is
|
||||
/// zero, we stop writing any new exception contexts in this process.
|
||||
|
|
|
@ -96,6 +96,20 @@ class _LocalNameScope {
|
|||
return scope;
|
||||
}
|
||||
|
||||
factory _LocalNameScope.forExtensionType(
|
||||
_LocalNameScope enclosing, ExtensionTypeDeclaration node) {
|
||||
final scope = _LocalNameScope(enclosing);
|
||||
scope.addTypeParameters(node.typeParameters);
|
||||
for (final member in node.members) {
|
||||
if (member is FieldDeclaration) {
|
||||
scope.addVariableNames(member.fields);
|
||||
} else if (member is MethodDeclaration) {
|
||||
scope.add(member.name);
|
||||
}
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
factory _LocalNameScope.forFunction(
|
||||
_LocalNameScope enclosing, FunctionDeclaration node) {
|
||||
_LocalNameScope scope = _LocalNameScope(enclosing);
|
||||
|
@ -231,6 +245,17 @@ class _ReferencedNamesComputer extends GeneralizingAstVisitor<void> {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
|
||||
final outerScope = localScope;
|
||||
try {
|
||||
localScope = _LocalNameScope.forExtensionType(localScope, node);
|
||||
super.visitExtensionTypeDeclaration(node);
|
||||
} finally {
|
||||
localScope = outerScope;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitFunctionDeclaration(FunctionDeclaration node) {
|
||||
_LocalNameScope outerScope = localScope;
|
||||
|
|
|
@ -306,6 +306,7 @@ class Search {
|
|||
if (unitResult is UnitElementResult) {
|
||||
unitResult.element.classes.forEach(addElements);
|
||||
unitResult.element.enums.forEach(addElements);
|
||||
unitResult.element.extensionTypes.forEach(addElements);
|
||||
unitResult.element.mixins.forEach(addElements);
|
||||
}
|
||||
}
|
||||
|
@ -462,6 +463,7 @@ class Search {
|
|||
unitElement.classes.forEach(addElement);
|
||||
unitElement.enums.forEach(addElement);
|
||||
unitElement.extensions.forEach(addElement);
|
||||
unitElement.extensionTypes.forEach(addElement);
|
||||
unitElement.functions.forEach(addElement);
|
||||
unitElement.mixins.forEach(addElement);
|
||||
unitElement.topLevelVariables.forEach(addElement);
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
// 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/ast/ast.dart';
|
||||
import 'package:analyzer/dart/analysis/features.dart';
|
||||
import 'package:analyzer/dart/analysis/utilities.dart';
|
||||
import 'package:analyzer/src/dart/analysis/defined_names.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../../../generated/parser_test_base.dart';
|
||||
import '../../../util/feature_sets.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
|
@ -16,7 +17,7 @@ main() {
|
|||
}
|
||||
|
||||
@reflectiveTest
|
||||
class DefinedNamesTest extends ParserTestCase {
|
||||
class DefinedNamesTest {
|
||||
test_classMemberNames_class() {
|
||||
DefinedNames names = _computeDefinedNames('''
|
||||
class A {
|
||||
|
@ -36,12 +37,29 @@ class B {
|
|||
unorderedEquals(['a', 'b', 'd', 'e', 'f', 'g']));
|
||||
}
|
||||
|
||||
test_classMemberNames_extensionType() {
|
||||
DefinedNames names = _computeDefinedNames('''
|
||||
extension type A.named(int it) {
|
||||
int a, b;
|
||||
A();
|
||||
A.other();
|
||||
void d() {}
|
||||
int get e => 0;
|
||||
set f(int _) {}
|
||||
}
|
||||
extension type B(int it) {
|
||||
void g() {}
|
||||
}
|
||||
''');
|
||||
expect(names.topLevelNames, unorderedEquals(['A', 'B']));
|
||||
expect(names.classMemberNames,
|
||||
unorderedEquals(['a', 'b', 'd', 'e', 'f', 'g']));
|
||||
}
|
||||
|
||||
test_classMemberNames_mixin() {
|
||||
DefinedNames names = _computeDefinedNames('''
|
||||
mixin A {
|
||||
int a, b;
|
||||
A();
|
||||
A.c();
|
||||
d() {}
|
||||
get e => null;
|
||||
set f(_) {}
|
||||
|
@ -59,7 +77,7 @@ mixin B {
|
|||
DefinedNames names = _computeDefinedNames('''
|
||||
class A {}
|
||||
class B = Object with A;
|
||||
typedef C {}
|
||||
typedef C();
|
||||
D() {}
|
||||
get E => null;
|
||||
set F(_) {}
|
||||
|
@ -71,8 +89,14 @@ mixin M {}
|
|||
expect(names.classMemberNames, isEmpty);
|
||||
}
|
||||
|
||||
DefinedNames _computeDefinedNames(String code) {
|
||||
CompilationUnit unit = parseCompilationUnit2(code);
|
||||
return computeDefinedNames(unit);
|
||||
DefinedNames _computeDefinedNames(
|
||||
String code, {
|
||||
FeatureSet? featureSet,
|
||||
}) {
|
||||
final parseResult = parseString(
|
||||
content: code,
|
||||
featureSet: featureSet ?? FeatureSets.latestWithExperiments,
|
||||
);
|
||||
return computeDefinedNames(parseResult.unit);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,15 @@
|
|||
// 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/features.dart';
|
||||
import 'package:analyzer/dart/analysis/utilities.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/dart/analysis/referenced_names.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../../../generated/parser_test_base.dart';
|
||||
import '../../../util/feature_sets.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
|
@ -17,7 +20,7 @@ main() {
|
|||
}
|
||||
|
||||
@reflectiveTest
|
||||
class ComputeReferencedNamesTest extends ParserTestCase {
|
||||
class ComputeReferencedNamesTest {
|
||||
test_class_constructor() {
|
||||
Set<String> names = _computeReferencedNames('''
|
||||
class U {
|
||||
|
@ -174,6 +177,17 @@ class U<T> {
|
|||
expect(names, unorderedEquals(['A']));
|
||||
}
|
||||
|
||||
test_extensionType_typeParameters() {
|
||||
Set<String> names = _computeReferencedNames('''
|
||||
extension type Z<T>(int it) {
|
||||
A m(B b, T t, Z z) {
|
||||
C c = 0;
|
||||
}
|
||||
}
|
||||
''');
|
||||
expect(names, unorderedEquals(['int', 'A', 'B', 'C']));
|
||||
}
|
||||
|
||||
test_instantiatedNames_importPrefix() {
|
||||
Set<String> names = _computeReferencedNames('''
|
||||
import 'a.dart' as p1;
|
||||
|
@ -405,8 +419,15 @@ main() {
|
|||
expect(names, unorderedEquals(['A', 'B', 'C']));
|
||||
}
|
||||
|
||||
Set<String> _computeReferencedNames(String code) {
|
||||
CompilationUnit unit = parseCompilationUnit2(code);
|
||||
Set<String> _computeReferencedNames(
|
||||
String code, {
|
||||
FeatureSet? featureSet,
|
||||
}) {
|
||||
final parseResult = parseString(
|
||||
content: code,
|
||||
featureSet: featureSet ?? FeatureSets.latestWithExperiments,
|
||||
);
|
||||
final unit = parseResult.unit;
|
||||
return computeReferencedNames(unit);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue