Chromium Code Reviews| 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; |
| } |
| /** |