Index: pkg/analyzer/lib/src/generated/parser.dart |
diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart |
index cb2a41218b94e69df3cb937443cced98691977ad..c75ad60930f018501fab853d9cb962195001ce93 100644 |
--- a/pkg/analyzer/lib/src/generated/parser.dart |
+++ b/pkg/analyzer/lib/src/generated/parser.dart |
@@ -90,7 +90,7 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{ |
'parseTypeArgumentList_0': new MethodTrampoline( |
0, (Parser target) => target.parseTypeArgumentList()), |
'parseTypeName_0': |
- new MethodTrampoline(0, (Parser target) => target.parseTypeName()), |
+ new MethodTrampoline(0, (Parser target) => target.parseTypeName(false)), |
'parseTypeParameter_0': |
new MethodTrampoline(0, (Parser target) => target.parseTypeParameter()), |
'parseTypeParameterList_0': new MethodTrampoline( |
@@ -683,6 +683,12 @@ class Parser { |
bool _enableAssertInitializer = false; |
/** |
+ * A flag indicating whether the parser is to parse the non-nullable modifier |
+ * in type names. |
+ */ |
+ bool _enableNnbd = false; |
+ |
+ /** |
* A flag indicating whether the parser is to parse the async support. |
*/ |
bool _parseAsync = true; |
@@ -764,6 +770,20 @@ class Parser { |
} |
/** |
+ * Return `true` if the parser is to parse the non-nullable modifier in type |
+ * names. |
+ */ |
+ bool get enableNnbd => _enableNnbd; |
+ |
+ /** |
+ * Set whether the parser is to parse the non-nullable modifier in type names |
+ * to match the given [enable] flag. |
+ */ |
+ void set enableNnbd(bool enable) { |
+ _enableNnbd = enable; |
+ } |
+ |
+ /** |
* Return `true` if the current token is the first token of a return type that |
* is followed by an identifier, possibly followed by a list of type |
* parameters, followed by a left-parenthesis. This is used by |
@@ -1521,7 +1541,7 @@ class Parser { |
* type ('.' identifier)? |
*/ |
ConstructorName parseConstructorName() { |
- TypeName type = parseTypeName(); |
+ TypeName type = parseTypeName(false); |
Token period = null; |
SimpleIdentifier name = null; |
if (_matches(TokenType.PERIOD)) { |
@@ -1637,7 +1657,7 @@ class Parser { |
*/ |
ExtendsClause parseExtendsClause() { |
Token keyword = getAndAdvance(); |
- TypeName superclass = parseTypeName(); |
+ TypeName superclass = parseTypeName(false); |
return new ExtendsClause(keyword, superclass); |
} |
@@ -1718,9 +1738,9 @@ class Parser { |
ImplementsClause parseImplementsClause() { |
Token keyword = getAndAdvance(); |
List<TypeName> interfaces = <TypeName>[]; |
- interfaces.add(parseTypeName()); |
+ interfaces.add(parseTypeName(false)); |
while (_optional(TokenType.COMMA)) { |
- interfaces.add(parseTypeName()); |
+ interfaces.add(parseTypeName(false)); |
} |
return new ImplementsClause(keyword, interfaces); |
} |
@@ -1822,13 +1842,18 @@ class Parser { |
_reportErrorForToken( |
ParserErrorCode.FUNCTION_TYPED_PARAMETER_VAR, holder.keyword); |
} |
+ Token question = null; |
+ if (enableNnbd && _matches(TokenType.QUESTION)) { |
+ question = getAndAdvance(); |
+ } |
return new FunctionTypedFormalParameter( |
commentAndMetadata.comment, |
commentAndMetadata.metadata, |
holder.type, |
new SimpleIdentifier(identifier.token, isDeclaration: true), |
typeParameters, |
- parameters); |
+ parameters, |
+ question: question); |
} else { |
return new FieldFormalParameter( |
commentAndMetadata.comment, |
@@ -1901,7 +1926,7 @@ class Parser { |
if (_currentToken.keyword == Keyword.VOID) { |
return new TypeName(new SimpleIdentifier(getAndAdvance()), null); |
} else { |
- return parseTypeName(); |
+ return parseTypeName(false); |
} |
} |
@@ -1991,9 +2016,9 @@ class Parser { |
*/ |
TypeArgumentList parseTypeArgumentList() { |
Token leftBracket = getAndAdvance(); |
- List<TypeName> arguments = <TypeName>[parseTypeName()]; |
+ List<TypeName> arguments = <TypeName>[parseTypeName(false)]; |
while (_optional(TokenType.COMMA)) { |
- arguments.add(parseTypeName()); |
+ arguments.add(parseTypeName(false)); |
} |
Token rightBracket = _expectGt(); |
return new TypeArgumentList(leftBracket, arguments, rightBracket); |
@@ -2005,8 +2030,8 @@ class Parser { |
* type ::= |
* qualified typeArguments? |
*/ |
- TypeName parseTypeName() { |
- TypeName realType = _parseTypeName(); |
+ TypeName parseTypeName(bool inExpression) { |
+ TypeName realType = _parseTypeName(inExpression); |
// If this is followed by a generic method type comment, allow the comment |
// type to replace the real type name. |
// TODO(jmesserly): this feels like a big hammer. Can we restrict it to |
@@ -2026,7 +2051,7 @@ class Parser { |
SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); |
if (_matchesKeyword(Keyword.EXTENDS)) { |
Token keyword = getAndAdvance(); |
- TypeName bound = parseTypeName(); |
+ TypeName bound = parseTypeName(false); |
return new TypeParameter(commentAndMetadata.comment, |
commentAndMetadata.metadata, name, keyword, bound); |
} |
@@ -2063,14 +2088,31 @@ class Parser { |
*/ |
WithClause parseWithClause() { |
Token withKeyword = getAndAdvance(); |
- List<TypeName> types = <TypeName>[parseTypeName()]; |
+ List<TypeName> types = <TypeName>[parseTypeName(false)]; |
while (_optional(TokenType.COMMA)) { |
- types.add(parseTypeName()); |
+ types.add(parseTypeName(false)); |
} |
return new WithClause(withKeyword, types); |
} |
/** |
+ * Return `true` if the current token could be the question mark in a |
+ * condition expression. The current token is assumed to be a question mark. |
+ */ |
+ bool _isConditionalOperator() { |
+ void parseFunction(Parser parser) { |
scheglov
2016/09/16 15:29:38
The name looks like this function will parse a fun
Brian Wilkerson
2016/09/16 15:47:35
Yes it does. :-) I've renamed it to 'parseOperatio
|
+ parser.parseExpressionWithoutCascade(); |
+ } |
+ |
+ Token token = _skip(_currentToken.next, parseFunction); |
+ if (token == null || !_tokenMatches(token, TokenType.COLON)) { |
+ return false; |
+ } |
+ token = _skip(token.next, parseFunction); |
scheglov
2016/09/16 15:29:38
Out of curiosity - what is an example when just se
Brian Wilkerson
2016/09/16 15:47:35
I'm concerned about cases like
if (foo is Bar?
|
+ return token != null; |
+ } |
+ |
+ /** |
* Advance to the next token in the token stream. |
*/ |
void _advance() { |
@@ -3507,7 +3549,7 @@ class Parser { |
SimpleIdentifier className, |
TypeParameterList typeParameters) { |
Token equals = _expect(TokenType.EQ); |
- TypeName superclass = parseTypeName(); |
+ TypeName superclass = parseTypeName(false); |
WithClause withClause = null; |
if (_matchesKeyword(Keyword.WITH)) { |
withClause = parseWithClause(); |
@@ -4580,7 +4622,7 @@ class Parser { |
if (keyword == Keyword.FINAL || keyword == Keyword.CONST) { |
keywordToken = getAndAdvance(); |
if (_isTypedIdentifier(_currentToken)) { |
- type = parseTypeName(); |
+ type = parseTypeName(false); |
} else { |
// Support `final/*=T*/ x;` |
type = _parseOptionalTypeNameComment(); |
@@ -6217,7 +6259,7 @@ class Parser { |
TypeName _parseOptionalTypeNameComment() { |
if (_injectGenericCommentTypeAssign()) { |
- return _parseTypeName(); |
+ return _parseTypeName(false); |
} |
return null; |
} |
@@ -6555,7 +6597,7 @@ class Parser { |
Keyword keyword = _currentToken.keyword; |
if (keyword == Keyword.AS) { |
Token asOperator = getAndAdvance(); |
- return new AsExpression(expression, asOperator, parseTypeName()); |
+ return new AsExpression(expression, asOperator, parseTypeName(true)); |
} else if (keyword == Keyword.IS) { |
Token isOperator = getAndAdvance(); |
Token notOperator = null; |
@@ -6563,7 +6605,7 @@ class Parser { |
notOperator = getAndAdvance(); |
} |
return new IsExpression( |
- expression, isOperator, notOperator, parseTypeName()); |
+ expression, isOperator, notOperator, parseTypeName(true)); |
} else if (_currentToken.type.isRelationalOperator) { |
Token operator = getAndAdvance(); |
return new BinaryExpression( |
@@ -7002,7 +7044,7 @@ class Parser { |
TypeName exceptionType = null; |
if (_matchesString(_ON)) { |
onKeyword = getAndAdvance(); |
- exceptionType = parseTypeName(); |
+ exceptionType = parseTypeName(false); |
} |
Token catchKeyword = null; |
Token leftParenthesis = null; |
@@ -7092,7 +7134,7 @@ class Parser { |
return _parseFunctionTypeAlias(commentAndMetadata, keyword); |
} |
- TypeName _parseTypeName() { |
+ TypeName _parseTypeName(bool inExpression) { |
Identifier typeName; |
if (_matchesIdentifier()) { |
typeName = _parsePrefixedIdentifierUnchecked(); |
@@ -7104,7 +7146,50 @@ class Parser { |
_reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TYPE_NAME); |
} |
TypeArgumentList typeArguments = _parseOptionalTypeArguments(); |
- return new TypeName(typeName, typeArguments); |
+ Token question = null; |
+ if (enableNnbd && _matches(TokenType.QUESTION)) { |
+ if (!inExpression || !_isConditionalOperator()) { |
+ question = getAndAdvance(); |
+ } |
+ } |
+ return new TypeName(typeName, typeArguments, question: question); |
+ } |
+ |
+ /** |
+ * Execute the given [parseFunction] in a temporary parser whose current token |
+ * has been set to the given [startToken]. If the parse does not generate any |
+ * errors or exceptions, then return the token following the matching portion |
+ * of the token stream. Otherwise, return `null`. |
+ * |
+ * Note: This is an extremely inefficient way of testing whether the tokens in |
+ * the token stream match a given production. It should not be used for |
+ * production code. |
+ */ |
+ Token _skip(Token startToken, parseFunction(Parser parser)) { |
+ // TODO(brianwilkerson) We need to create a copy of the token stream from |
+ // the current token onward so that token re-writes don't change the |
+ // behavior of the current parser. |
+ BooleanErrorListener listener = new BooleanErrorListener(); |
+ Parser parser = new Parser(_source, listener); |
+ parser._currentToken = startToken; |
+ parser._enableAssertInitializer = _enableAssertInitializer; |
+ parser._enableNnbd = _enableNnbd; |
+ parser._inAsync = _inAsync; |
+ parser._inGenerator = _inGenerator; |
+ parser._inInitializer = _inInitializer; |
+ parser._inLoop = _inLoop; |
+ parser._inSwitch = _inSwitch; |
+ parser._parseAsync = _parseAsync; |
+ parser._parseFunctionBodies = _parseFunctionBodies; |
+ try { |
+ parseFunction(parser); |
+ } catch (exception) { |
+ return null; |
+ } |
+ if (listener.errorReported) { |
+ return null; |
+ } |
+ return parser._currentToken; |
} |
/** |