From bbc3dcbe302538d988d4ee9a71cdbc7943a1cedb Mon Sep 17 00:00:00 2001
From: Konstantin Shcheglov
Date: Wed, 30 Aug 2023 21:18:25 +0000
Subject: [PATCH] Extension type. Tests for type hierarchy.
Change-Id: Ic0e31bb13efd68a3413429d1ff9110bb9b268e2e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/323432
Reviewed-by: Brian Wilkerson
Commit-Queue: Konstantin Shcheglov
---
pkg/analysis_server/doc/api.html | 2 +-
.../lib/plugin/protocol/protocol_dart.dart | 3 +
.../lib/src/search/type_hierarchy.dart | 6 +
.../support/protocol_matchers.dart | 2 +
.../test/plugin/protocol_dart_test.dart | 8 +
.../test/protocol_server_test.dart | 1 -
.../test/search/type_hierarchy_test.dart | 171 ++++++++++++++++++
.../generated/java/types/ElementKind.java | 2 +
.../lib/src/protocol/protocol_common.dart | 6 +
pkg/analyzer_plugin/doc/api.html | 2 +-
.../lib/protocol/protocol_common.dart | 6 +
.../support/protocol_matchers.dart | 2 +
.../tool/spec/common_types_spec.html | 1 +
13 files changed, 209 insertions(+), 3 deletions(-)
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index d86890ce38a..3baa89791eb 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -4307,7 +4307,7 @@ a:focus, a:hover {
An enumeration of the kinds of elements.
- - CLASS
- CLASS_TYPE_ALIAS
- COMPILATION_UNIT
- CONSTRUCTOR
- CONSTRUCTOR_INVOCATION
- ENUM
- ENUM_CONSTANT
- EXTENSION
- FIELD
- FILE
- FUNCTION
- FUNCTION_INVOCATION
- FUNCTION_TYPE_ALIAS
- GETTER
- LABEL
- LIBRARY
- LOCAL_VARIABLE
- METHOD
- MIXIN
- PARAMETER
- PREFIX
- SETTER
- TOP_LEVEL_VARIABLE
- TYPE_ALIAS
- TYPE_PARAMETER
- UNIT_TEST_GROUP
- UNIT_TEST_TEST
- UNKNOWN
ExecutableFile: object
+ - CLASS
- CLASS_TYPE_ALIAS
- COMPILATION_UNIT
- CONSTRUCTOR
- CONSTRUCTOR_INVOCATION
- ENUM
- ENUM_CONSTANT
- EXTENSION
- EXTENSION_TYPE
- FIELD
- FILE
- FUNCTION
- FUNCTION_INVOCATION
- FUNCTION_TYPE_ALIAS
- GETTER
- LABEL
- LIBRARY
- LOCAL_VARIABLE
- METHOD
- MIXIN
- PARAMETER
- PREFIX
- SETTER
- TOP_LEVEL_VARIABLE
- TYPE_ALIAS
- TYPE_PARAMETER
- UNIT_TEST_GROUP
- UNIT_TEST_TEST
- UNKNOWN
ExecutableFile: object
A description of an executable file.
diff --git a/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart b/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart
index 1f2985c05f0..0babcc5266d 100644
--- a/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart
+++ b/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart
@@ -64,6 +64,9 @@ ElementKind convertElementKind(engine.ElementKind kind) {
if (kind == engine.ElementKind.EXTENSION) {
return ElementKind.EXTENSION;
}
+ if (kind == engine.ElementKind.EXTENSION_TYPE) {
+ return ElementKind.EXTENSION_TYPE;
+ }
if (kind == engine.ElementKind.FIELD) {
return ElementKind.FIELD;
}
diff --git a/pkg/analysis_server/lib/src/search/type_hierarchy.dart b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
index 0a0c7838dd9..038347f270e 100644
--- a/pkg/analysis_server/lib/src/search/type_hierarchy.dart
+++ b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
@@ -184,6 +184,12 @@ class TypeHierarchyComputerHelper {
}
ExecutableElement? findMemberElement(InterfaceElement clazz) {
+ // Members of extension types don't override anything.
+ // They redeclare, and resolved statically.
+ if (pivotClass is ExtensionTypeElement || clazz is ExtensionTypeElement) {
+ return null;
+ }
+
var pivotName = this.pivotName;
if (pivotName == null) {
return null;
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index 1b8e87ade28..b69e0a721dd 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -442,6 +442,7 @@ final Matcher isElementDeclaration =
/// ENUM
/// ENUM_CONSTANT
/// EXTENSION
+/// EXTENSION_TYPE
/// FIELD
/// FILE
/// FUNCTION
@@ -472,6 +473,7 @@ final Matcher isElementKind = MatchesEnum('ElementKind', [
'ENUM',
'ENUM_CONSTANT',
'EXTENSION',
+ 'EXTENSION_TYPE',
'FIELD',
'FILE',
'FUNCTION',
diff --git a/pkg/analysis_server/test/plugin/protocol_dart_test.dart b/pkg/analysis_server/test/plugin/protocol_dart_test.dart
index 7ff9068b05a..93c3ee08fb7 100644
--- a/pkg/analysis_server/test/plugin/protocol_dart_test.dart
+++ b/pkg/analysis_server/test/plugin/protocol_dart_test.dart
@@ -524,6 +524,10 @@ class ElementKindTest {
ElementKind.COMPILATION_UNIT);
expect(convertElementKind(engine.ElementKind.CONSTRUCTOR),
ElementKind.CONSTRUCTOR);
+ expect(convertElementKind(engine.ElementKind.EXTENSION),
+ ElementKind.EXTENSION);
+ expect(convertElementKind(engine.ElementKind.EXTENSION_TYPE),
+ ElementKind.EXTENSION_TYPE);
expect(convertElementKind(engine.ElementKind.FIELD), ElementKind.FIELD);
expect(
convertElementKind(engine.ElementKind.FUNCTION), ElementKind.FUNCTION);
@@ -555,6 +559,10 @@ class ElementKindTest {
expect(ElementKind(ElementKind.COMPILATION_UNIT.name),
ElementKind.COMPILATION_UNIT);
expect(ElementKind(ElementKind.CONSTRUCTOR.name), ElementKind.CONSTRUCTOR);
+ expect(ElementKind(ElementKind.CONSTRUCTOR.name), ElementKind.CONSTRUCTOR);
+ expect(ElementKind(ElementKind.EXTENSION.name), ElementKind.EXTENSION);
+ expect(ElementKind(ElementKind.EXTENSION_TYPE.name),
+ ElementKind.EXTENSION_TYPE);
expect(ElementKind(ElementKind.FIELD.name), ElementKind.FIELD);
expect(ElementKind(ElementKind.FUNCTION.name), ElementKind.FUNCTION);
expect(ElementKind(ElementKind.FUNCTION_TYPE_ALIAS.name),
diff --git a/pkg/analysis_server/test/protocol_server_test.dart b/pkg/analysis_server/test/protocol_server_test.dart
index 3852eec5078..936473c19e9 100644
--- a/pkg/analysis_server/test/protocol_server_test.dart
+++ b/pkg/analysis_server/test/protocol_server_test.dart
@@ -246,7 +246,6 @@ class EnumTest {
engine.ElementKind.EXPORT: ElementKind.UNKNOWN,
engine.ElementKind.GENERIC_FUNCTION_TYPE: ElementKind.FUNCTION_TYPE_ALIAS,
engine.ElementKind.IMPORT: ElementKind.UNKNOWN,
- engine.ElementKind.EXTENSION_TYPE: ElementKind.UNKNOWN,
engine.ElementKind.LIBRARY_AUGMENTATION: ElementKind.UNKNOWN,
engine.ElementKind.NAME: ElementKind.UNKNOWN,
engine.ElementKind.NEVER: ElementKind.UNKNOWN,
diff --git a/pkg/analysis_server/test/search/type_hierarchy_test.dart b/pkg/analysis_server/test/search/type_hierarchy_test.dart
index ab77d7e7ef0..0b0c729b723 100644
--- a/pkg/analysis_server/test/search/type_hierarchy_test.dart
+++ b/pkg/analysis_server/test/search/type_hierarchy_test.dart
@@ -1354,6 +1354,177 @@ enum E with M {
]);
}
+ Future test_extensionType_implements_class() async {
+ addTestFile('''
+class A {}
+class B extends A {}
+extension type E(B it) implements A {}
+''');
+ var items = await _getTypeHierarchy('E(B it)');
+ expect(_toJson(items), [
+ {
+ 'classElement': {
+ 'kind': 'EXTENSION_TYPE',
+ 'name': 'E',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'interfaces': [1],
+ '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': []
+ }
+ ]);
+ }
+
+ Future test_extensionType_implements_class2() async {
+ addTestFile('''
+class A {}
+extension type E(A it) implements A {}
+''');
+ var items = await _getTypeHierarchy('A {}');
+ expect(_toJson(items), [
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'A',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 1,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': [2]
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'Object',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'EXTENSION_TYPE',
+ 'name': 'E',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 0,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ }
+ ]);
+ }
+
+ Future test_extensionType_implements_extensionType() async {
+ addTestFile('''
+class A {}
+extension type E1(A it) {}
+extension type E2(A it) implements E1 {}
+''');
+ var items = await _getTypeHierarchy('E2(A it)');
+ expect(_toJson(items), [
+ {
+ 'classElement': {
+ 'kind': 'EXTENSION_TYPE',
+ 'name': 'E2',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'interfaces': [1],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'EXTENSION_TYPE',
+ 'name': 'E1',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'interfaces': [2],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'Object',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ }
+ ]);
+ }
+
+ Future test_extensionType_member_method() async {
+ addTestFile('''
+class A {
+ void test() {} // in A
+}
+extension type E(A it) implements A {
+ void test() {} // in E
+}
+''');
+ var items = await _getTypeHierarchy('test() {} // in E');
+ var itemE = items[0];
+ var itemA = items[itemE.interfaces.single];
+ expect(itemA.classElement.name, 'A');
+ expect(itemE.classElement.name, 'E');
+ expect(itemA.memberElement, isNull);
+ expect(itemE.memberElement, isNull);
+ }
+
+ Future test_extensionType_member_method2() async {
+ addTestFile('''
+class A {
+ void test() {} // in A
+}
+extension type E(A it) implements A {
+ void test() {} // in E
+}
+''');
+ var items = await _getTypeHierarchy('test() {} // in A');
+ var itemA = items[0];
+ var itemE = items[itemA.subclasses.single];
+ expect(itemA.classElement.name, 'A');
+ expect(itemE.classElement.name, 'E');
+ expect(
+ itemA.memberElement!.location!.offset, findOffset('test() {} // in A'));
+ expect(itemE.memberElement, isNull);
+ }
+
void _assertMember(TypeHierarchyItem item, String search) {
expect(item.memberElement!.location!.offset, findOffset(search));
}
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/ElementKind.java b/pkg/analysis_server/tool/spec/generated/java/types/ElementKind.java
index 81be704cbbe..e9cd8cc3b87 100644
--- a/pkg/analysis_server/tool/spec/generated/java/types/ElementKind.java
+++ b/pkg/analysis_server/tool/spec/generated/java/types/ElementKind.java
@@ -31,6 +31,8 @@ public class ElementKind {
public static final String EXTENSION = "EXTENSION";
+ public static final String EXTENSION_TYPE = "EXTENSION_TYPE";
+
public static final String FIELD = "FIELD";
public static final String FILE = "FILE";
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
index 815c4cabb6c..256557ede47 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
@@ -1409,6 +1409,7 @@ class Element implements HasToJson {
/// ENUM
/// ENUM_CONSTANT
/// EXTENSION
+/// EXTENSION_TYPE
/// FIELD
/// FILE
/// FUNCTION
@@ -1450,6 +1451,8 @@ class ElementKind implements Enum {
static const ElementKind EXTENSION = ElementKind._('EXTENSION');
+ static const ElementKind EXTENSION_TYPE = ElementKind._('EXTENSION_TYPE');
+
static const ElementKind FIELD = ElementKind._('FIELD');
static const ElementKind FILE = ElementKind._('FILE');
@@ -1503,6 +1506,7 @@ class ElementKind implements Enum {
ENUM,
ENUM_CONSTANT,
EXTENSION,
+ EXTENSION_TYPE,
FIELD,
FILE,
FUNCTION,
@@ -1548,6 +1552,8 @@ class ElementKind implements Enum {
return ENUM_CONSTANT;
case 'EXTENSION':
return EXTENSION;
+ case 'EXTENSION_TYPE':
+ return EXTENSION_TYPE;
case 'FIELD':
return FIELD;
case 'FILE':
diff --git a/pkg/analyzer_plugin/doc/api.html b/pkg/analyzer_plugin/doc/api.html
index beb55a5606a..32d6b9fda05 100644
--- a/pkg/analyzer_plugin/doc/api.html
+++ b/pkg/analyzer_plugin/doc/api.html
@@ -1355,7 +1355,7 @@ a:focus, a:hover {
An enumeration of the kinds of elements.
- - CLASS
- CLASS_TYPE_ALIAS
- COMPILATION_UNIT
- CONSTRUCTOR
- CONSTRUCTOR_INVOCATION
- ENUM
- ENUM_CONSTANT
- EXTENSION
- FIELD
- FILE
- FUNCTION
- FUNCTION_INVOCATION
- FUNCTION_TYPE_ALIAS
- GETTER
- LABEL
- LIBRARY
- LOCAL_VARIABLE
- METHOD
- MIXIN
- PARAMETER
- PREFIX
- SETTER
- TOP_LEVEL_VARIABLE
- TYPE_ALIAS
- TYPE_PARAMETER
- UNIT_TEST_GROUP
- UNIT_TEST_TEST
- UNKNOWN
FilePath: String
+ - CLASS
- CLASS_TYPE_ALIAS
- COMPILATION_UNIT
- CONSTRUCTOR
- CONSTRUCTOR_INVOCATION
- ENUM
- ENUM_CONSTANT
- EXTENSION
- EXTENSION_TYPE
- FIELD
- FILE
- FUNCTION
- FUNCTION_INVOCATION
- FUNCTION_TYPE_ALIAS
- GETTER
- LABEL
- LIBRARY
- LOCAL_VARIABLE
- METHOD
- MIXIN
- PARAMETER
- PREFIX
- SETTER
- TOP_LEVEL_VARIABLE
- TYPE_ALIAS
- TYPE_PARAMETER
- UNIT_TEST_GROUP
- UNIT_TEST_TEST
- UNKNOWN
FilePath: String
The absolute, normalized path of a file.
diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
index 6ea6d24a8a1..0e2ac2d99b6 100644
--- a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
+++ b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
@@ -1409,6 +1409,7 @@ class Element implements HasToJson {
/// ENUM
/// ENUM_CONSTANT
/// EXTENSION
+/// EXTENSION_TYPE
/// FIELD
/// FILE
/// FUNCTION
@@ -1450,6 +1451,8 @@ class ElementKind implements Enum {
static const ElementKind EXTENSION = ElementKind._('EXTENSION');
+ static const ElementKind EXTENSION_TYPE = ElementKind._('EXTENSION_TYPE');
+
static const ElementKind FIELD = ElementKind._('FIELD');
static const ElementKind FILE = ElementKind._('FILE');
@@ -1503,6 +1506,7 @@ class ElementKind implements Enum {
ENUM,
ENUM_CONSTANT,
EXTENSION,
+ EXTENSION_TYPE,
FIELD,
FILE,
FUNCTION,
@@ -1548,6 +1552,8 @@ class ElementKind implements Enum {
return ENUM_CONSTANT;
case 'EXTENSION':
return EXTENSION;
+ case 'EXTENSION_TYPE':
+ return EXTENSION_TYPE;
case 'FIELD':
return FIELD;
case 'FILE':
diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
index f2ea620ef41..8cab210b597 100644
--- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
+++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
@@ -254,6 +254,7 @@ final Matcher isElement = LazyMatcher(() => MatchesJsonObject('Element', {
/// ENUM
/// ENUM_CONSTANT
/// EXTENSION
+/// EXTENSION_TYPE
/// FIELD
/// FILE
/// FUNCTION
@@ -284,6 +285,7 @@ final Matcher isElementKind = MatchesEnum('ElementKind', [
'ENUM',
'ENUM_CONSTANT',
'EXTENSION',
+ 'EXTENSION_TYPE',
'FIELD',
'FILE',
'FUNCTION',
diff --git a/pkg/analyzer_plugin/tool/spec/common_types_spec.html b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
index 516886a30c7..fa5b7f5b2b3 100644
--- a/pkg/analyzer_plugin/tool/spec/common_types_spec.html
+++ b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
@@ -601,6 +601,7 @@
ENUM
ENUM_CONSTANT
EXTENSION
+ EXTENSION_TYPE
FIELD
FILE
FUNCTION