| Index: pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart
|
| diff --git a/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart b/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart
|
| index 43159a580a7d1846560494432beb8c246720ea9f..a5d7b09fe6e530e0e916e2975d237970694f92e8 100644
|
| --- a/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart
|
| +++ b/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart
|
| @@ -139,16 +139,17 @@ abstract class ArrayBasedScanner extends AbstractScanner {
|
| /**
|
| * Appends a token that begins an end group, represented by [type].
|
| * It handles the group end tokens '}', ')' and ']'. The tokens '>' and
|
| - * '>>' are handled separately bo [appendGt] and [appendGtGt].
|
| + * '>>' are handled separately by [appendGt] and [appendGtGt].
|
| */
|
| int appendEndGroup(TokenType type, int openKind) {
|
| assert(!identical(openKind, LT_TOKEN)); // openKind is < for > and >>
|
| - discardBeginGroupUntil(openKind);
|
| - appendPrecedenceToken(type);
|
| - Token close = tail;
|
| - if (groupingStack.isEmpty) {
|
| + if (!discardBeginGroupUntil(openKind)) {
|
| + // No begin group found. Just continue.
|
| + appendPrecedenceToken(type);
|
| return advance();
|
| }
|
| + appendPrecedenceToken(type);
|
| + Token close = tail;
|
| BeginToken begin = groupingStack.head;
|
| if (!identical(begin.kind, openKind)) {
|
| assert(begin.kind == STRING_INTERPOLATION_TOKEN &&
|
| @@ -166,21 +167,52 @@ abstract class ArrayBasedScanner extends AbstractScanner {
|
| }
|
|
|
| /**
|
| - * Discards begin group tokens until a match with [openKind] is found.
|
| - * This recovers nicely from from a situation like "{[}".
|
| + * If a begin group token matches [openKind],
|
| + * then discard begin group tokens up to that match and return `true`,
|
| + * otherwise return `false`.
|
| + * This recovers nicely from from situations like "{[}" and "{foo());}",
|
| + * but not "foo(() {bar());});
|
| */
|
| - void discardBeginGroupUntil(int openKind) {
|
| - while (!groupingStack.isEmpty) {
|
| + bool discardBeginGroupUntil(int openKind) {
|
| + Link<BeginToken> originalStack = groupingStack;
|
| +
|
| + bool first = true;
|
| + do {
|
| // Don't report unmatched errors for <; it is also the less-than operator.
|
| discardOpenLt();
|
| - if (groupingStack.isEmpty) return;
|
| + if (groupingStack.isEmpty) break; // recover
|
| BeginToken begin = groupingStack.head;
|
| - if (openKind == begin.kind) return;
|
| - if (openKind == OPEN_CURLY_BRACKET_TOKEN &&
|
| - begin.kind == STRING_INTERPOLATION_TOKEN) return;
|
| - unmatchedBeginGroup(begin);
|
| + if (openKind == begin.kind ||
|
| + (openKind == OPEN_CURLY_BRACKET_TOKEN &&
|
| + begin.kind == STRING_INTERPOLATION_TOKEN)) {
|
| + if (first) {
|
| + // If the expected opener has been found on the first pass
|
| + // then no recovery necessary.
|
| + return true;
|
| + }
|
| + break; // recover
|
| + }
|
| + first = false;
|
| groupingStack = groupingStack.tail;
|
| + } while (!groupingStack.isEmpty);
|
| +
|
| + // If the stack does not have any opener of the given type,
|
| + // then return without discarding anything.
|
| + // This recovers nicely from from situations like "{foo());}".
|
| + if (groupingStack.isEmpty) {
|
| + groupingStack = originalStack;
|
| + return false;
|
| + }
|
| +
|
| + // Insert synthetic closers and report errors for any unbalanced openers.
|
| + // This recovers nicely from from situations like "{[}".
|
| + while (!identical(originalStack, groupingStack)) {
|
| + // Don't report unmatched errors for <; it is also the less-than operator.
|
| + if (!identical(groupingStack.head.kind, LT_TOKEN))
|
| + unmatchedBeginGroup(originalStack.head);
|
| + originalStack = originalStack.tail;
|
| }
|
| + return true;
|
| }
|
|
|
| /**
|
|
|