From 7a968ae283ecd994223d3c327ffabbcf8b6227db Mon Sep 17 00:00:00 2001 From: Brian Wilkerson Date: Thu, 13 Dec 2018 17:11:02 +0000 Subject: [PATCH] Add a question mark to type annotations in the AST Change-Id: Icb2e6e4a52d91d3ecf3c4e8a44a2d8f2a3d7a204 Reviewed-on: https://dart-review.googlesource.com/c/87167 Reviewed-by: Dan Rubel Commit-Queue: Brian Wilkerson --- pkg/analyzer/lib/dart/ast/ast.dart | 27 ++++++++++++- pkg/analyzer/lib/dart/ast/ast_factory.dart | 9 +++-- pkg/analyzer/lib/src/dart/ast/ast.dart | 36 ++++++++--------- .../lib/src/dart/ast/ast_factory.dart | 11 ++++-- pkg/analyzer/lib/src/dart/ast/utilities.dart | 39 ++++++++++++++----- .../generated/testing/ast_test_factory.dart | 14 +++++-- .../test/src/dart/ast/utilities_test.dart | 35 +++++++++++++++++ 7 files changed, 132 insertions(+), 39 deletions(-) diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart index d0c6320e7b97..2378850a26f2 100644 --- a/pkg/analyzer/lib/dart/ast/ast.dart +++ b/pkg/analyzer/lib/dart/ast/ast.dart @@ -3779,7 +3779,8 @@ abstract class FunctionTypedFormalParameter extends NormalFormalParameter { * An anonymous function type. * * functionType ::= - * [TypeAnnotation]? 'Function' [TypeParameterList]? [FormalParameterList] + * [TypeAnnotation]? 'Function' [TypeParameterList]? + * [FormalParameterList] '?'? * * where the FormalParameterList is being used to represent the following * grammar, despite the fact that FormalParameterList can represent a much @@ -3828,6 +3829,18 @@ abstract class GenericFunctionType extends TypeAnnotation { */ void set parameters(FormalParameterList parameters); + /** + * The question mark indicating that the type is nullable, or `null` if there + * is no question mark. + */ + Token get question; + + /** + * Set the question mark indicating that the type is nullable to the given + * [token]. + */ + void set question(Token token); + /** * Return the return type of the function type being defined, or `null` if * no return type was given. @@ -5295,6 +5308,18 @@ abstract class NamedType extends TypeAnnotation { */ void set name(Identifier identifier); + /** + * The question mark indicating that the type is nullable, or `null` if there + * is no question mark. + */ + Token get question; + + /** + * Set the question mark indicating that the type is nullable to the given + * [token]. + */ + void set question(Token token); + /** * Set the type being named to the given [type]. */ diff --git a/pkg/analyzer/lib/dart/ast/ast_factory.dart b/pkg/analyzer/lib/dart/ast/ast_factory.dart index 4d402b0d5398..1a4933334b7e 100644 --- a/pkg/analyzer/lib/dart/ast/ast_factory.dart +++ b/pkg/analyzer/lib/dart/ast/ast_factory.dart @@ -624,7 +624,8 @@ abstract class AstFactory { TypeAnnotation returnType, Token functionKeyword, TypeParameterList typeParameters, - FormalParameterList parameters); + FormalParameterList parameters, + {Token question}); /** * Returns a newly created generic type alias. Either or both of the @@ -1084,9 +1085,11 @@ abstract class AstFactory { /** * Returns a newly created type name. The [typeArguments] can be `null` if - * there are no type arguments. + * there are no type arguments. The [question] can be `null` if there is no + * question mark. */ - TypeName typeName(Identifier name, TypeArgumentList typeArguments); + TypeName typeName(Identifier name, TypeArgumentList typeArguments, + {Token question}); /** * Returns a newly created type parameter. Either or both of the [comment] diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart index b328a9ab4a90..e188861b41c8 100644 --- a/pkg/analyzer/lib/src/dart/ast/ast.dart +++ b/pkg/analyzer/lib/src/dart/ast/ast.dart @@ -5720,35 +5720,36 @@ class GenericFunctionTypeImpl extends TypeAnnotationImpl */ FormalParameterListImpl _parameters; + @override + Token question; + @override DartType type; /** * Initialize a newly created generic function type. */ - GenericFunctionTypeImpl( - TypeAnnotationImpl returnType, - this.functionKeyword, - TypeParameterListImpl typeParameters, - FormalParameterListImpl parameters) { + GenericFunctionTypeImpl(TypeAnnotationImpl returnType, this.functionKeyword, + TypeParameterListImpl typeParameters, FormalParameterListImpl parameters, + {this.question}) { _returnType = _becomeParentOf(returnType); _typeParameters = _becomeParentOf(typeParameters); _parameters = _becomeParentOf(parameters); } @override - Token get beginToken => - _returnType == null ? functionKeyword : _returnType.beginToken; + Token get beginToken => _returnType?.beginToken ?? functionKeyword; @override Iterable get childEntities => new ChildEntities() ..add(_returnType) ..add(functionKeyword) ..add(_typeParameters) - ..add(_parameters); + ..add(_parameters) + ..add(question); @override - Token get endToken => _parameters.endToken; + Token get endToken => question ?? _parameters.endToken; @override FormalParameterList get parameters => _parameters; @@ -11031,7 +11032,7 @@ abstract class TypedLiteralImpl extends LiteralImpl implements TypedLiteral { * The name of a type, which can optionally include type arguments. * * typeName ::= - * [Identifier] typeArguments? + * [Identifier] typeArguments? '?'? */ class TypeNameImpl extends TypeAnnotationImpl implements TypeName { /** @@ -11045,6 +11046,9 @@ class TypeNameImpl extends TypeAnnotationImpl implements TypeName { */ TypeArgumentListImpl _typeArguments; + @override + Token question; + /** * The type being named, or `null` if the AST structure has not been resolved. */ @@ -11054,7 +11058,8 @@ class TypeNameImpl extends TypeAnnotationImpl implements TypeName { * Initialize a newly created type name. The [typeArguments] can be `null` if * there are no type arguments. */ - TypeNameImpl(IdentifierImpl name, TypeArgumentListImpl typeArguments) { + TypeNameImpl(IdentifierImpl name, TypeArgumentListImpl typeArguments, + {this.question}) { _name = _becomeParentOf(name); _typeArguments = _becomeParentOf(typeArguments); } @@ -11064,15 +11069,10 @@ class TypeNameImpl extends TypeAnnotationImpl implements TypeName { @override Iterable get childEntities => - new ChildEntities()..add(_name)..add(_typeArguments); + new ChildEntities()..add(_name)..add(_typeArguments)..add(question); @override - Token get endToken { - if (_typeArguments != null) { - return _typeArguments.endToken; - } - return _name.endToken; - } + Token get endToken => question ?? _typeArguments?.endToken ?? _name.endToken; @override bool get isDeferred { diff --git a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart index d12b7a6af7dc..cc7023a434b6 100644 --- a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart +++ b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart @@ -546,9 +546,11 @@ class AstFactoryImpl extends AstFactory { TypeAnnotation returnType, Token functionKeyword, TypeParameterList typeParameters, - FormalParameterList parameters) => + FormalParameterList parameters, + {Token question}) => new GenericFunctionTypeImpl( - returnType, functionKeyword, typeParameters, parameters); + returnType, functionKeyword, typeParameters, parameters, + question: question); @override GenericTypeAlias genericTypeAlias( @@ -952,8 +954,9 @@ class AstFactoryImpl extends AstFactory { new TypeArgumentListImpl(leftBracket, arguments, rightBracket); @override - TypeName typeName(Identifier name, TypeArgumentList typeArguments) => - new TypeNameImpl(name, typeArguments); + TypeName typeName(Identifier name, TypeArgumentList typeArguments, + {Token question}) => + new TypeNameImpl(name, typeArguments, question: question); @override TypeParameter typeParameter(Comment comment, List metadata, diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart index 2370ed0b96f9..7acc16357af0 100644 --- a/pkg/analyzer/lib/src/dart/ast/utilities.dart +++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart @@ -574,7 +574,8 @@ class AstCloner implements AstVisitor { cloneNode(node.returnType), cloneToken(node.functionKeyword), cloneNode(node.typeParameters), - cloneNode(node.parameters)); + cloneNode(node.parameters), + question: cloneToken(node.question)); @override AstNode visitGenericTypeAlias(GenericTypeAlias node) => @@ -958,7 +959,8 @@ class AstCloner implements AstVisitor { @override TypeName visitTypeName(TypeName node) => - astFactory.typeName(cloneNode(node.name), cloneNode(node.typeArguments)); + astFactory.typeName(cloneNode(node.name), cloneNode(node.typeArguments), + question: cloneToken(node.question)); @override TypeParameter visitTypeParameter(TypeParameter node) => @@ -1661,7 +1663,8 @@ class AstComparator implements AstVisitor { return isEqualNodes(node.returnType, other.returnType) && isEqualTokens(node.functionKeyword, other.functionKeyword) && isEqualNodes(node.typeParameters, other.typeParameters) && - isEqualNodes(node.parameters, other.parameters); + isEqualNodes(node.parameters, other.parameters) && + isEqualTokens(node.question, other.question); } @override @@ -2140,7 +2143,8 @@ class AstComparator implements AstVisitor { bool visitTypeName(TypeName node) { TypeName other = _other as TypeName; return isEqualNodes(node.name, other.name) && - isEqualNodes(node.typeArguments, other.typeArguments); + isEqualNodes(node.typeArguments, other.typeArguments) && + isEqualTokens(node.question, other.question); } @override @@ -3384,7 +3388,8 @@ class IncrementalAstCloner implements AstVisitor { _cloneNode(node.returnType), _mapToken(node.functionKeyword), _cloneNode(node.typeParameters), - _cloneNode(node.parameters)); + _cloneNode(node.parameters), + question: _mapToken(node.question)); @override AstNode visitGenericTypeAlias(GenericTypeAlias node) => @@ -3887,7 +3892,8 @@ class IncrementalAstCloner implements AstVisitor { @override TypeName visitTypeName(TypeName node) { TypeName copy = astFactory.typeName( - _cloneNode(node.name), _cloneNode(node.typeArguments)); + _cloneNode(node.name), _cloneNode(node.typeArguments), + question: _mapToken(node.question)); copy.type = node.type; return copy; } @@ -6073,7 +6079,8 @@ class ResolutionCopier implements AstVisitor { _isEqualNodes(node.returnType, toNode.returnType), _isEqualTokens(node.functionKeyword, toNode.functionKeyword), _isEqualNodes(node.typeParameters, toNode.typeParameters), - _isEqualNodes(node.parameters, toNode.parameters))) { + _isEqualNodes(node.parameters, toNode.parameters), + _isEqualTokens(node.question, toNode.question))) { toNode.type = node.type; return true; } @@ -6718,8 +6725,10 @@ class ResolutionCopier implements AstVisitor { @override bool visitTypeName(TypeName node) { TypeName toNode = this._toNode as TypeName; - if (_and(_isEqualNodes(node.name, toNode.name), - _isEqualNodes(node.typeArguments, toNode.typeArguments))) { + if (_and( + _isEqualNodes(node.name, toNode.name), + _isEqualNodes(node.typeArguments, toNode.typeArguments), + _isEqualTokens(node.question, toNode.question))) { toNode.type = node.type; return true; } @@ -7595,6 +7604,9 @@ class ToSourceVisitor implements AstVisitor { _writer.print(' Function'); _visitNode(node.typeParameters); _visitNode(node.parameters); + if (node.question != null) { + _writer.print('?'); + } } @override @@ -8035,6 +8047,9 @@ class ToSourceVisitor implements AstVisitor { void visitTypeName(TypeName node) { _visitNode(node.name); _visitNode(node.typeArguments); + if (node.question != null) { + _writer.print('?'); + } } @override @@ -8826,6 +8841,9 @@ class ToSourceVisitor2 implements AstVisitor { sink.write(' Function'); safelyVisitNode(node.typeParameters); safelyVisitNode(node.parameters); + if (node.question != null) { + sink.write('?'); + } } @override @@ -9266,6 +9284,9 @@ class ToSourceVisitor2 implements AstVisitor { void visitTypeName(TypeName node) { safelyVisitNode(node.name); safelyVisitNode(node.typeArguments); + if (node.question != null) { + sink.write('?'); + } } @override 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 7da4bb211445..824767b9b64b 100644 --- a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart +++ b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart @@ -611,9 +611,12 @@ class AstTestFactory { parameters: formalParameterList(parameters)); static GenericFunctionType genericFunctionType(TypeAnnotation returnType, - TypeParameterList typeParameters, FormalParameterList parameters) => + TypeParameterList typeParameters, FormalParameterList parameters, + {bool question: false}) => astFactory.genericFunctionType(returnType, - TokenFactory.tokenFromString("Function"), typeParameters, parameters); + TokenFactory.tokenFromString("Function"), typeParameters, parameters, + question: + question ? TokenFactory.tokenFromType(TokenType.QUESTION) : null); static GenericTypeAlias genericTypeAlias(String name, TypeParameterList typeParameters, GenericFunctionType functionType) => @@ -1264,8 +1267,11 @@ class AstTestFactory { [List arguments]) => astFactory.typeName(name, typeArgumentList(arguments)); - static TypeName typeName4(String name, [List arguments]) => - astFactory.typeName(identifier3(name), typeArgumentList(arguments)); + static TypeName typeName4(String name, + [List arguments, bool question = false]) => + astFactory.typeName(identifier3(name), typeArgumentList(arguments), + question: + question ? TokenFactory.tokenFromType(TokenType.QUESTION) : null); static TypeParameter typeParameter(String name) => astFactory.typeParameter(null, null, identifier3(name), null, null); diff --git a/pkg/analyzer/test/src/dart/ast/utilities_test.dart b/pkg/analyzer/test/src/dart/ast/utilities_test.dart index 795c3da83564..1008202fb24c 100644 --- a/pkg/analyzer/test/src/dart/ast/utilities_test.dart +++ b/pkg/analyzer/test/src/dart/ast/utilities_test.dart @@ -2404,6 +2404,19 @@ class ToSourceVisitor2Test extends EngineTestCase { ]))); } + void test_visitGenericFunctionType_withQuestion() { + _assertSource( + "int Function(T)?", + AstTestFactory.genericFunctionType( + AstTestFactory.typeName4("int"), + AstTestFactory.typeParameterList(['T']), + AstTestFactory.formalParameterList([ + AstTestFactory.simpleFormalParameter4( + AstTestFactory.typeName4("T"), null) + ]), + question: true)); + } + void test_visitGenericTypeAlias() { _assertSource( "typedef X = S Function(T)", @@ -3260,11 +3273,20 @@ class ToSourceVisitor2Test extends EngineTestCase { _assertSource("C", AstTestFactory.typeName4("C")); } + void test_visitTypeName_noArgs_withQuestion() { + _assertSource("C?", AstTestFactory.typeName4("C", null, true)); + } + void test_visitTypeName_singleArg() { _assertSource( "C", AstTestFactory.typeName4("C", [AstTestFactory.typeName4("D")])); } + void test_visitTypeName_singleArg_withQuestion() { + _assertSource("C?", + AstTestFactory.typeName4("C", [AstTestFactory.typeName4("D")], true)); + } + void test_visitTypeParameter_withExtends() { _assertSource("E extends C", AstTestFactory.typeParameter2("E", AstTestFactory.typeName4("C"))); @@ -4790,6 +4812,19 @@ class ToSourceVisitorTest extends EngineTestCase { ]))); } + void test_visitGenericFunctionType_withQuestion() { + _assertSource( + "int Function(T)?", + AstTestFactory.genericFunctionType( + AstTestFactory.typeName4("int"), + AstTestFactory.typeParameterList(['T']), + AstTestFactory.formalParameterList([ + AstTestFactory.simpleFormalParameter4( + AstTestFactory.typeName4("T"), null) + ]), + question: true)); + } + void test_visitGenericTypeAlias() { _assertSource( "typedef X = S Function(T)",