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

Unified Diff: dart/sdk/lib/_internal/compiler/implementation/scanner/listener.dart

Issue 324293002: Improve parser error recovery. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Addressed Johnni's comments Created 6 years, 6 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
Index: dart/sdk/lib/_internal/compiler/implementation/scanner/listener.dart
diff --git a/dart/sdk/lib/_internal/compiler/implementation/scanner/listener.dart b/dart/sdk/lib/_internal/compiler/implementation/scanner/listener.dart
index 5ee68c2747cace5452630c430dc89908d3478fe5..3e4be25ecf12ecfcf78d3faaa50d8050cf8aac6d 100644
--- a/dart/sdk/lib/_internal/compiler/implementation/scanner/listener.dart
+++ b/dart/sdk/lib/_internal/compiler/implementation/scanner/listener.dart
@@ -11,6 +11,9 @@ const bool VERBOSE = false;
* on parser errors.
*/
class Listener {
+ set suppressParseErrors(bool value) {
+ }
+
void beginArguments(Token token) {
}
@@ -768,6 +771,17 @@ class ElementListener extends Listener {
Link<MetadataAnnotation> metadata = const Link<MetadataAnnotation>();
+ /// Records a stack of booleans for each member parsed (a stack is used to
+ /// support nested members which isn't currently possible, but it also serves
+ /// as a simple way to tell we're currently parsing a member). In this case,
+ /// member refers to members of a library or a class (but currently, classes
+ /// themselves are not considered members). If the top of the stack
+ /// (memberErrors.head) is true, the current member has already reported at
+ /// least one parse error.
+ Link<bool> memberErrors = const Link<bool>();
+
+ bool suppressParseErrors = false;
+
ElementListener(DiagnosticListener listener,
this.compilationUnitElement,
this.idGenerator)
@@ -775,6 +789,10 @@ class ElementListener extends Listener {
stringValidator = new StringValidator(listener),
interpolationScope = const Link<StringQuoting>();
+ bool get currentMemberHasParseError {
+ return !memberErrors.isEmpty && memberErrors.head;
+ }
+
void pushQuoting(StringQuoting quoting) {
interpolationScope = interpolationScope.prepend(quoting);
}
@@ -954,6 +972,8 @@ class ElementListener extends Listener {
}
void endTopLevelMethod(Token beginToken, Token getOrSet, Token endToken) {
+ bool hasParseError = currentMemberHasParseError;
+ memberErrors = memberErrors.tail;
Identifier name = popNode();
TypeAnnotation type = popNode();
Modifiers modifiers = popNode();
@@ -965,12 +985,16 @@ class ElementListener extends Listener {
} else if (identical(getOrSet.stringValue, 'set')) {
kind = ElementKind.SETTER;
}
- pushElement(new PartialFunctionElement(name.source, beginToken, getOrSet,
- endToken, kind, modifiers,
- compilationUnitElement, false));
+ PartialFunctionElement element = new PartialFunctionElement(
+ name.source, beginToken, getOrSet, endToken, kind, modifiers,
+ compilationUnitElement, false);
+ element.hasParseError = hasParseError;
+ pushElement(element);
}
void endTopLevelFields(int count, Token beginToken, Token endToken) {
+ bool hasParseError = currentMemberHasParseError;
+ memberErrors = memberErrors.tail;
void buildFieldElement(Identifier name, VariableList fields) {
pushElement(
new FieldElementX(name, compilationUnitElement, fields));
@@ -980,7 +1004,7 @@ class ElementListener extends Listener {
Modifiers modifiers = popNode();
buildFieldElements(modifiers, variables, compilationUnitElement,
buildFieldElement,
- beginToken, endToken);
+ beginToken, endToken, hasParseError);
}
void buildFieldElements(Modifiers modifiers,
@@ -988,9 +1012,10 @@ class ElementListener extends Listener {
Element enclosingElement,
void buildFieldElement(Identifier name,
VariableList fields),
- Token beginToken, Token endToken) {
+ Token beginToken, Token endToken,
+ bool hasParseError) {
VariableList fields =
- new PartialFieldList(beginToken, endToken, modifiers);
+ new PartialFieldList(beginToken, endToken, modifiers, hasParseError);
for (Link<Node> variableNodes = variables.nodes;
!variableNodes.isEmpty;
variableNodes = variableNodes.tail) {
@@ -1067,13 +1092,43 @@ class ElementListener extends Listener {
Token expected(String string, Token token) {
if (token is ErrorToken) {
reportErrorToken(token);
+ } else if (identical(';', string)) {
+ // When a semicolon is missing, it often leads to an error on the
+ // following line. So we try to find the token preceding the semicolon
+ // and report that something is missing *after* it.
+ Token preceding = findPrecedingToken(token);
+ if (preceding == token) {
+ reportError(
+ token, MessageKind.MISSING_TOKEN_BEFORE_THIS, {'token': string});
+ } else {
+ reportError(
+ preceding, MessageKind.MISSING_TOKEN_AFTER_THIS, {'token': string});
+ }
+ return token;
} else {
reportFatalError(
- token, "Expected '$string', but got '${token.value}'.");
+ token,
+ MessageKind.MISSING_TOKEN_BEFORE_THIS.message(
+ {'token': string}, true).toString());
}
return skipToEof(token);
}
+ /// Finds the preceding token via the begin token of the last AST node pushed
+ /// on the [nodes] stack.
+ Token findPrecedingToken(Token token) {
+ if (!nodes.isEmpty && nodes.head != null) {
+ Token current = nodes.head.getBeginToken();
+ while (current.kind != EOF_TOKEN && current.next != token) {
+ current = current.next;
+ }
+ if (current.kind != EOF_TOKEN) {
+ return current;
+ }
+ }
+ return token;
+ }
+
Token expectedIdentifier(Token token) {
if (token is KeywordToken) {
reportError(
@@ -1313,6 +1368,30 @@ class ElementListener extends Listener {
pushNode(accumulator);
}
+ void beginMember(Token token) {
+ memberErrors = memberErrors.prepend(false);
+ }
+
+ void beginTopLevelMember(Token token) {
+ beginMember(token);
+ }
+
+ void endFields(fieldCount, start, token) {
+ memberErrors = memberErrors.tail;
+ }
+
+ void endMethod(getOrSet, start, token) {
+ memberErrors = memberErrors.tail;
+ }
+
+ void beginFactoryMethod(Token token) {
+ memberErrors = memberErrors.prepend(false);
+ }
+
+ void endFactoryMethod(Token beginToken, Token endToken) {
+ memberErrors = memberErrors.tail;
+ }
+
void reportFatalError(Spannable spannable,
String message) {
listener.reportFatalError(
@@ -1322,14 +1401,36 @@ class ElementListener extends Listener {
void reportError(Spannable spannable,
MessageKind errorCode,
[Map arguments = const {}]) {
+ if (currentMemberHasParseError) return; // Error already reported.
+ if (suppressParseErrors) return;
+ if (!memberErrors.isEmpty) {
+ memberErrors = memberErrors.tail.prepend(true);
+ }
listener.reportError(spannable, errorCode, arguments);
}
}
class NodeListener extends ElementListener {
- NodeListener(DiagnosticListener listener, CompilationUnitElement element)
+ final bool throwOnFatalError;
+
+ NodeListener(
+ DiagnosticListener listener,
+ CompilationUnitElement element,
+ {bool this.throwOnFatalError: false})
: super(listener, element, null);
+ void reportFatalError(Spannable spannable,
+ String message) {
+ if (throwOnFatalError) {
+ if (!currentMemberHasParseError && !suppressParseErrors) {
+ reportError(spannable, MessageKind.GENERIC, {'text': message});
+ }
+ throw new ParserError(message);
+ } else {
+ super.reportFatalError(spannable, message);
+ }
+ }
+
void addLibraryTag(LibraryTag tag) {
pushNode(tag);
}
@@ -1497,12 +1598,13 @@ class NodeListener extends ElementListener {
if (identical(token.stringValue, 'native')) {
return native.handleNativeFunctionBody(this, token);
} else if (token is ErrorToken) {
+ pushNode(null);
reportErrorToken(token);
} else {
reportFatalError(token,
"Expected a function body, but got '${token.value}'.");
- return skipToEof(token);
}
+ return skipToEof(token);
}
Token expectedClassBody(Token token) {
@@ -1667,7 +1769,8 @@ class NodeListener extends ElementListener {
void endInitializer(Token assignmentOperator) {
Expression initializer = popNode();
- NodeList arguments = new NodeList.singleton(initializer);
+ NodeList arguments =
+ initializer == null ? null : new NodeList.singleton(initializer);
Expression name = popNode();
Operator op = new Operator(assignmentOperator);
pushNode(new SendSet(null, name, op, arguments));
@@ -1938,6 +2041,7 @@ class NodeListener extends ElementListener {
}
void endFactoryMethod(Token beginToken, Token endToken) {
+ super.endFactoryMethod(beginToken, endToken);
Statement body = popNode();
NodeList formals = popNode();
Node name = popNode();
@@ -2072,6 +2176,12 @@ class NodeListener extends ElementListener {
}
}
+abstract class PartialElement implements Element {
+ bool hasParseError = false;
+
+ bool get isErroneous => hasParseError;
+}
+
abstract class PartialFunctionMixin implements FunctionElement {
FunctionExpression cachedNode;
Modifiers get modifiers;
@@ -2111,7 +2221,7 @@ abstract class PartialFunctionMixin implements FunctionElement {
p.parseFunction(beginToken, getOrSet);
}
}
- cachedNode = parse(listener, compilationUnit, parseFunction);
+ cachedNode = parse(listener, this, parseFunction);
return cachedNode;
}
@@ -2119,7 +2229,7 @@ abstract class PartialFunctionMixin implements FunctionElement {
}
class PartialFunctionElement extends FunctionElementX
- with PartialFunctionMixin {
+ with PartialElement, PartialFunctionMixin {
PartialFunctionElement(String name,
Token beginToken,
Token getOrSet,
@@ -2134,7 +2244,7 @@ class PartialFunctionElement extends FunctionElementX
}
class PartialConstructorElement extends ConstructorElementX
- with PartialFunctionMixin {
+ with PartialElement, PartialFunctionMixin {
PartialConstructorElement(String name,
Token beginToken,
Token endToken,
@@ -2149,23 +2259,32 @@ class PartialConstructorElement extends ConstructorElementX
class PartialFieldList extends VariableList {
final Token beginToken;
final Token endToken;
+ final bool hasParseError;
- PartialFieldList(Token this.beginToken,
- Token this.endToken,
- Modifiers modifiers)
+ PartialFieldList(this.beginToken,
+ this.endToken,
+ Modifiers modifiers,
+ this.hasParseError)
: super(modifiers);
VariableDefinitions parseNode(Element element, DiagnosticListener listener) {
if (definitions != null) return definitions;
listener.withCurrentElement(element, () {
- definitions = parse(listener,
- element.compilationUnit,
- (p) => p.parseVariablesDeclaration(beginToken));
-
- if (!definitions.modifiers.isVar &&
+ definitions = parse(
+ listener, element,
+ (Parser parser) {
+ if (hasParseError) {
+ parser.listener.suppressParseErrors = true;
+ }
+ return parser.parseMember(beginToken);
+ });
+
+ if (!hasParseError &&
+ !definitions.modifiers.isVar &&
!definitions.modifiers.isFinal &&
!definitions.modifiers.isConst &&
- definitions.type == null) {
+ definitions.type == null &&
+ !definitions.isErroneous) {
listener.reportError(
definitions,
MessageKind.GENERIC,
@@ -2201,7 +2320,7 @@ class PartialTypedefElement extends TypedefElementX {
Node parseNode(DiagnosticListener listener) {
if (cachedNode != null) return cachedNode;
cachedNode = parse(listener,
- compilationUnit,
+ this,
(p) => p.parseTopLevelDeclaration(token));
return cachedNode;
}
@@ -2230,7 +2349,7 @@ class PartialMetadataAnnotation extends MetadataAnnotationX {
Node parseNode(DiagnosticListener listener) {
if (cachedNode != null) return cachedNode;
Metadata metadata = parse(listener,
- annotatedElement.compilationUnit,
+ annotatedElement,
(p) => p.parseMetadata(beginToken));
cachedNode = metadata.expression;
return cachedNode;
@@ -2238,10 +2357,20 @@ class PartialMetadataAnnotation extends MetadataAnnotationX {
}
Node parse(DiagnosticListener diagnosticListener,
- CompilationUnitElement element,
+ Element element,
doParse(Parser parser)) {
- NodeListener listener = new NodeListener(diagnosticListener, element);
- doParse(new Parser(listener));
+ CompilationUnitElement unit = element.compilationUnit;
+ NodeListener listener =
+ new NodeListener(diagnosticListener, unit, throwOnFatalError: true);
+ listener.memberErrors = listener.memberErrors.prepend(false);
+ try {
+ doParse(new Parser(listener));
+ } on ParserError catch (e) {
+ if (element is PartialElement) {
+ element.hasParseError = true;
+ }
+ return new ErrorNode(element.position, e.reason);
+ }
Node node = listener.popNode();
assert(listener.nodes.isEmpty);
return node;

Powered by Google App Engine
This is Rietveld 408576698