Add a minimal implementation of ExtensionDeclaration
Change-Id: I5a556ea1819b38a3130e801a3cbc4bcbad98dcdd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103380
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index 7667168..2e5f1ae 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -500,6 +500,8 @@
R visitExtendsClause(ExtendsClause node);
+ R visitExtensionDeclaration(ExtensionDeclaration node);
+
R visitFieldDeclaration(FieldDeclaration node);
R visitFieldFormalParameter(FieldFormalParameter node);
diff --git a/pkg/analyzer/lib/dart/ast/ast_factory.dart b/pkg/analyzer/lib/dart/ast/ast_factory.dart
index 815ad03..bf624d8 100644
--- a/pkg/analyzer/lib/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/dart/ast/ast_factory.dart
@@ -339,6 +339,20 @@
/// Returns a newly created extends clause.
ExtendsClause extendsClause(Token extendsKeyword, TypeName superclass);
+ /// Return a newly created extention declaration. The list of [typeParameters]
+ /// can be `null` if there are no type parameters.
+ ExtensionDeclaration extensionDeclaration(
+ {Comment comment,
+ List<Annotation> metadata,
+ Token extensionKeyword,
+ @required SimpleIdentifier name,
+ TypeParameterList typeParameters,
+ Token onKeyword,
+ @required TypeAnnotation extendedType,
+ Token leftBracket,
+ List<ClassMember> members,
+ Token rightBracket});
+
/// Returns a newly created field declaration. Either or both of the [comment]
/// and [metadata] can be `null` if the declaration does not have the
/// corresponding attribute. The [staticKeyword] can be `null` if the field is
diff --git a/pkg/analyzer/lib/dart/ast/visitor.dart b/pkg/analyzer/lib/dart/ast/visitor.dart
index 6646631..68f4621 100644
--- a/pkg/analyzer/lib/dart/ast/visitor.dart
+++ b/pkg/analyzer/lib/dart/ast/visitor.dart
@@ -277,6 +277,10 @@
R visitExtendsClause(ExtendsClause node) => visitNode(node);
@override
+ R visitExtensionDeclaration(ExtensionDeclaration node) =>
+ visitNamedCompilationUnitMember(node);
+
+ @override
R visitFieldDeclaration(FieldDeclaration node) => visitClassMember(node);
@override
@@ -839,6 +843,12 @@
}
@override
+ R visitExtensionDeclaration(ExtensionDeclaration node) {
+ node.visitChildren(this);
+ return null;
+ }
+
+ @override
R visitFieldDeclaration(FieldDeclaration node) {
node.visitChildren(this);
return null;
@@ -1447,6 +1457,9 @@
R visitExtendsClause(ExtendsClause node) => null;
@override
+ R visitExtensionDeclaration(ExtensionDeclaration node) => null;
+
+ @override
R visitFieldDeclaration(FieldDeclaration node) => null;
@override
@@ -1822,6 +1835,9 @@
R visitExtendsClause(ExtendsClause node) => _throw(node);
@override
+ R visitExtensionDeclaration(ExtensionDeclaration node) => _throw(node);
+
+ @override
R visitFieldDeclaration(FieldDeclaration node) => _throw(node);
@override
@@ -2406,6 +2422,14 @@
}
@override
+ T visitExtensionDeclaration(ExtensionDeclaration node) {
+ stopwatch.start();
+ T result = _baseVisitor.visitExtensionDeclaration(node);
+ stopwatch.stop();
+ return result;
+ }
+
+ @override
T visitFieldDeclaration(FieldDeclaration node) {
stopwatch.start();
T result = _baseVisitor.visitFieldDeclaration(node);
@@ -3181,6 +3205,9 @@
R visitExtendsClause(ExtendsClause node) => visitNode(node);
@override
+ R visitExtensionDeclaration(ExtensionDeclaration node) => visitNode(node);
+
+ @override
R visitFieldDeclaration(FieldDeclaration node) => visitNode(node);
@override
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index e3064ce..8a28fb8 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -3821,6 +3821,109 @@
}
}
+/// The declaration of an extension of a type.
+///
+/// extension ::=
+/// 'extension' [SimpleIdentifier] [TypeParameterList]?
+/// 'on' [TypeAnnotation] '{' [ClassMember]* '}'
+///
+/// Clients may not extend, implement or mix-in this class.
+class ExtensionDeclarationImpl extends NamedCompilationUnitMemberImpl
+ implements ExtensionDeclaration {
+ @override
+ Token extensionKeyword;
+
+ /// The type parameters for the extension, or `null` if the extension
+ /// does not have any type parameters.
+ TypeParameterListImpl _typeParameters;
+
+ @override
+ Token onKeyword;
+
+ /// The type that is being extended.
+ TypeAnnotationImpl _extendedType;
+
+ @override
+ Token leftBracket;
+
+ /// The members being added to the extended class.
+ NodeList<ClassMember> _members;
+
+ @override
+ Token rightBracket;
+
+ ExtensionDeclarationImpl(
+ CommentImpl comment,
+ List<Annotation> metadata,
+ this.extensionKeyword,
+ SimpleIdentifierImpl name,
+ TypeParameterListImpl typeParameters,
+ this.onKeyword,
+ TypeAnnotationImpl extendedType,
+ this.leftBracket,
+ List<ClassMember> members,
+ this.rightBracket)
+ : super(comment, metadata, name) {
+ _typeParameters = _becomeParentOf(typeParameters);
+ _extendedType = _becomeParentOf(extendedType);
+ _members = new NodeListImpl<ClassMember>(this, members);
+ }
+
+ @override
+ Token get beginToken => extensionKeyword;
+
+ @override
+ Iterable<SyntacticEntity> get childEntities => new ChildEntities()
+ ..add(extensionKeyword)
+ ..add(name)
+ ..add(typeParameters)
+ ..add(onKeyword)
+ ..add(extendedType)
+ ..add(leftBracket)
+ ..addAll(members)
+ ..add(rightBracket);
+
+ @override
+ Element get declaredElement => name.staticElement;
+
+ @override
+ Element get element => name.staticElement;
+
+ @override
+ Token get endToken => rightBracket;
+
+ @override
+ TypeAnnotation get extendedType => _extendedType;
+
+ void set extendedType(TypeAnnotation extendedClass) {
+ _extendedType = _becomeParentOf(extendedClass as TypeAnnotationImpl);
+ }
+
+ @override
+ Token get firstTokenAfterCommentAndMetadata => name.beginToken;
+
+ @override
+ NodeList<ClassMember> get members => _members;
+
+ @override
+ TypeParameterList get typeParameters => _typeParameters;
+
+ void set typeParameters(TypeParameterList typeParameters) {
+ _typeParameters = _becomeParentOf(typeParameters as TypeParameterListImpl);
+ }
+
+ @override
+ E accept<E>(AstVisitor<E> visitor) => visitor.visitExtensionDeclaration(this);
+
+ @override
+ void visitChildren(AstVisitor visitor) {
+ name?.accept(visitor);
+ _typeParameters?.accept(visitor);
+ _extendedType?.accept(visitor);
+ _members.accept(visitor);
+ }
+}
+
/// The declaration of one or more fields of the same type.
///
/// fieldDeclaration ::=
diff --git a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
index 95b5867..ded10fd 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
@@ -370,6 +370,30 @@
new ExtendsClauseImpl(extendsKeyword, superclass);
@override
+ ExtensionDeclaration extensionDeclaration(
+ {Comment comment,
+ List<Annotation> metadata,
+ Token extensionKeyword,
+ @required SimpleIdentifier name,
+ TypeParameterList typeParameters,
+ Token onKeyword,
+ @required TypeAnnotation extendedType,
+ Token leftBracket,
+ List<ClassMember> members,
+ Token rightBracket}) =>
+ new ExtensionDeclarationImpl(
+ comment,
+ metadata,
+ extensionKeyword,
+ name,
+ typeParameters,
+ onKeyword,
+ extendedType,
+ leftBracket,
+ members,
+ rightBracket);
+
+ @override
FieldDeclaration fieldDeclaration(
Comment comment,
List<Annotation> metadata,
diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart
index 5ac34d1..1a26ff2 100644
--- a/pkg/analyzer/lib/src/dart/ast/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart
@@ -450,6 +450,20 @@
cloneToken(node.extendsKeyword), cloneNode(node.superclass));
@override
+ ExtensionDeclaration visitExtensionDeclaration(ExtensionDeclaration node) =>
+ astFactory.extensionDeclaration(
+ comment: cloneNode(node.documentationComment),
+ metadata: cloneNodeList(node.metadata),
+ extensionKeyword: cloneToken(node.extensionKeyword),
+ name: cloneNode(node.name),
+ typeParameters: cloneNode(node.typeParameters),
+ onKeyword: cloneToken(node.onKeyword),
+ extendedType: cloneNode(node.extendedType),
+ leftBracket: cloneToken(node.leftBracket),
+ members: cloneNodeList(node.members),
+ rightBracket: cloneToken(node.rightBracket));
+
+ @override
FieldDeclaration visitFieldDeclaration(FieldDeclaration node) =>
astFactory.fieldDeclaration2(
comment: cloneNode(node.documentationComment),
@@ -1577,6 +1591,22 @@
}
@override
+ bool visitExtensionDeclaration(ExtensionDeclaration node) {
+ ExtensionDeclaration other = _other as ExtensionDeclaration;
+ return isEqualNodes(
+ node.documentationComment, other.documentationComment) &&
+ _isEqualNodeLists(node.metadata, other.metadata) &&
+ isEqualTokens(node.extensionKeyword, other.extensionKeyword) &&
+ isEqualNodes(node.name, other.name) &&
+ isEqualNodes(node.typeParameters, other.typeParameters) &&
+ isEqualTokens(node.onKeyword, other.onKeyword) &&
+ isEqualNodes(node.extendedType, other.extendedType) &&
+ isEqualTokens(node.leftBracket, other.leftBracket) &&
+ _isEqualNodeLists(node.members, other.members) &&
+ isEqualTokens(node.rightBracket, other.rightBracket);
+ }
+
+ @override
bool visitFieldDeclaration(FieldDeclaration node) {
FieldDeclaration other = _other as FieldDeclaration;
return isEqualNodes(
@@ -2817,6 +2847,20 @@
_mapToken(node.extendsKeyword), _cloneNode(node.superclass));
@override
+ ExtensionDeclaration visitExtensionDeclaration(ExtensionDeclaration node) =>
+ astFactory.extensionDeclaration(
+ comment: _cloneNode(node.documentationComment),
+ metadata: _cloneNodeList(node.metadata),
+ extensionKeyword: _mapToken(node.extensionKeyword),
+ name: _cloneNode(node.name),
+ typeParameters: _cloneNode(node.typeParameters),
+ onKeyword: _mapToken(node.onKeyword),
+ extendedType: _cloneNode(node.extendedType),
+ leftBracket: _mapToken(node.leftBracket),
+ members: _cloneNodeList(node.members),
+ rightBracket: _mapToken(node.rightBracket));
+
+ @override
FieldDeclaration visitFieldDeclaration(FieldDeclaration node) =>
astFactory.fieldDeclaration2(
comment: _cloneNode(node.documentationComment),
@@ -4230,6 +4274,29 @@
return visitNode(node);
}
+ bool visitExtensionDeclaration(ExtensionDeclaration node) {
+ if (identical(node.documentationComment, _oldNode)) {
+ node.documentationComment = _newNode as Comment;
+ return true;
+ } else if (_replaceInList(node.metadata)) {
+ return true;
+ } else if (identical(node.name, _oldNode)) {
+ (node as ExtensionDeclarationImpl).name = _newNode as SimpleIdentifier;
+ return true;
+ } else if (identical(node.typeParameters, _oldNode)) {
+ (node as ExtensionDeclarationImpl).typeParameters =
+ _newNode as TypeParameterList;
+ return true;
+ } else if (identical(node.extendedType, _oldNode)) {
+ (node as ExtensionDeclarationImpl).extendedType =
+ _newNode as TypeAnnotation;
+ return true;
+ } else if (_replaceInList(node.members)) {
+ return true;
+ }
+ return visitNode(node);
+ }
+
@override
bool visitFieldDeclaration(FieldDeclaration node) {
if (identical(node.fields, _oldNode)) {
@@ -5585,6 +5652,25 @@
}
@override
+ bool visitExtensionDeclaration(ExtensionDeclaration node) {
+ ExtensionDeclaration toNode = this._toNode as ExtensionDeclaration;
+ if (_and(
+ _isEqualNodes(node.documentationComment, toNode.documentationComment),
+ _isEqualNodeLists(node.metadata, toNode.metadata),
+ _isEqualTokens(node.extensionKeyword, toNode.extensionKeyword),
+ _isEqualNodes(node.name, toNode.name),
+ _isEqualNodes(node.typeParameters, toNode.typeParameters),
+ _isEqualTokens(node.onKeyword, toNode.onKeyword),
+ _isEqualNodes(node.extendedType, toNode.extendedType),
+ _isEqualTokens(node.leftBracket, toNode.leftBracket),
+ _isEqualNodeLists(node.members, toNode.members),
+ _isEqualTokens(node.rightBracket, toNode.rightBracket))) {
+ return true;
+ }
+ return false;
+ }
+
+ @override
bool visitFieldDeclaration(FieldDeclaration node) {
FieldDeclaration toNode = this._toNode as FieldDeclaration;
return _and(
@@ -7155,6 +7241,21 @@
}
@override
+ void visitExtensionDeclaration(ExtensionDeclaration node) {
+ _visitNodeListWithSeparatorAndSuffix(node.metadata, ' ', ' ');
+ _visitTokenWithSuffix(node.extensionKeyword, ' ');
+ _visitNode(node.name);
+ _visitNode(node.typeParameters);
+ _writer.print(' ');
+ _visitToken(node.onKeyword);
+ _writer.print(' ');
+ _visitNodeWithSuffix(node.extendedType, ' ');
+ _visitToken(node.leftBracket);
+ _visitNodeListWithSeparator(node.members, ' ');
+ _visitToken(node.rightBracket);
+ }
+
+ @override
void visitFieldDeclaration(FieldDeclaration node) {
_visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
_visitTokenWithSuffix(node.staticKeyword, " ");
@@ -7927,6 +8028,15 @@
}
/**
+ * Safely visit the given [token].
+ */
+ void _visitToken(Token token) {
+ if (token != null) {
+ _writer.print(token.lexeme);
+ }
+ }
+
+ /**
* Safely visit the given [token], printing the [suffix] after the token if it
* is non-`null`.
*/
@@ -8076,6 +8186,16 @@
}
/**
+ * Safely visit the given [token].
+ */
+ @protected
+ void safelyVisitToken(Token token) {
+ if (token != null) {
+ sink.write(token.lexeme);
+ }
+ }
+
+ /**
* Safely visit the given [token], printing the [suffix] after the token if it
* is non-`null`.
*/
@@ -8420,6 +8540,21 @@
}
@override
+ void visitExtensionDeclaration(ExtensionDeclaration node) {
+ safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, ' ', ' ');
+ safelyVisitTokenWithSuffix(node.extensionKeyword, ' ');
+ safelyVisitNode(node.name);
+ safelyVisitNode(node.typeParameters);
+ sink.write(' ');
+ safelyVisitToken(node.onKeyword);
+ sink.write(' ');
+ safelyVisitNodeWithSuffix(node.extendedType, ' ');
+ safelyVisitToken(node.leftBracket);
+ safelyVisitNodeListWithSeparator(node.members, ' ');
+ safelyVisitToken(node.rightBracket);
+ }
+
+ @override
void visitFieldDeclaration(FieldDeclaration node) {
safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
safelyVisitTokenWithSuffix(node.staticKeyword, " ");
diff --git a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
index ab12e27..469e29c 100644
--- a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
+++ b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
@@ -8,6 +8,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/generated/testing/token_factory.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:meta/meta.dart';
/**
* The class `AstTestFactory` defines utility methods that can be used to create AST nodes. The
@@ -469,6 +470,24 @@
static ExtendsClause extendsClause(TypeName type) => astFactory.extendsClause(
TokenFactory.tokenFromKeyword(Keyword.EXTENDS), type);
+ static ExtensionDeclaration extensionDeclaration(
+ {@required String name,
+ TypeParameterList typeParameters,
+ @required TypeAnnotation extendedType,
+ List<ClassMember> members}) =>
+ astFactory.extensionDeclaration(
+ comment: null,
+ metadata: null,
+ extensionKeyword: TokenFactory.tokenFromKeyword(Keyword.EXTENSION),
+ name: identifier3(name),
+ typeParameters: typeParameters,
+ onKeyword: TokenFactory.tokenFromKeyword(Keyword.ON),
+ extendedType: extendedType,
+ leftBracket: TokenFactory.tokenFromType(TokenType.OPEN_CURLY_BRACKET),
+ members: members,
+ rightBracket:
+ TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET));
+
static FieldDeclaration fieldDeclaration(bool isStatic, Keyword keyword,
TypeAnnotation type, List<VariableDeclaration> variables) =>
astFactory.fieldDeclaration2(
diff --git a/pkg/analyzer/test/src/dart/ast/utilities_test.dart b/pkg/analyzer/test/src/dart/ast/utilities_test.dart
index 5771180..bcdc6f3 100644
--- a/pkg/analyzer/test/src/dart/ast/utilities_test.dart
+++ b/pkg/analyzer/test/src/dart/ast/utilities_test.dart
@@ -1713,6 +1713,48 @@
AstTestFactory.extendsClause(AstTestFactory.typeName4("C")));
}
+ void test_visitExtensionDeclaration_empty() {
+ _assertSource(
+ 'extension E on C {}',
+ AstTestFactory.extensionDeclaration(
+ name: 'E', extendedType: AstTestFactory.typeName4('C')));
+ }
+
+ void test_visitExtensionDeclaration_multipleMember() {
+ _assertSource(
+ 'extension E on C {var a; var b;}',
+ AstTestFactory.extensionDeclaration(
+ name: 'E',
+ extendedType: AstTestFactory.typeName4('C'),
+ members: [
+ AstTestFactory.fieldDeclaration2(false, Keyword.VAR,
+ [AstTestFactory.variableDeclaration('a')]),
+ AstTestFactory.fieldDeclaration2(
+ false, Keyword.VAR, [AstTestFactory.variableDeclaration('b')])
+ ]));
+ }
+
+ void test_visitExtensionDeclaration_parameters() {
+ _assertSource(
+ 'extension E<T> on C {}',
+ AstTestFactory.extensionDeclaration(
+ name: 'E',
+ typeParameters: AstTestFactory.typeParameterList(['T']),
+ extendedType: AstTestFactory.typeName4('C')));
+ }
+
+ void test_visitExtensionDeclaration_singleMember() {
+ _assertSource(
+ 'extension E on C {var a;}',
+ AstTestFactory.extensionDeclaration(
+ name: 'E',
+ extendedType: AstTestFactory.typeName4('C'),
+ members: [
+ AstTestFactory.fieldDeclaration2(
+ false, Keyword.VAR, [AstTestFactory.variableDeclaration('a')])
+ ]));
+ }
+
void test_visitFieldDeclaration_instance() {
_assertSource(
"var a;",
@@ -4427,6 +4469,48 @@
AstTestFactory.extendsClause(AstTestFactory.typeName4("C")));
}
+ void test_visitExtensionDeclaration_empty() {
+ _assertSource(
+ 'extension E on C {}',
+ AstTestFactory.extensionDeclaration(
+ name: 'E', extendedType: AstTestFactory.typeName4('C')));
+ }
+
+ void test_visitExtensionDeclaration_multipleMember() {
+ _assertSource(
+ 'extension E on C {var a; var b;}',
+ AstTestFactory.extensionDeclaration(
+ name: 'E',
+ extendedType: AstTestFactory.typeName4('C'),
+ members: [
+ AstTestFactory.fieldDeclaration2(false, Keyword.VAR,
+ [AstTestFactory.variableDeclaration('a')]),
+ AstTestFactory.fieldDeclaration2(
+ false, Keyword.VAR, [AstTestFactory.variableDeclaration('b')])
+ ]));
+ }
+
+ void test_visitExtensionDeclaration_parameters() {
+ _assertSource(
+ 'extension E<T> on C {}',
+ AstTestFactory.extensionDeclaration(
+ name: 'E',
+ typeParameters: AstTestFactory.typeParameterList(['T']),
+ extendedType: AstTestFactory.typeName4('C')));
+ }
+
+ void test_visitExtensionDeclaration_singleMember() {
+ _assertSource(
+ 'extension E on C {var a;}',
+ AstTestFactory.extensionDeclaration(
+ name: 'E',
+ extendedType: AstTestFactory.typeName4('C'),
+ members: [
+ AstTestFactory.fieldDeclaration2(
+ false, Keyword.VAR, [AstTestFactory.variableDeclaration('a')])
+ ]));
+ }
+
void test_visitFieldDeclaration_instance() {
_assertSource(
"var a;",
diff --git a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
index 33fff86..9e1ecef 100644
--- a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
@@ -6673,6 +6673,9 @@
@override
visitForPartsWithExpression(ForPartsWithExpression node) =>
_unreachable(node);
+
+ @override
+ visitExtensionDeclaration(ExtensionDeclaration node) => _unreachable(node);
}
// TODO(jacobr): we would like to do something like the following