Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(196)

Unified Diff: pkg/analyzer/lib/src/generated/parser.dart

Issue 137863002: Issue 8742. Preserve leading line comments during java2dart translation. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Test for block-style comment translation. Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer/lib/src/generated/index.dart ('k') | pkg/analyzer/lib/src/generated/resolver.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 d325fd1eaf3235f63bd193fcbce244d25bf3fcb6..4e99c1be378465678fd7bc9becaeec36cf2b20ec 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -224,6 +224,10 @@ class IncrementalParseDispatcher implements ASTVisitor<ASTNode> {
ASTNode visitAssignmentExpression(AssignmentExpression node) {
if (identical(_oldNode, node.leftHandSide)) {
+ // TODO(brianwilkerson) If the assignment is part of a cascade section, then we don't have a
+ // single parse method that will work. Otherwise, we can parse a conditional expression, but
+ // need to ensure that the resulting expression is assignable.
+ // return parser.parseConditionalExpression();
throw new InsufficientContextException();
} else if (identical(_oldNode, node.rightHandSide)) {
if (isCascadeAllowed(node)) {
@@ -509,6 +513,7 @@ class IncrementalParseDispatcher implements ASTVisitor<ASTNode> {
}
ASTNode visitFormalParameterList(FormalParameterList node) {
+ // We don't know which kind of parameter to parse.
throw new InsufficientContextException();
}
@@ -756,6 +761,7 @@ class IncrementalParseDispatcher implements ASTVisitor<ASTNode> {
}
return _parser.parseSimpleIdentifier();
} else if (identical(_oldNode, node.body)) {
+ //return parser.parseFunctionBody();
throw new InsufficientContextException();
}
return notAChild(node);
@@ -1076,6 +1082,7 @@ class IncrementalParseDispatcher implements ASTVisitor<ASTNode> {
* @return `true` if the right-hand side can be a cascade expression
*/
bool isCascadeAllowed(AssignmentExpression node) {
+ // TODO(brianwilkerson) Implement this method.
throw new InsufficientContextException();
}
@@ -1086,6 +1093,7 @@ class IncrementalParseDispatcher implements ASTVisitor<ASTNode> {
* @return `true` if the expression can be a cascade expression
*/
bool isCascadeAllowed2(ThrowExpression node) {
+ // TODO(brianwilkerson) Implement this method.
throw new InsufficientContextException();
}
@@ -1187,20 +1195,33 @@ class IncrementalParser {
ASTNode reparse(ASTNode originalStructure, Token leftToken, Token rightToken, int originalStart, int originalEnd) {
ASTNode oldNode = null;
ASTNode newNode = null;
+ //
+ // Find the first token that needs to be re-parsed.
+ //
Token firstToken = leftToken.next;
if (identical(firstToken, rightToken)) {
+ // If there are no new tokens, then we need to include at least one copied node in the range.
firstToken = leftToken;
}
+ //
+ // Find the smallest AST node that encompasses the range of re-scanned tokens.
+ //
if (originalEnd < originalStart) {
oldNode = new NodeLocator.con1(originalStart).searchWithin(originalStructure);
} else {
oldNode = new NodeLocator.con2(originalStart, originalEnd).searchWithin(originalStructure);
}
+ //
+ // Find the token at which parsing is to begin.
+ //
int originalOffset = oldNode.offset;
Token parseToken = findTokenAt(firstToken, originalOffset);
if (parseToken == null) {
return null;
}
+ //
+ // Parse the appropriate AST structure starting at the appropriate place.
+ //
Parser parser = new Parser(_source, _errorListener);
parser.currentToken = parseToken;
while (newNode == null) {
@@ -1214,6 +1235,9 @@ class IncrementalParser {
try {
IncrementalParseDispatcher dispatcher = new IncrementalParseDispatcher(parser, oldNode);
newNode = parent.accept(dispatcher);
+ //
+ // Validate that the new node can replace the old node.
+ //
Token mappedToken = _tokenMap.get(oldNode.endToken.next);
if (mappedToken == null || mappedToken.offset != newNode.endToken.next.offset || newNode.offset != oldNode.offset) {
advanceToParent = true;
@@ -1232,7 +1256,11 @@ class IncrementalParser {
}
}
_updatedNode = newNode;
+ //
+ // Replace the old node with the new node in a copy of the original AST structure.
+ //
if (identical(oldNode, originalStructure)) {
+ // We ended up re-parsing the whole structure, so there's no need for a copy.
ResolutionCopier.copyResolutionData(oldNode, newNode);
return newNode;
}
@@ -1476,6 +1504,10 @@ class Parser {
* @return the argument that was parsed
*/
Expression parseArgument() {
+ //
+ // Both namedArgument and expression can start with an identifier, but only namedArgument can
+ // have an identifier followed by a colon.
+ //
if (matchesIdentifier() && matches4(peek(), TokenType.COLON)) {
return new NamedExpression(parseLabel(), parseExpression3());
} else {
@@ -1503,6 +1535,10 @@ class Parser {
if (matches5(TokenType.CLOSE_PAREN)) {
return new ArgumentList(leftParenthesis, arguments, andAdvance);
}
+ //
+ // Even though unnamed arguments must all appear before any named arguments, we allow them to
+ // appear in any order so that we can recover faster.
+ //
Expression argument = parseArgument();
arguments.add(argument);
bool foundNamedArgument = argument is NamedExpression;
@@ -1512,6 +1548,7 @@ class Parser {
arguments.add(argument);
if (foundNamedArgument) {
if (!generatedError && argument is! NamedExpression) {
+ // Report the error, once, but allow the arguments to be in any order in the AST.
reportError11(ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT, []);
generatedError = true;
}
@@ -1519,6 +1556,9 @@ class Parser {
foundNamedArgument = true;
}
}
+ // TODO(brianwilkerson) Recovery: Look at the left parenthesis to see whether there is a
+ // matching right parenthesis. If there is, then we're more likely missing a comma and should
+ // go back to parsing arguments.
Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
return new ArgumentList(leftParenthesis, arguments, rightParenthesis);
}
@@ -1568,6 +1608,7 @@ class Parser {
statements.add(statement);
}
if (identical(_currentToken, statementStart)) {
+ // Ensure that we are making progress and report an error if we're not.
reportError12(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
advance();
}
@@ -1611,13 +1652,22 @@ class Parser {
validateModifiersForGetterOrSetterOrMethod(modifiers);
return parseMethodDeclaration(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, returnType);
} else {
+ //
+ // We have found an error of some kind. Try to recover.
+ //
if (matchesIdentifier()) {
if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
+ //
+ // We appear to have a variable declaration with a type of "void".
+ //
reportError10(ParserErrorCode.VOID_VARIABLE, returnType, []);
return parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, validateModifiersForField(modifiers), returnType);
}
}
if (isOperator(_currentToken)) {
+ //
+ // We appear to have found an operator declaration without the 'operator' keyword.
+ //
validateModifiersForOperator(modifiers);
return parseOperator(commentAndMetadata, modifiers.externalKeyword, returnType);
}
@@ -1635,6 +1685,9 @@ class Parser {
return parseOperator(commentAndMetadata, modifiers.externalKeyword, null);
} else if (!matchesIdentifier()) {
if (isOperator(_currentToken)) {
+ //
+ // We appear to have found an operator declaration without the 'operator' keyword.
+ //
validateModifiersForOperator(modifiers);
return parseOperator(commentAndMetadata, modifiers.externalKeyword, null);
}
@@ -1669,12 +1722,25 @@ class Parser {
return parseOperator(commentAndMetadata, modifiers.externalKeyword, type);
} else if (!matchesIdentifier()) {
if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
+ //
+ // We appear to have found an incomplete declaration at the end of the class. At this point
+ // it consists of a type name, so we'll treat it as a field declaration with a missing
+ // field name and semicolon.
+ //
return parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, validateModifiersForField(modifiers), type);
}
if (isOperator(_currentToken)) {
+ //
+ // We appear to have found an operator declaration without the 'operator' keyword.
+ //
validateModifiersForOperator(modifiers);
return parseOperator(commentAndMetadata, modifiers.externalKeyword, type);
}
+ //
+ // We appear to have found an incomplete declaration before another declaration.
+ // At this point it consists of a type name, so we'll treat it as a field declaration
+ // with a missing field name and semicolon.
+ //
reportError12(ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken, []);
try {
lockErrorListener();
@@ -1724,6 +1790,10 @@ class Parser {
if (matches5(TokenType.SCRIPT_TAG)) {
scriptTag = new ScriptTag(andAdvance);
}
+ //
+ // Even though all directives must appear before declarations and must occur in a given order,
+ // we allow directives and declarations to occur in any order so that we can recover better.
+ //
bool libraryDirectiveFound = false;
bool partOfDirectiveFound = false;
bool partDirectiveFound = false;
@@ -1855,6 +1925,11 @@ class Parser {
} else if (matches(Keyword.RETHROW)) {
return parseRethrowExpression();
}
+ //
+ // assignableExpression is a subset of conditionalExpression, so we can parse a conditional
+ // expression and then determine whether it is followed by an assignmentOperator, checking for
+ // conformance to the restricted grammar after making that determination.
+ //
Expression expression = parseConditionalExpression();
TokenType tokenType = _currentToken.type;
if (identical(tokenType, TokenType.PERIOD_PERIOD)) {
@@ -1893,6 +1968,11 @@ class Parser {
} else if (matches(Keyword.RETHROW)) {
return parseRethrowExpression();
}
+ //
+ // assignableExpression is a subset of conditionalExpression, so we can parse a conditional
+ // expression and then determine whether it is followed by an assignmentOperator, checking for
+ // conformance to the restricted grammar after making that determination.
+ //
Expression expression = parseConditionalExpression();
if (_currentToken.type.isAssignmentOperator) {
Token operator = andAdvance;
@@ -1948,6 +2028,11 @@ class Parser {
if (matches5(TokenType.CLOSE_PAREN)) {
return new FormalParameterList(leftParenthesis, null, null, null, andAdvance);
}
+ //
+ // Even though it is invalid to have default parameters outside of brackets, required parameters
+ // inside of brackets, or multiple groups of default and named parameters, we allow all of these
+ // cases so that we can recover better.
+ //
List<FormalParameter> parameters = new List<FormalParameter>();
List<FormalParameter> normalParameters = new List<FormalParameter>();
List<FormalParameter> positionalParameters = new List<FormalParameter>();
@@ -1968,6 +2053,7 @@ class Parser {
if (firstParameter) {
firstParameter = false;
} else if (!optional(TokenType.COMMA)) {
+ // TODO(brianwilkerson) The token is wrong, we need to recover from this case.
if (getEndToken(leftParenthesis) != null) {
reportError11(ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]);
} else {
@@ -1976,6 +2062,9 @@ class Parser {
}
}
initialToken = _currentToken;
+ //
+ // Handle the beginning of parameter groups.
+ //
if (matches5(TokenType.OPEN_SQUARE_BRACKET)) {
wasOptionalParameter = true;
if (leftSquareBracket != null && !reportedMuliplePositionalGroups) {
@@ -2003,12 +2092,19 @@ class Parser {
currentParameters = namedParameters;
kind = ParameterKind.NAMED;
}
+ //
+ // Parse and record the parameter.
+ //
FormalParameter parameter = parseFormalParameter(kind);
parameters.add(parameter);
currentParameters.add(parameter);
if (identical(kind, ParameterKind.REQUIRED) && wasOptionalParameter) {
reportError10(ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter, []);
}
+ //
+ // Handle the end of parameter groups.
+ //
+ // TODO(brianwilkerson) Improve the detection and reporting of missing and mismatched delimiters.
if (matches5(TokenType.CLOSE_SQUARE_BRACKET)) {
rightSquareBracket = andAdvance;
currentParameters = normalParameters;
@@ -2038,12 +2134,18 @@ class Parser {
}
} while (!matches5(TokenType.CLOSE_PAREN) && initialToken != _currentToken);
Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
+ //
+ // Check that the groups were closed correctly.
+ //
if (leftSquareBracket != null && rightSquareBracket == null) {
reportError11(ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
}
if (leftCurlyBracket != null && rightCurlyBracket == null) {
reportError11(ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
}
+ //
+ // Build the parameter list.
+ //
if (leftSquareBracket == null) {
leftSquareBracket = leftCurlyBracket;
}
@@ -2542,15 +2644,20 @@ class Parser {
*/
bool couldBeStartOfCompilationUnitMember() {
if ((matches(Keyword.IMPORT) || matches(Keyword.EXPORT) || matches(Keyword.LIBRARY) || matches(Keyword.PART)) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
+ // This looks like the start of a directive
return true;
} else if (matches(Keyword.CLASS)) {
+ // This looks like the start of a class definition
return true;
} else if (matches(Keyword.TYPEDEF) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
+ // This looks like the start of a typedef
return true;
} else if (matches(Keyword.VOID) || ((matches(Keyword.GET) || matches(Keyword.SET)) && matchesIdentifier2(peek())) || (matches(Keyword.OPERATOR) && isOperator(peek()))) {
+ // This looks like the start of a function
return true;
} else if (matchesIdentifier()) {
if (matches4(peek(), TokenType.OPEN_PAREN)) {
+ // This looks like the start of a function
return true;
}
Token token = skipReturnType(_currentToken);
@@ -2572,6 +2679,10 @@ class Parser {
SimpleIdentifier createSyntheticIdentifier() {
Token syntheticToken;
if (identical(_currentToken.type, TokenType.KEYWORD)) {
+ // Consider current keyword token as an identifier.
+ // It is not always true, e.g. "^is T" where "^" is place the place for synthetic identifier.
+ // By creating SyntheticStringToken we can distinguish a real identifier from synthetic.
+ // In the code completion behavior will depend on a cursor position - before or on "is".
syntheticToken = injectToken(new SyntheticStringToken(TokenType.IDENTIFIER, _currentToken.lexeme, _currentToken.offset));
} else {
syntheticToken = createSyntheticToken2(TokenType.IDENTIFIER);
@@ -2633,6 +2744,8 @@ class Parser {
if (matches(keyword)) {
return andAdvance;
}
+ // Remove uses of this method in favor of matches?
+ // Pass in the error code to use to report the error?
reportError11(ParserErrorCode.EXPECTED_TOKEN, [keyword.syntax]);
return _currentToken;
}
@@ -2648,6 +2761,8 @@ class Parser {
if (matches5(type)) {
return andAdvance;
}
+ // Remove uses of this method in favor of matches?
+ // Pass in the error code to use to report the error?
if (identical(type, TokenType.SEMICOLON)) {
reportError12(ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, [type.lexeme]);
} else {
@@ -2786,10 +2901,13 @@ class Parser {
}
Token afterReturnType = skipTypeName(_currentToken);
if (afterReturnType == null) {
+ // There was no return type, but it is optional, so go back to where we started.
afterReturnType = _currentToken;
}
Token afterIdentifier = skipSimpleIdentifier(afterReturnType);
if (afterIdentifier == null) {
+ // It's possible that we parsed the function name as if it were a type name, so see whether
+ // it makes sense if we assume that there is no type.
afterIdentifier = skipSimpleIdentifier(_currentToken);
}
if (afterIdentifier == null) {
@@ -2798,6 +2916,8 @@ class Parser {
if (isFunctionExpression(afterIdentifier)) {
return true;
}
+ // It's possible that we have found a getter. While this isn't valid at this point we test for
+ // it in order to recover better.
if (matches(Keyword.GET)) {
Token afterName = skipSimpleIdentifier(_currentToken.next);
if (afterName == null) {
@@ -2860,17 +2980,22 @@ class Parser {
*/
bool isInitializedVariableDeclaration() {
if (matches(Keyword.FINAL) || matches(Keyword.VAR)) {
+ // An expression cannot start with a keyword other than 'const', 'rethrow', or 'throw'.
return true;
}
if (matches(Keyword.CONST)) {
+ // Look to see whether we might be at the start of a list or map literal, otherwise this
+ // should be the start of a variable declaration.
return !matchesAny(peek(), [
TokenType.LT,
TokenType.OPEN_CURLY_BRACKET,
TokenType.OPEN_SQUARE_BRACKET,
TokenType.INDEX]);
}
+ // We know that we have an identifier, and need to see whether it might be a type name.
Token token = skipTypeName(_currentToken);
if (token == null) {
+ // There was no type name, so this can't be a declaration.
return false;
}
token = skipSimpleIdentifier(token);
@@ -2920,16 +3045,20 @@ class Parser {
* @return `true` if the given token appears to be the beginning of an operator declaration
*/
bool isOperator(Token startToken) {
+ // Accept any operator here, even if it is not user definable.
if (!startToken.isOperator) {
return false;
}
+ // Token "=" means that it is actually field initializer.
if (identical(startToken.type, TokenType.EQ)) {
return false;
}
+ // Consume all operator tokens.
Token token = startToken.next;
while (token.isOperator) {
token = token.next;
}
+ // Formal parameter list is expect now.
return matches4(token, TokenType.OPEN_PAREN);
}
@@ -3220,6 +3349,11 @@ class Parser {
if (matches(Keyword.SUPER)) {
return parseAssignableSelector(new SuperExpression(andAdvance), false);
}
+ //
+ // A primary expression can start with an identifier. We resolve the ambiguity by determining
+ // whether the primary consists of anything other than an identifier and/or is followed by an
+ // assignableSelector.
+ //
Expression expression = parsePrimaryExpression();
bool isOptional = primaryAllowed || expression is SimpleIdentifier;
while (true) {
@@ -3277,6 +3411,7 @@ class Parser {
return new PropertyAccess(prefix, period, parseSimpleIdentifier());
} else {
if (!optional) {
+ // Report the missing selector.
reportError11(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, []);
}
return prefix;
@@ -3396,6 +3531,7 @@ class Parser {
period = null;
functionName = null;
} else if (expression == null) {
+ // It should not be possible to get here.
expression = new MethodInvocation(expression, period, createSyntheticIdentifier(), parseArgumentList());
} else {
expression = new FunctionExpressionInvocation(expression, parseArgumentList());
@@ -3463,6 +3599,10 @@ class Parser {
if (matches5(TokenType.LT)) {
typeParameters = parseTypeParameterList();
}
+ //
+ // Parse the clauses. The parser accepts clauses in any order, but will generate errors if they
+ // are not in the order required by the specification.
+ //
ExtendsClause extendsClause = null;
WithClause withClause = null;
ImplementsClause implementsClause = null;
@@ -3504,10 +3644,16 @@ class Parser {
if (withClause != null && extendsClause == null) {
reportError12(ParserErrorCode.WITH_WITHOUT_EXTENDS, withClause.withKeyword, []);
}
+ //
+ // Look for and skip over the extra-lingual 'native' specification.
+ //
NativeClause nativeClause = null;
if (matches2(_NATIVE) && matches4(peek(), TokenType.STRING)) {
nativeClause = parseNativeClause();
}
+ //
+ // Parse the body of the class.
+ //
Token leftBracket = null;
List<ClassMember> members = null;
Token rightBracket = null;
@@ -3677,6 +3823,7 @@ class Parser {
* @return the comment reference that was parsed, or `null` if no reference could be found
*/
CommentReference parseCommentReference(String referenceSource, int sourceOffset) {
+ // TODO(brianwilkerson) The errors are not getting the right offset/length and are being duplicated.
if (referenceSource.length == 0) {
return null;
}
@@ -3710,6 +3857,10 @@ class Parser {
}
return new CommentReference(newKeyword, identifier);
} else if (matches3(firstToken, Keyword.THIS) || matches3(firstToken, Keyword.NULL) || matches3(firstToken, Keyword.TRUE) || matches3(firstToken, Keyword.FALSE)) {
+ // TODO(brianwilkerson) If we want to support this we will need to extend the definition
+ // of CommentReference to take an expression rather than an identifier. For now we just
+ // ignore it to reduce the number of errors produced, but that's probably not a valid
+ // long term approach.
return null;
}
} on JavaException catch (exception) {
@@ -3808,8 +3959,14 @@ class Parser {
validateModifiersForTopLevelFunction(modifiers);
return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, returnType);
} else {
+ //
+ // We have found an error of some kind. Try to recover.
+ //
if (matchesIdentifier()) {
if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
+ //
+ // We appear to have a variable declaration with a type of "void".
+ //
reportError10(ParserErrorCode.VOID_VARIABLE, returnType, []);
return new TopLevelVariableDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, parseVariableDeclarationList2(null, validateModifiersForTopLevelVariable(modifiers), null), expect2(TokenType.SEMICOLON));
}
@@ -3845,6 +4002,7 @@ class Parser {
} else if (matches5(TokenType.AT)) {
return new TopLevelVariableDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, parseVariableDeclarationList2(null, validateModifiersForTopLevelVariable(modifiers), returnType), expect2(TokenType.SEMICOLON));
} else if (!matchesIdentifier()) {
+ // TODO(brianwilkerson) Generalize this error. We could also be parsing a top-level variable at this point.
reportError12(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken, []);
Token semicolon;
if (matches5(TokenType.SEMICOLON)) {
@@ -4028,6 +4186,8 @@ class Parser {
} else if (matches(Keyword.PART)) {
return parsePartDirective(commentAndMetadata);
} else {
+ // Internal error: this method should not have been invoked if the current token was something
+ // other than one of the above.
throw new IllegalStateException("parseDirective invoked in an invalid state; currentToken = ${_currentToken}");
}
}
@@ -4296,6 +4456,7 @@ class Parser {
DeclaredIdentifier loopVariable = null;
SimpleIdentifier identifier = null;
if (variableList == null) {
+ // We found: <expression> 'in'
reportError11(ParserErrorCode.MISSING_VARIABLE_IN_FOR_EACH, []);
} else {
NodeList<VariableDeclaration> variables = variableList.variables;
@@ -4399,6 +4560,7 @@ class Parser {
}
return new NativeFunctionBody(nativeToken, stringLiteral, expect2(TokenType.SEMICOLON));
} else {
+ // Invalid function body
reportError11(emptyErrorCode, []);
return new EmptyFunctionBody(createSyntheticToken2(TokenType.SEMICOLON));
}
@@ -4452,6 +4614,11 @@ class Parser {
} else {
body = new EmptyFunctionBody(expect2(TokenType.SEMICOLON));
}
+ // if (!isStatement && matches(TokenType.SEMICOLON)) {
+ // // TODO(brianwilkerson) Improve this error message.
+ // reportError(ParserErrorCode.UNEXPECTED_TOKEN, currentToken.getLexeme());
+ // advance();
+ // }
return new FunctionDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, returnType, keyword, name, new FunctionExpression(parameters, body));
}
@@ -4529,6 +4696,9 @@ class Parser {
return new FunctionTypeAlias(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, returnType, name, typeParameters, parameters, semicolon);
} else if (!matches5(TokenType.OPEN_PAREN)) {
reportError11(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS, []);
+ // TODO(brianwilkerson) Recover from this error. At the very least we should skip to the start
+ // of the next valid compilation unit member, allowing for the possibility of finding the
+ // typedef parameters before that point.
return new FunctionTypeAlias(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, returnType, name, typeParameters, new FormalParameterList(createSyntheticToken2(TokenType.OPEN_PAREN), null, null, null, createSyntheticToken2(TokenType.CLOSE_PAREN)), createSyntheticToken2(TokenType.SEMICOLON));
}
FormalParameterList parameters = parseFormalParameterList();
@@ -4721,6 +4891,8 @@ class Parser {
if (matchesIdentifier()) {
return parseLibraryIdentifier();
} else if (matches5(TokenType.STRING)) {
+ // TODO(brianwilkerson) Recovery: This should be extended to handle arbitrary tokens until we
+ // can find a token that can start a compilation unit member.
StringLiteral string = parseStringLiteral();
reportError10(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string, []);
} else {
@@ -4746,6 +4918,7 @@ class Parser {
* @return the list literal that was parsed
*/
ListLiteral parseListLiteral(Token modifier, TypeArgumentList typeArguments) {
+ // may be empty list literal
if (matches5(TokenType.INDEX)) {
BeginToken leftBracket = new BeginToken(TokenType.OPEN_SQUARE_BRACKET, _currentToken.offset);
Token rightBracket = new Token(TokenType.CLOSE_SQUARE_BRACKET, _currentToken.offset + 1);
@@ -4756,6 +4929,7 @@ class Parser {
_currentToken = _currentToken.next;
return new ListLiteral(modifier, typeArguments, leftBracket, null, rightBracket);
}
+ // open
Token leftBracket = expect2(TokenType.OPEN_SQUARE_BRACKET);
if (matches5(TokenType.CLOSE_SQUARE_BRACKET)) {
return new ListLiteral(modifier, typeArguments, leftBracket, null, andAdvance);
@@ -5057,6 +5231,7 @@ class Parser {
* @return the non-labeled statement that was parsed
*/
Statement parseNonLabeledStatement() {
+ // TODO(brianwilkerson) Pass the comment and metadata on where appropriate.
CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
if (matches4(peek(), TokenType.STRING)) {
@@ -5068,6 +5243,7 @@ class Parser {
return parseBlock();
} else if (matches5(TokenType.KEYWORD) && !(_currentToken as KeywordToken).keyword.isPseudoKeyword) {
Keyword keyword = (_currentToken as KeywordToken).keyword;
+ // TODO(jwren) compute some metrics to figure out a better order for this if-then sequence to optimize performance
if (identical(keyword, Keyword.ASSERT)) {
return parseAssertStatement();
} else if (identical(keyword, Keyword.BREAK)) {
@@ -5102,15 +5278,26 @@ class Parser {
TokenType.FUNCTION])) {
return parseFunctionDeclarationStatement2(commentAndMetadata, returnType);
} else {
+ //
+ // We have found an error of some kind. Try to recover.
+ //
if (matchesIdentifier()) {
if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
+ //
+ // We appear to have a variable declaration with a type of "void".
+ //
reportError10(ParserErrorCode.VOID_VARIABLE, returnType, []);
return parseVariableDeclarationStatement(commentAndMetadata);
}
} else if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
+ //
+ // We appear to have found an incomplete statement at the end of a block. Parse it as a
+ // variable declaration.
+ //
return parseVariableDeclarationStatement2(commentAndMetadata, null, returnType);
}
reportError11(ParserErrorCode.MISSING_STATEMENT, []);
+ // TODO(brianwilkerson) Recover from this error.
return new EmptyStatement(createSyntheticToken2(TokenType.SEMICOLON));
}
} else if (identical(keyword, Keyword.CONST)) {
@@ -5132,6 +5319,9 @@ class Parser {
} else if (identical(keyword, Keyword.NEW) || identical(keyword, Keyword.TRUE) || identical(keyword, Keyword.FALSE) || identical(keyword, Keyword.NULL) || identical(keyword, Keyword.SUPER) || identical(keyword, Keyword.THIS)) {
return new ExpressionStatement(parseExpression3(), expect2(TokenType.SEMICOLON));
} else {
+ //
+ // We have found an error of some kind. Try to recover.
+ //
reportError11(ParserErrorCode.MISSING_STATEMENT, []);
return new EmptyStatement(createSyntheticToken2(TokenType.SEMICOLON));
}
@@ -5350,6 +5540,16 @@ class Parser {
} else if (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.INDEX)) {
return parseListLiteral(null, null);
} else if (matchesIdentifier()) {
+ // TODO(brianwilkerson) The code below was an attempt to recover from an error case, but it
+ // needs to be applied as a recovery only after we know that parsing it as an identifier
+ // doesn't work. Leaving the code as a reminder of how to recover.
+ // if (isFunctionExpression(peek())) {
+ // //
+ // // Function expressions were allowed to have names at one point, but this is now illegal.
+ // //
+ // reportError(ParserErrorCode.NAMED_FUNCTION_EXPRESSION, getAndAdvance());
+ // return parseFunctionExpression();
+ // }
return parsePrefixedIdentifier();
} else if (matches(Keyword.NEW)) {
return parseNewExpression();
@@ -5368,6 +5568,10 @@ class Parser {
} else if (matches5(TokenType.QUESTION)) {
return parseArgumentDefinitionTest();
} else if (matches(Keyword.VOID)) {
+ //
+ // Recover from having a return type of "void" where a return type is not expected.
+ //
+ // TODO(brianwilkerson) Improve this error message.
reportError11(ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
advance();
return parsePrimaryExpression();
@@ -5662,6 +5866,8 @@ class Parser {
Token colon = expect2(TokenType.COLON);
members.add(new SwitchDefault(labels, defaultKeyword, colon, parseStatements2()));
} else {
+ // We need to advance, otherwise we could end up in an infinite loop, but this could be a
+ // lot smarter about recovering from the error.
reportError11(ParserErrorCode.EXPECTED_CASE_OR_DEFAULT, []);
while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET) && !matches(Keyword.CASE) && !matches(Keyword.DEFAULT)) {
advance();
@@ -5878,6 +6084,10 @@ class Parser {
Token operator = andAdvance;
if (matches(Keyword.SUPER)) {
if (matches4(peek(), TokenType.OPEN_SQUARE_BRACKET) || matches4(peek(), TokenType.PERIOD)) {
+ // "prefixOperator unaryExpression"
+ // --> "prefixOperator postfixExpression"
+ // --> "prefixOperator primary selector*"
+ // --> "prefixOperator 'super' assignableSelector selector*"
return new PrefixExpression(operator, parseUnaryExpression());
}
return new PrefixExpression(operator, new SuperExpression(andAdvance));
@@ -5887,8 +6097,14 @@ class Parser {
Token operator = andAdvance;
if (matches(Keyword.SUPER)) {
if (matches4(peek(), TokenType.OPEN_SQUARE_BRACKET) || matches4(peek(), TokenType.PERIOD)) {
+ // --> "prefixOperator 'super' assignableSelector selector*"
return new PrefixExpression(operator, parseUnaryExpression());
}
+ //
+ // Even though it is not valid to use an incrementing operator ('++' or '--') before 'super',
+ // we can (and therefore must) interpret "--super" as semantically equivalent to "-(-super)".
+ // Unfortunately, we cannot do the same for "++super" because "+super" is also not valid.
+ //
if (identical(operator.type, TokenType.MINUS_MINUS)) {
int offset = operator.offset;
Token firstOperator = new Token(TokenType.MINUS, offset);
@@ -5898,6 +6114,7 @@ class Parser {
operator.previous.setNext(firstOperator);
return new PrefixExpression(firstOperator, new PrefixExpression(secondOperator, new SuperExpression(andAdvance)));
} else {
+ // Invalid operator before 'super'
reportError11(ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]);
return new PrefixExpression(operator, new SuperExpression(andAdvance));
}
@@ -5989,7 +6206,14 @@ class Parser {
* @return the variable declaration statement that was parsed
*/
VariableDeclarationStatement parseVariableDeclarationStatement(CommentAndMetadata commentAndMetadata) {
+ // Token startToken = currentToken;
VariableDeclarationList variableList = parseVariableDeclarationList(commentAndMetadata);
+ // if (!matches(TokenType.SEMICOLON)) {
+ // if (matches(startToken, Keyword.VAR) && isTypedIdentifier(startToken.getNext())) {
+ // // TODO(brianwilkerson) This appears to be of the form "var type variable". We should do
+ // // a better job of recovering in this case.
+ // }
+ // }
Token semicolon = expect2(TokenType.SEMICOLON);
return new VariableDeclarationStatement(variableList, semicolon);
}
@@ -6136,9 +6360,11 @@ class Parser {
Token next = startToken.next;
if (matchesIdentifier2(next)) {
Token next2 = next.next;
+ // "Type parameter" or "Type<" or "prefix.Type"
if (matchesIdentifier2(next2) || matches4(next2, TokenType.LT) || matches4(next2, TokenType.PERIOD)) {
return skipTypeName(next);
}
+ // "parameter"
return next;
}
} else if (matches3(startToken, Keyword.VAR)) {
@@ -6197,18 +6423,29 @@ class Parser {
if (matches4(next, TokenType.CLOSE_PAREN)) {
return next.next;
}
+ //
+ // Look to see whether the token after the open parenthesis is something that should only occur
+ // at the beginning of a parameter list.
+ //
if (matchesAny(next, [
TokenType.AT,
TokenType.OPEN_SQUARE_BRACKET,
TokenType.OPEN_CURLY_BRACKET]) || matches3(next, Keyword.VOID) || (matchesIdentifier2(next) && (matchesAny(next.next, [TokenType.COMMA, TokenType.CLOSE_PAREN])))) {
return skipPastMatchingToken(startToken);
}
+ //
+ // Look to see whether the first parameter is a function typed parameter without a return type.
+ //
if (matchesIdentifier2(next) && matches4(next.next, TokenType.OPEN_PAREN)) {
Token afterParameters = skipFormalParameterList(next.next);
if (afterParameters != null && (matchesAny(afterParameters, [TokenType.COMMA, TokenType.CLOSE_PAREN]))) {
return skipPastMatchingToken(startToken);
}
}
+ //
+ // Look to see whether the first parameter has a type or is a function typed parameter with a
+ // return type.
+ //
Token afterType = skipFinalConstVarOrType(next);
if (afterType == null) {
return null;
@@ -6328,6 +6565,11 @@ class Parser {
if (identical(type, TokenType.STRING_INTERPOLATION_EXPRESSION)) {
token = token.next;
type = token.type;
+ //
+ // Rather than verify that the following tokens represent a valid expression, we simply skip
+ // tokens until we reach the end of the interpolation, being careful to handle nested string
+ // literals.
+ //
int bracketNestingLevel = 1;
while (bracketNestingLevel > 0) {
if (identical(type, TokenType.EOF)) {
@@ -6484,6 +6726,10 @@ class Parser {
if (!matches4(startToken, TokenType.LT)) {
return null;
}
+ //
+ // We can't skip a type parameter because it can be preceeded by metadata, so we just assume
+ // that everything before the matching end token is valid.
+ //
int depth = 1;
Token next = startToken.next;
while (depth > 0) {
@@ -6532,9 +6778,18 @@ class Parser {
builder.appendChar(currentChar);
return index + 1;
}
+ //
+ // We have found an escape sequence, so we parse the string to determine what kind of escape
+ // sequence and what character to add to the builder.
+ //
int length = lexeme.length;
int currentIndex = index + 1;
if (currentIndex >= length) {
+ // Illegal escape sequence: no char after escape
+ // This cannot actually happen because it would require the escape character to be the last
+ // character in the string, but if it were it would escape the closing quote, leaving the
+ // string unclosed.
+ // reportError(ParserErrorCode.MISSING_CHAR_IN_ESCAPE_SEQUENCE);
return length;
}
currentChar = lexeme.codeUnitAt(currentIndex);
@@ -6552,12 +6807,14 @@ class Parser {
builder.appendChar(0xB);
} else if (currentChar == 0x78) {
if (currentIndex + 2 >= length) {
+ // Illegal escape sequence: not enough hex digits
reportError11(ParserErrorCode.INVALID_HEX_ESCAPE, []);
return length;
}
int firstDigit = lexeme.codeUnitAt(currentIndex + 1);
int secondDigit = lexeme.codeUnitAt(currentIndex + 2);
if (!isHexDigit(firstDigit) || !isHexDigit(secondDigit)) {
+ // Illegal escape sequence: invalid hex digit
reportError11(ParserErrorCode.INVALID_HEX_ESCAPE, []);
} else {
builder.appendChar(((Character.digit(firstDigit, 16) << 4) + Character.digit(secondDigit, 16)));
@@ -6566,6 +6823,7 @@ class Parser {
} else if (currentChar == 0x75) {
currentIndex++;
if (currentIndex >= length) {
+ // Illegal escape sequence: not enough hex digits
reportError11(ParserErrorCode.INVALID_UNICODE_ESCAPE, []);
return length;
}
@@ -6573,6 +6831,7 @@ class Parser {
if (currentChar == 0x7B) {
currentIndex++;
if (currentIndex >= length) {
+ // Illegal escape sequence: incomplete escape
reportError11(ParserErrorCode.INVALID_UNICODE_ESCAPE, []);
return length;
}
@@ -6581,6 +6840,7 @@ class Parser {
int value = 0;
while (currentChar != 0x7D) {
if (!isHexDigit(currentChar)) {
+ // Illegal escape sequence: invalid hex digit
reportError11(ParserErrorCode.INVALID_UNICODE_ESCAPE, []);
currentIndex++;
while (currentIndex < length && lexeme.codeUnitAt(currentIndex) != 0x7D) {
@@ -6592,18 +6852,21 @@ class Parser {
value = (value << 4) + Character.digit(currentChar, 16);
currentIndex++;
if (currentIndex >= length) {
+ // Illegal escape sequence: incomplete escape
reportError11(ParserErrorCode.INVALID_UNICODE_ESCAPE, []);
return length;
}
currentChar = lexeme.codeUnitAt(currentIndex);
}
if (digitCount < 1 || digitCount > 6) {
+ // Illegal escape sequence: not enough or too many hex digits
reportError11(ParserErrorCode.INVALID_UNICODE_ESCAPE, []);
}
appendScalarValue(builder, lexeme.substring(index, currentIndex + 1), value, index, currentIndex);
return currentIndex + 1;
} else {
if (currentIndex + 3 >= length) {
+ // Illegal escape sequence: not enough hex digits
reportError11(ParserErrorCode.INVALID_UNICODE_ESCAPE, []);
return length;
}
@@ -6612,6 +6875,7 @@ class Parser {
int thirdDigit = lexeme.codeUnitAt(currentIndex + 2);
int fourthDigit = lexeme.codeUnitAt(currentIndex + 3);
if (!isHexDigit(firstDigit) || !isHexDigit(secondDigit) || !isHexDigit(thirdDigit) || !isHexDigit(fourthDigit)) {
+ // Illegal escape sequence: invalid hex digits
reportError11(ParserErrorCode.INVALID_UNICODE_ESCAPE, []);
} else {
appendScalarValue(builder, lexeme.substring(index, currentIndex + 1), (((((Character.digit(firstDigit, 16) << 4) + Character.digit(secondDigit, 16)) << 4) + Character.digit(thirdDigit, 16)) << 4) + Character.digit(fourthDigit, 16), index, currentIndex + 3);
@@ -8169,6 +8433,9 @@ class ResolutionCopier implements ASTVisitor<bool> {
this._toNode = toNode;
return fromNode.accept(this);
}
+ //
+ // Check for a simple transformation caused by entering a period.
+ //
if (toNode is PrefixedIdentifier) {
SimpleIdentifier prefix = toNode.prefix;
if (fromNode.runtimeType == prefix.runtimeType) {
@@ -8257,6 +8524,8 @@ class ResolutionCopier implements ASTVisitor<bool> {
* AST node (and all of it's children) to a writer.
*/
class ToFormattedSourceVisitor implements ASTVisitor<Object> {
+ static String COMMENTS_KEY = "List of comments before statement";
+
/**
* The writer to which the source is to be written.
*/
@@ -8448,9 +8717,11 @@ class ToFormattedSourceVisitor implements ASTVisitor<Object> {
ScriptTag scriptTag = node.scriptTag;
NodeList<Directive> directives = node.directives;
visit(scriptTag);
+ // directives
String prefix = scriptTag == null ? "" : " ";
visitList7(prefix, directives, "\n");
nl();
+ // declarations
prefix = scriptTag == null && directives.isEmpty ? "" : "\n";
visitList7(prefix, node.declarations, "\n\n");
return null;
@@ -9164,6 +9435,18 @@ class ToFormattedSourceVisitor implements ASTVisitor<Object> {
indent();
}
+ void printLeadingComments(Statement statement) {
+ List<String> comments = statement.getProperty(COMMENTS_KEY) as List<String>;
+ if (comments == null) {
+ return;
+ }
+ for (String comment in comments) {
+ _writer.print(comment);
+ _writer.print("\n");
+ indent();
+ }
+ }
+
/**
* Safely visit the given node.
*
@@ -9269,10 +9552,12 @@ class ToFormattedSourceVisitor implements ASTVisitor<Object> {
if (nodes != null) {
int size = nodes.length;
if (size != 0) {
+ // prefix
_writer.print(prefix);
if (prefix.endsWith("\n")) {
indent();
}
+ // nodes
bool newLineSeparator = separator.endsWith("\n");
for (int i = 0; i < size; i++) {
if (i > 0) {
@@ -9281,8 +9566,13 @@ class ToFormattedSourceVisitor implements ASTVisitor<Object> {
indent();
}
}
- nodes[i].accept(this);
+ ASTNode node = nodes[i];
+ if (node is Statement) {
+ printLeadingComments(node);
+ }
+ node.accept(this);
}
+ // suffix
_writer.print(suffix);
}
}
« no previous file with comments | « pkg/analyzer/lib/src/generated/index.dart ('k') | pkg/analyzer/lib/src/generated/resolver.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698