[parser] More 'typing' parser tests (with missing end-braces)

Fixes issue when "good" braces are on the same line but the (e.g.) `if`
is on another line.

Change-Id: I3e78a4c557f596eb62b51546927ee0076c8b1a82
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/417163
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/scanner/abstract_scanner.dart b/pkg/_fe_analyzer_shared/lib/src/scanner/abstract_scanner.dart
index 2cefe49..151ee55 100644
--- a/pkg/_fe_analyzer_shared/lib/src/scanner/abstract_scanner.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/scanner/abstract_scanner.dart
@@ -356,6 +356,22 @@
     appendToken(new KeywordToken(keyword, tokenStart, comments));
   }
 
+  int _getLineOf(Token token) {
+    if (lineStarts.isEmpty) return -1;
+    final int offset = token.offset;
+    int low = 0, high = lineStarts.length - 1;
+    while (low < high) {
+      int mid = high - ((high - low) >> 1); // Get middle, rounding up.
+      int pivot = lineStarts[mid];
+      if (pivot <= offset) {
+        low = mid;
+      } else {
+        high = mid - 1;
+      }
+    }
+    return low;
+  }
+
   /// Find the indentation of the logical line of [token].
   ///
   /// By logical take this as an example:
@@ -415,18 +431,7 @@
     }
 
     // Now find the line of [token].
-    final int offset = token.offset;
-    int low = 0, high = lineStarts.length - 1;
-    while (low < high) {
-      int mid = high - ((high - low) >> 1); // Get middle, rounding up.
-      int pivot = lineStarts[mid];
-      if (pivot <= offset) {
-        low = mid;
-      } else {
-        high = mid - 1;
-      }
-    }
-    final int lineIndex = low;
+    final int lineIndex = _getLineOf(token);
     if (lineIndex == 0) {
       // On first line.
       return tokens.next?.charOffset ?? -1;
@@ -463,10 +468,12 @@
     Token? lastMismatch;
     while (next != null && !next.isEof) {
       if (next.isA(TokenType.OPEN_CURLY_BRACKET)) {
-        int indentOfNext = _spacesAtStartOfLogicalLineOf(next);
-        if (indentOfNext >= 0 &&
-            indentOfNext != _spacesAtStartOfLogicalLineOf(next.endGroup!)) {
-          lastMismatch = next;
+        if (_getLineOf(next) != _getLineOf(next.endGroup!)) {
+          int indentOfNext = _spacesAtStartOfLogicalLineOf(next);
+          if (indentOfNext >= 0 &&
+              indentOfNext != _spacesAtStartOfLogicalLineOf(next.endGroup!)) {
+            lastMismatch = next;
+          }
         }
       }
       next = next.next;
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart
new file mode 100644
index 0000000..7d5c768
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart
@@ -0,0 +1,25 @@
+// Without specific missing-brace recovery, we'll end up with:
+// * Class Foo, member Foo.foo_method1, Foo.toplevel_method1, Foo.toplevel_method2
+// With specific missing-brace recovery, we'll end up with the correct:
+// * Class Foo, members Foo.foo_method1, Foo.foo_method2
+// * Top-level members toplevel_method1, toplevel_method2
+
+class Foo {
+  void foo_method1(dynamic input) {
+    if (1 + 1 == 2) {
+      foo_method2({1, 2, 3 /* missing: }*/)
+    // missing: }
+  }
+
+  void foo_method2(dynamic whatever) {
+    // bla
+  }
+}
+
+void toplevel_method1() {
+  // bla
+}
+
+void toplevel_method2() {
+  // bla
+}
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.expect b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.expect
new file mode 100644
index 0000000..ac17730
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.expect
@@ -0,0 +1,137 @@
+Problems reported:
+
+parser/error_recovery/with_outline/typing_14:10:43: Expected ';' after this.
+      foo_method2({1, 2, 3 /* missing: }*/)
+                                          ^
+
+parser/error_recovery/with_outline/typing_14:10:19: Can't find '}' to match '{'.
+      foo_method2({1, 2, 3 /* missing: }*/)
+                  ^
+
+parser/error_recovery/with_outline/typing_14:9:21: Can't find '}' to match '{'.
+    if (1 + 1 == 2) {
+                    ^
+
+beginCompilationUnit(class)
+  beginMetadataStar(class)
+  endMetadataStar(0)
+  beginClassOrMixinOrNamedMixinApplicationPrelude(class)
+    handleIdentifier(Foo, classOrMixinDeclaration)
+    handleNoTypeVariables({)
+    beginClassDeclaration(class, null, null, null, null, null, null, null, null, Foo)
+      handleNoType(Foo)
+      handleClassExtends(null, 1)
+      handleClassNoWithClause()
+      handleImplements(null, 0)
+      handleClassHeader(class, class, null)
+      beginClassOrMixinOrExtensionBody(DeclarationKind.Class, {)
+        beginMetadataStar(void)
+        endMetadataStar(0)
+        beginMember()
+          beginMethod(DeclarationKind.Class, null, null, null, null, null, null, foo_method1, Foo)
+            handleVoidKeyword(void)
+            handleIdentifier(foo_method1, methodDeclaration)
+            handleNoTypeVariables(()
+            beginFormalParameters((, MemberKind.NonStaticMethod)
+              beginMetadataStar(dynamic)
+              endMetadataStar(0)
+              beginFormalParameter(dynamic, MemberKind.NonStaticMethod, null, null, null)
+                handleIdentifier(dynamic, typeReference)
+                handleNoTypeArguments(input)
+                handleType(dynamic, null)
+                handleIdentifier(input, formalParameterDeclaration)
+                handleFormalParameterWithoutValue())
+              endFormalParameter(null, null, null, input, null, null, FormalParameterKind.requiredPositional, MemberKind.NonStaticMethod)
+            endFormalParameters(1, (, ), MemberKind.NonStaticMethod)
+            handleNoInitializers()
+            handleAsyncModifier(null, null)
+            beginBlockFunctionBody({)
+              beginIfStatement(if)
+                handleLiteralInt(1)
+                beginBinaryExpression(+)
+                  handleLiteralInt(1)
+                endBinaryExpression(+, 1)
+                beginBinaryExpression(==)
+                  handleLiteralInt(2)
+                endBinaryExpression(==, 2)
+                handleParenthesizedCondition((, null, null)
+                beginThenStatement({)
+                  beginBlock({, BlockKind(statement))
+                    handleIdentifier(foo_method2, expression)
+                    handleNoTypeArguments(()
+                    beginArguments(()
+                      handleNoTypeArguments({)
+                      handleLiteralInt(1)
+                      handleLiteralInt(2)
+                      handleLiteralInt(3)
+                      handleLiteralSetOrMap(3, {, null, }, true)
+                    endArguments(1, (, ))
+                    handleSend(foo_method2, ))
+                    handleRecoverableError(Message[ExpectedAfterButGot, Expected ';' after this., null, {string: ;}], ), ))
+                    handleExpressionStatement(foo_method2, ;)
+                  endBlock(1, {, }, BlockKind(statement))
+                endThenStatement({, })
+              endIfStatement(if, null, })
+            endBlockFunctionBody(1, {, })
+          endClassMethod(null, void, (, null, })
+        endMember()
+        beginMetadataStar(void)
+        endMetadataStar(0)
+        beginMember()
+          beginMethod(DeclarationKind.Class, null, null, null, null, null, null, foo_method2, Foo)
+            handleVoidKeyword(void)
+            handleIdentifier(foo_method2, methodDeclaration)
+            handleNoTypeVariables(()
+            beginFormalParameters((, MemberKind.NonStaticMethod)
+              beginMetadataStar(dynamic)
+              endMetadataStar(0)
+              beginFormalParameter(dynamic, MemberKind.NonStaticMethod, null, null, null)
+                handleIdentifier(dynamic, typeReference)
+                handleNoTypeArguments(whatever)
+                handleType(dynamic, null)
+                handleIdentifier(whatever, formalParameterDeclaration)
+                handleFormalParameterWithoutValue())
+              endFormalParameter(null, null, null, whatever, null, null, FormalParameterKind.requiredPositional, MemberKind.NonStaticMethod)
+            endFormalParameters(1, (, ), MemberKind.NonStaticMethod)
+            handleNoInitializers()
+            handleAsyncModifier(null, null)
+            beginBlockFunctionBody({)
+            endBlockFunctionBody(0, {, })
+          endClassMethod(null, void, (, null, })
+        endMember()
+      endClassOrMixinOrExtensionBody(DeclarationKind.Class, 2, {, })
+    endClassDeclaration(class, })
+  endTopLevelDeclaration(})
+  beginMetadataStar(void)
+  endMetadataStar(0)
+  beginTopLevelMember(void)
+    beginTopLevelMethod(}, null, null)
+      handleVoidKeyword(void)
+      handleIdentifier(toplevel_method1, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginBlockFunctionBody({)
+      endBlockFunctionBody(0, {, })
+    endTopLevelMethod(void, null, })
+  endTopLevelDeclaration(})
+  beginMetadataStar(void)
+  endMetadataStar(0)
+  beginTopLevelMember(void)
+    beginTopLevelMethod(}, null, null)
+      handleVoidKeyword(void)
+      handleIdentifier(toplevel_method2, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginBlockFunctionBody({)
+      endBlockFunctionBody(0, {, })
+    endTopLevelMethod(void, null, })
+  endTopLevelDeclaration(})
+  handleErrorToken(UnmatchedToken({))
+  handleRecoverableError(Message[UnmatchedToken, Can't find '}' to match '{'., null, {string: }, lexeme: {}], UnmatchedToken({), UnmatchedToken({))
+  handleErrorToken(UnmatchedToken({))
+  handleRecoverableError(Message[UnmatchedToken, Can't find '}' to match '{'., null, {string: }, lexeme: {}], UnmatchedToken({), UnmatchedToken({))
+endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.intertwined.expect b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.intertwined.expect
new file mode 100644
index 0000000..ab558dd
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.intertwined.expect
@@ -0,0 +1,269 @@
+parseUnit(UnmatchedToken({))
+  skipErrorTokens(UnmatchedToken({))
+  listener: beginCompilationUnit(class)
+  syntheticPreviousToken(class)
+  parseTopLevelDeclarationImpl(UnmatchedToken({), DirectiveContext(DirectiveState.Unknown))
+    parseMetadataStar(UnmatchedToken({))
+      listener: beginMetadataStar(class)
+      listener: endMetadataStar(0)
+    parseTopLevelKeywordDeclaration(class, UnmatchedToken({), class, null, null, null, null, DirectiveContext(DirectiveState.Unknown))
+      parseClassOrNamedMixinApplication(class, null, null, null, null, null, null, null, null, class)
+        listener: beginClassOrMixinOrNamedMixinApplicationPrelude(class)
+        ensureIdentifier(class, classOrMixinDeclaration)
+          listener: handleIdentifier(Foo, classOrMixinDeclaration)
+        listener: handleNoTypeVariables({)
+        listener: beginClassDeclaration(class, null, null, null, null, null, null, null, null, Foo)
+        parseClass(Foo, class, class, Foo)
+          parseClassHeaderOpt(Foo, class, class)
+            parseClassExtendsOpt(Foo, DeclarationHeaderKind.Class)
+              listener: handleNoType(Foo)
+              listener: handleClassExtends(null, 1)
+            parseClassWithClauseOpt(Foo)
+              listener: handleClassNoWithClause()
+            parseClassOrMixinOrEnumImplementsOpt(Foo)
+              listener: handleImplements(null, 0)
+            listener: handleClassHeader(class, class, null)
+          parseClassOrMixinOrExtensionBody(Foo, DeclarationKind.Class, Foo)
+            listener: beginClassOrMixinOrExtensionBody(DeclarationKind.Class, {)
+            notEofOrValue(}, void)
+            parseClassOrMixinOrExtensionOrEnumMemberImpl({, DeclarationKind.Class, Foo)
+              parseMetadataStar({)
+                listener: beginMetadataStar(void)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseMethod({, null, null, null, null, null, null, null, {, VoidType(), null, foo_method1, DeclarationKind.Class, Foo, false)
+                listener: beginMethod(DeclarationKind.Class, null, null, null, null, null, null, foo_method1, Foo)
+                listener: handleVoidKeyword(void)
+                ensureIdentifierPotentiallyRecovered(void, methodDeclaration, false)
+                  listener: handleIdentifier(foo_method1, methodDeclaration)
+                parseQualifiedRestOpt(foo_method1, methodDeclarationContinuation)
+                parseMethodTypeVar(foo_method1)
+                  listener: handleNoTypeVariables(()
+                parseGetterOrFormalParameters(foo_method1, foo_method1, false, MemberKind.NonStaticMethod)
+                  parseFormalParameters(foo_method1, MemberKind.NonStaticMethod)
+                    parseFormalParametersRest((, MemberKind.NonStaticMethod)
+                      listener: beginFormalParameters((, MemberKind.NonStaticMethod)
+                      parseFormalParameter((, FormalParameterKind.requiredPositional, MemberKind.NonStaticMethod)
+                        parseMetadataStar(()
+                          listener: beginMetadataStar(dynamic)
+                          listener: endMetadataStar(0)
+                        listener: beginFormalParameter(dynamic, MemberKind.NonStaticMethod, null, null, null)
+                        listener: handleIdentifier(dynamic, typeReference)
+                        listener: handleNoTypeArguments(input)
+                        listener: handleType(dynamic, null)
+                        ensureIdentifier(dynamic, formalParameterDeclaration)
+                          listener: handleIdentifier(input, formalParameterDeclaration)
+                        listener: handleFormalParameterWithoutValue())
+                        listener: endFormalParameter(null, null, null, input, null, null, FormalParameterKind.requiredPositional, MemberKind.NonStaticMethod)
+                      listener: endFormalParameters(1, (, ), MemberKind.NonStaticMethod)
+                parseInitializersOpt())
+                  listener: handleNoInitializers()
+                parseAsyncModifierOpt())
+                  listener: handleAsyncModifier(null, null)
+                  inPlainSync()
+                inPlainSync()
+                parseFunctionBody(), false, true)
+                  listener: beginBlockFunctionBody({)
+                  notEofOrValue(}, if)
+                  parseStatement({)
+                    parseStatementX({)
+                      parseIfStatement({)
+                        listener: beginIfStatement(if)
+                        ensureParenthesizedCondition(if, allowCase: false)
+                          parseExpressionInParenthesisRest((, allowCase: false)
+                            parseExpression(()
+                              parsePrecedenceExpression((, 1, true, ConstantPatternContext.none)
+                                parseUnaryExpression((, true, ConstantPatternContext.none)
+                                  parsePrimary((, expression, ConstantPatternContext.none)
+                                    parseLiteralInt(()
+                                      listener: handleLiteralInt(1)
+                                listener: beginBinaryExpression(+)
+                                parsePrecedenceExpression(+, 14, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(+, true, ConstantPatternContext.none)
+                                    parsePrimary(+, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(+)
+                                        listener: handleLiteralInt(1)
+                                listener: endBinaryExpression(+, 1)
+                                listener: beginBinaryExpression(==)
+                                parsePrecedenceExpression(==, 8, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(==, true, ConstantPatternContext.none)
+                                    parsePrimary(==, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(==)
+                                        listener: handleLiteralInt(2)
+                                listener: endBinaryExpression(==, 2)
+                            ensureCloseParen(2, ()
+                            listener: handleParenthesizedCondition((, null, null)
+                        listener: beginThenStatement({)
+                        parseStatement())
+                          parseStatementX())
+                            parseBlock(), BlockKind(statement))
+                              ensureBlock(), BlockKind(statement))
+                              listener: beginBlock({, BlockKind(statement))
+                              notEofOrValue(}, foo_method2)
+                              parseStatement({)
+                                parseStatementX({)
+                                  parseExpressionStatementOrDeclarationAfterModifiers({, {, null, null, null, null)
+                                    looksLikeLocalFunction(foo_method2)
+                                    parseExpressionStatement({)
+                                      parseExpression({)
+                                        parsePrecedenceExpression({, 1, true, ConstantPatternContext.none)
+                                          parseUnaryExpression({, true, ConstantPatternContext.none)
+                                            parsePrimary({, expression, ConstantPatternContext.none)
+                                              parseSendOrFunctionLiteral({, expression, ConstantPatternContext.none)
+                                                looksLikeFunctionBody(})
+                                                parseSend({, expression, ConstantPatternContext.none)
+                                                  isNextIdentifier({)
+                                                  ensureIdentifier({, expression)
+                                                    listener: handleIdentifier(foo_method2, expression)
+                                                  listener: handleNoTypeArguments(()
+                                                  parseArgumentsOpt(foo_method2)
+                                                    parseArguments(foo_method2)
+                                                      parseArgumentsRest(()
+                                                        listener: beginArguments(()
+                                                        parseExpression(()
+                                                          parsePrecedenceExpression((, 1, true, ConstantPatternContext.none)
+                                                            parseUnaryExpression((, true, ConstantPatternContext.none)
+                                                              parsePrimary((, expression, ConstantPatternContext.none)
+                                                                listener: handleNoTypeArguments({)
+                                                                parseLiteralSetOrMapSuffix((, null)
+                                                                  parseExpression({)
+                                                                    parsePrecedenceExpression({, 1, true, ConstantPatternContext.none)
+                                                                      parseUnaryExpression({, true, ConstantPatternContext.none)
+                                                                        parsePrimary({, expression, ConstantPatternContext.none)
+                                                                          parseLiteralInt({)
+                                                                            listener: handleLiteralInt(1)
+                                                                  parseExpression(,)
+                                                                    parsePrecedenceExpression(,, 1, true, ConstantPatternContext.none)
+                                                                      parseUnaryExpression(,, true, ConstantPatternContext.none)
+                                                                        parsePrimary(,, expression, ConstantPatternContext.none)
+                                                                          parseLiteralInt(,)
+                                                                            listener: handleLiteralInt(2)
+                                                                  parseExpression(,)
+                                                                    parsePrecedenceExpression(,, 1, true, ConstantPatternContext.none)
+                                                                      parseUnaryExpression(,, true, ConstantPatternContext.none)
+                                                                        parsePrimary(,, expression, ConstantPatternContext.none)
+                                                                          parseLiteralInt(,)
+                                                                            listener: handleLiteralInt(3)
+                                                                  listener: handleLiteralSetOrMap(3, {, null, }, true)
+                                                        listener: endArguments(1, (, ))
+                                                  listener: handleSend(foo_method2, ))
+                                      ensureSemicolon())
+                                        reportRecoverableError(), Message[ExpectedAfterButGot, Expected ';' after this., null, {string: ;}])
+                                          listener: handleRecoverableError(Message[ExpectedAfterButGot, Expected ';' after this., null, {string: ;}], ), ))
+                                        rewriter()
+                                      listener: handleExpressionStatement(foo_method2, ;)
+                              notEofOrValue(}, })
+                              listener: endBlock(1, {, }, BlockKind(statement))
+                        listener: endThenStatement({, })
+                        listener: endIfStatement(if, null, })
+                  notEofOrValue(}, })
+                  listener: endBlockFunctionBody(1, {, })
+                listener: endClassMethod(null, void, (, null, })
+              listener: endMember()
+            notEofOrValue(}, void)
+            parseClassOrMixinOrExtensionOrEnumMemberImpl(}, DeclarationKind.Class, Foo)
+              parseMetadataStar(})
+                listener: beginMetadataStar(void)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseMethod(}, null, null, null, null, null, null, null, }, VoidType(), null, foo_method2, DeclarationKind.Class, Foo, false)
+                listener: beginMethod(DeclarationKind.Class, null, null, null, null, null, null, foo_method2, Foo)
+                listener: handleVoidKeyword(void)
+                ensureIdentifierPotentiallyRecovered(void, methodDeclaration, false)
+                  listener: handleIdentifier(foo_method2, methodDeclaration)
+                parseQualifiedRestOpt(foo_method2, methodDeclarationContinuation)
+                parseMethodTypeVar(foo_method2)
+                  listener: handleNoTypeVariables(()
+                parseGetterOrFormalParameters(foo_method2, foo_method2, false, MemberKind.NonStaticMethod)
+                  parseFormalParameters(foo_method2, MemberKind.NonStaticMethod)
+                    parseFormalParametersRest((, MemberKind.NonStaticMethod)
+                      listener: beginFormalParameters((, MemberKind.NonStaticMethod)
+                      parseFormalParameter((, FormalParameterKind.requiredPositional, MemberKind.NonStaticMethod)
+                        parseMetadataStar(()
+                          listener: beginMetadataStar(dynamic)
+                          listener: endMetadataStar(0)
+                        listener: beginFormalParameter(dynamic, MemberKind.NonStaticMethod, null, null, null)
+                        listener: handleIdentifier(dynamic, typeReference)
+                        listener: handleNoTypeArguments(whatever)
+                        listener: handleType(dynamic, null)
+                        ensureIdentifier(dynamic, formalParameterDeclaration)
+                          listener: handleIdentifier(whatever, formalParameterDeclaration)
+                        listener: handleFormalParameterWithoutValue())
+                        listener: endFormalParameter(null, null, null, whatever, null, null, FormalParameterKind.requiredPositional, MemberKind.NonStaticMethod)
+                      listener: endFormalParameters(1, (, ), MemberKind.NonStaticMethod)
+                parseInitializersOpt())
+                  listener: handleNoInitializers()
+                parseAsyncModifierOpt())
+                  listener: handleAsyncModifier(null, null)
+                  inPlainSync()
+                inPlainSync()
+                parseFunctionBody(), false, true)
+                  listener: beginBlockFunctionBody({)
+                  notEofOrValue(}, })
+                  listener: endBlockFunctionBody(0, {, })
+                listener: endClassMethod(null, void, (, null, })
+              listener: endMember()
+            notEofOrValue(}, })
+            listener: endClassOrMixinOrExtensionBody(DeclarationKind.Class, 2, {, })
+          listener: endClassDeclaration(class, })
+  listener: endTopLevelDeclaration(})
+  parseTopLevelDeclarationImpl(}, DirectiveContext(DirectiveState.Declarations))
+    parseMetadataStar(})
+      listener: beginMetadataStar(void)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(})
+      listener: beginTopLevelMember(void)
+      parseTopLevelMethod(}, null, null, }, VoidType(), null, toplevel_method1, false)
+        listener: beginTopLevelMethod(}, null, null)
+        listener: handleVoidKeyword(void)
+        ensureIdentifierPotentiallyRecovered(void, topLevelFunctionDeclaration, false)
+          listener: handleIdentifier(toplevel_method1, topLevelFunctionDeclaration)
+        parseMethodTypeVar(toplevel_method1)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(toplevel_method1, toplevel_method1, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(toplevel_method1, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          listener: beginBlockFunctionBody({)
+          notEofOrValue(}, })
+          listener: endBlockFunctionBody(0, {, })
+        listener: endTopLevelMethod(void, null, })
+  listener: endTopLevelDeclaration(})
+  parseTopLevelDeclarationImpl(}, DirectiveContext(DirectiveState.Declarations))
+    parseMetadataStar(})
+      listener: beginMetadataStar(void)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(})
+      listener: beginTopLevelMember(void)
+      parseTopLevelMethod(}, null, null, }, VoidType(), null, toplevel_method2, false)
+        listener: beginTopLevelMethod(}, null, null)
+        listener: handleVoidKeyword(void)
+        ensureIdentifierPotentiallyRecovered(void, topLevelFunctionDeclaration, false)
+          listener: handleIdentifier(toplevel_method2, topLevelFunctionDeclaration)
+        parseMethodTypeVar(toplevel_method2)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(toplevel_method2, toplevel_method2, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(toplevel_method2, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          listener: beginBlockFunctionBody({)
+          notEofOrValue(}, })
+          listener: endBlockFunctionBody(0, {, })
+        listener: endTopLevelMethod(void, null, })
+  listener: endTopLevelDeclaration(})
+  reportAllErrorTokens(UnmatchedToken({))
+    listener: handleErrorToken(UnmatchedToken({))
+    listener: handleRecoverableError(Message[UnmatchedToken, Can't find '}' to match '{'., null, {string: }, lexeme: {}], UnmatchedToken({), UnmatchedToken({))
+    listener: handleErrorToken(UnmatchedToken({))
+    listener: handleRecoverableError(Message[UnmatchedToken, Can't find '}' to match '{'., null, {string: }, lexeme: {}], UnmatchedToken({), UnmatchedToken({))
+  listener: endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.outline.expect b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.outline.expect
new file mode 100644
index 0000000..96af17b
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.outline.expect
@@ -0,0 +1,5 @@
+Class: Foo
+Class method: Foo.foo_method1
+Class method: Foo.foo_method2
+Top-level method: toplevel_method1
+Top-level method: toplevel_method2
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.parser.expect b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.parser.expect
new file mode 100644
index 0000000..8bd759f
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.parser.expect
@@ -0,0 +1,43 @@
+NOTICE: Stream was rewritten by parser!
+
+class Foo {
+void foo_method1(dynamic input) {
+if (1 + 1 == 2) {
+foo_method2({1, 2, 3 })
+
+;}}
+
+void foo_method2(dynamic whatever) {
+
+}
+}
+
+void toplevel_method1() {
+
+}
+
+void toplevel_method2() {
+
+}
+
+
+[UnmatchedToken][UnmatchedToken]class[KeywordToken] Foo[StringToken] {[BeginToken]
+void[KeywordToken] foo_method1[StringToken]([BeginToken]dynamic[KeywordToken] input[StringToken])[SimpleToken] {[BeginToken]
+if[KeywordToken] ([BeginToken]1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken])[SimpleToken] {[BeginToken]
+foo_method2[StringToken]([BeginToken]{[BeginToken]1[StringToken],[SimpleToken] 2[StringToken],[SimpleToken] 3[StringToken] }[SyntheticToken])[SimpleToken]
+
+;[SyntheticToken]}[SyntheticToken]}[SimpleToken]
+
+void[KeywordToken] foo_method2[StringToken]([BeginToken]dynamic[KeywordToken] whatever[StringToken])[SimpleToken] {[BeginToken]
+
+}[SimpleToken]
+}[SimpleToken]
+
+void[KeywordToken] toplevel_method1[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+
+}[SimpleToken]
+
+void[KeywordToken] toplevel_method2[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.scanner.expect b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.scanner.expect
new file mode 100644
index 0000000..3d0432b
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_14.dart.scanner.expect
@@ -0,0 +1,41 @@
+class Foo {
+void foo_method1(dynamic input) {
+if (1 + 1 == 2) {
+foo_method2({1, 2, 3 })
+
+}}
+
+void foo_method2(dynamic whatever) {
+
+}
+}
+
+void toplevel_method1() {
+
+}
+
+void toplevel_method2() {
+
+}
+
+
+[UnmatchedToken][UnmatchedToken]class[KeywordToken] Foo[StringToken] {[BeginToken]
+void[KeywordToken] foo_method1[StringToken]([BeginToken]dynamic[KeywordToken] input[StringToken])[SimpleToken] {[BeginToken]
+if[KeywordToken] ([BeginToken]1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken])[SimpleToken] {[BeginToken]
+foo_method2[StringToken]([BeginToken]{[BeginToken]1[StringToken],[SimpleToken] 2[StringToken],[SimpleToken] 3[StringToken] }[SyntheticToken])[SimpleToken]
+
+}[SyntheticToken]}[SimpleToken]
+
+void[KeywordToken] foo_method2[StringToken]([BeginToken]dynamic[KeywordToken] whatever[StringToken])[SimpleToken] {[BeginToken]
+
+}[SimpleToken]
+}[SimpleToken]
+
+void[KeywordToken] toplevel_method1[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+
+}[SimpleToken]
+
+void[KeywordToken] toplevel_method2[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart
new file mode 100644
index 0000000..e2ab30d
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart
@@ -0,0 +1,29 @@
+// Without specific missing-brace recovery, we'll end up with:
+// * Class Foo, member Foo.foo_method1, Foo.toplevel_method1, Foo.toplevel_method2
+// With specific missing-brace recovery, we'll end up with the correct:
+// * Class Foo, members Foo.foo_method1, Foo.foo_method2
+// * Top-level members toplevel_method1, toplevel_method2
+
+class Foo {
+  void foo_method1(dynamic input) {
+    if (1 + 1 == 2) {
+    // missing: }
+  }
+
+  void foo_method2() {
+    if (1 + 1 == 2 &&
+        1 + 1 == 2 &&
+        1 + 1 == 2 &&
+        1 + 1 == 2 &&
+        1 + 1 == 2 &&
+        1 + 1 == 2) {} /* braces on the same line but "if" on another */
+  }
+}
+
+void toplevel_method1() {
+  // bla
+}
+
+void toplevel_method2() {
+  // bla
+}
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.expect b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.expect
new file mode 100644
index 0000000..7792232
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.expect
@@ -0,0 +1,165 @@
+Problems reported:
+
+parser/error_recovery/with_outline/typing_15:9:21: Can't find '}' to match '{'.
+    if (1 + 1 == 2) {
+                    ^
+
+beginCompilationUnit(class)
+  beginMetadataStar(class)
+  endMetadataStar(0)
+  beginClassOrMixinOrNamedMixinApplicationPrelude(class)
+    handleIdentifier(Foo, classOrMixinDeclaration)
+    handleNoTypeVariables({)
+    beginClassDeclaration(class, null, null, null, null, null, null, null, null, Foo)
+      handleNoType(Foo)
+      handleClassExtends(null, 1)
+      handleClassNoWithClause()
+      handleImplements(null, 0)
+      handleClassHeader(class, class, null)
+      beginClassOrMixinOrExtensionBody(DeclarationKind.Class, {)
+        beginMetadataStar(void)
+        endMetadataStar(0)
+        beginMember()
+          beginMethod(DeclarationKind.Class, null, null, null, null, null, null, foo_method1, Foo)
+            handleVoidKeyword(void)
+            handleIdentifier(foo_method1, methodDeclaration)
+            handleNoTypeVariables(()
+            beginFormalParameters((, MemberKind.NonStaticMethod)
+              beginMetadataStar(dynamic)
+              endMetadataStar(0)
+              beginFormalParameter(dynamic, MemberKind.NonStaticMethod, null, null, null)
+                handleIdentifier(dynamic, typeReference)
+                handleNoTypeArguments(input)
+                handleType(dynamic, null)
+                handleIdentifier(input, formalParameterDeclaration)
+                handleFormalParameterWithoutValue())
+              endFormalParameter(null, null, null, input, null, null, FormalParameterKind.requiredPositional, MemberKind.NonStaticMethod)
+            endFormalParameters(1, (, ), MemberKind.NonStaticMethod)
+            handleNoInitializers()
+            handleAsyncModifier(null, null)
+            beginBlockFunctionBody({)
+              beginIfStatement(if)
+                handleLiteralInt(1)
+                beginBinaryExpression(+)
+                  handleLiteralInt(1)
+                endBinaryExpression(+, 1)
+                beginBinaryExpression(==)
+                  handleLiteralInt(2)
+                endBinaryExpression(==, 2)
+                handleParenthesizedCondition((, null, null)
+                beginThenStatement({)
+                  beginBlock({, BlockKind(statement))
+                  endBlock(0, {, }, BlockKind(statement))
+                endThenStatement({, })
+              endIfStatement(if, null, })
+            endBlockFunctionBody(1, {, })
+          endClassMethod(null, void, (, null, })
+        endMember()
+        beginMetadataStar(void)
+        endMetadataStar(0)
+        beginMember()
+          beginMethod(DeclarationKind.Class, null, null, null, null, null, null, foo_method2, Foo)
+            handleVoidKeyword(void)
+            handleIdentifier(foo_method2, methodDeclaration)
+            handleNoTypeVariables(()
+            beginFormalParameters((, MemberKind.NonStaticMethod)
+            endFormalParameters(0, (, ), MemberKind.NonStaticMethod)
+            handleNoInitializers()
+            handleAsyncModifier(null, null)
+            beginBlockFunctionBody({)
+              beginIfStatement(if)
+                handleLiteralInt(1)
+                beginBinaryExpression(+)
+                  handleLiteralInt(1)
+                endBinaryExpression(+, 1)
+                beginBinaryExpression(==)
+                  handleLiteralInt(2)
+                endBinaryExpression(==, 2)
+                beginBinaryExpression(&&)
+                  handleLiteralInt(1)
+                  beginBinaryExpression(+)
+                    handleLiteralInt(1)
+                  endBinaryExpression(+, 1)
+                  beginBinaryExpression(==)
+                    handleLiteralInt(2)
+                  endBinaryExpression(==, 2)
+                endBinaryExpression(&&, 2)
+                beginBinaryExpression(&&)
+                  handleLiteralInt(1)
+                  beginBinaryExpression(+)
+                    handleLiteralInt(1)
+                  endBinaryExpression(+, 1)
+                  beginBinaryExpression(==)
+                    handleLiteralInt(2)
+                  endBinaryExpression(==, 2)
+                endBinaryExpression(&&, 2)
+                beginBinaryExpression(&&)
+                  handleLiteralInt(1)
+                  beginBinaryExpression(+)
+                    handleLiteralInt(1)
+                  endBinaryExpression(+, 1)
+                  beginBinaryExpression(==)
+                    handleLiteralInt(2)
+                  endBinaryExpression(==, 2)
+                endBinaryExpression(&&, 2)
+                beginBinaryExpression(&&)
+                  handleLiteralInt(1)
+                  beginBinaryExpression(+)
+                    handleLiteralInt(1)
+                  endBinaryExpression(+, 1)
+                  beginBinaryExpression(==)
+                    handleLiteralInt(2)
+                  endBinaryExpression(==, 2)
+                endBinaryExpression(&&, 2)
+                beginBinaryExpression(&&)
+                  handleLiteralInt(1)
+                  beginBinaryExpression(+)
+                    handleLiteralInt(1)
+                  endBinaryExpression(+, 1)
+                  beginBinaryExpression(==)
+                    handleLiteralInt(2)
+                  endBinaryExpression(==, 2)
+                endBinaryExpression(&&, 2)
+                handleParenthesizedCondition((, null, null)
+                beginThenStatement({)
+                  beginBlock({, BlockKind(statement))
+                  endBlock(0, {, }, BlockKind(statement))
+                endThenStatement({, })
+              endIfStatement(if, null, })
+            endBlockFunctionBody(1, {, })
+          endClassMethod(null, void, (, null, })
+        endMember()
+      endClassOrMixinOrExtensionBody(DeclarationKind.Class, 2, {, })
+    endClassDeclaration(class, })
+  endTopLevelDeclaration(})
+  beginMetadataStar(void)
+  endMetadataStar(0)
+  beginTopLevelMember(void)
+    beginTopLevelMethod(}, null, null)
+      handleVoidKeyword(void)
+      handleIdentifier(toplevel_method1, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginBlockFunctionBody({)
+      endBlockFunctionBody(0, {, })
+    endTopLevelMethod(void, null, })
+  endTopLevelDeclaration(})
+  beginMetadataStar(void)
+  endMetadataStar(0)
+  beginTopLevelMember(void)
+    beginTopLevelMethod(}, null, null)
+      handleVoidKeyword(void)
+      handleIdentifier(toplevel_method2, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginBlockFunctionBody({)
+      endBlockFunctionBody(0, {, })
+    endTopLevelMethod(void, null, })
+  endTopLevelDeclaration(})
+  handleErrorToken(UnmatchedToken({))
+  handleRecoverableError(Message[UnmatchedToken, Can't find '}' to match '{'., null, {string: }, lexeme: {}], UnmatchedToken({), UnmatchedToken({))
+endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.intertwined.expect b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.intertwined.expect
new file mode 100644
index 0000000..6643676
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.intertwined.expect
@@ -0,0 +1,346 @@
+parseUnit(UnmatchedToken({))
+  skipErrorTokens(UnmatchedToken({))
+  listener: beginCompilationUnit(class)
+  syntheticPreviousToken(class)
+  parseTopLevelDeclarationImpl(UnmatchedToken({), DirectiveContext(DirectiveState.Unknown))
+    parseMetadataStar(UnmatchedToken({))
+      listener: beginMetadataStar(class)
+      listener: endMetadataStar(0)
+    parseTopLevelKeywordDeclaration(class, UnmatchedToken({), class, null, null, null, null, DirectiveContext(DirectiveState.Unknown))
+      parseClassOrNamedMixinApplication(class, null, null, null, null, null, null, null, null, class)
+        listener: beginClassOrMixinOrNamedMixinApplicationPrelude(class)
+        ensureIdentifier(class, classOrMixinDeclaration)
+          listener: handleIdentifier(Foo, classOrMixinDeclaration)
+        listener: handleNoTypeVariables({)
+        listener: beginClassDeclaration(class, null, null, null, null, null, null, null, null, Foo)
+        parseClass(Foo, class, class, Foo)
+          parseClassHeaderOpt(Foo, class, class)
+            parseClassExtendsOpt(Foo, DeclarationHeaderKind.Class)
+              listener: handleNoType(Foo)
+              listener: handleClassExtends(null, 1)
+            parseClassWithClauseOpt(Foo)
+              listener: handleClassNoWithClause()
+            parseClassOrMixinOrEnumImplementsOpt(Foo)
+              listener: handleImplements(null, 0)
+            listener: handleClassHeader(class, class, null)
+          parseClassOrMixinOrExtensionBody(Foo, DeclarationKind.Class, Foo)
+            listener: beginClassOrMixinOrExtensionBody(DeclarationKind.Class, {)
+            notEofOrValue(}, void)
+            parseClassOrMixinOrExtensionOrEnumMemberImpl({, DeclarationKind.Class, Foo)
+              parseMetadataStar({)
+                listener: beginMetadataStar(void)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseMethod({, null, null, null, null, null, null, null, {, VoidType(), null, foo_method1, DeclarationKind.Class, Foo, false)
+                listener: beginMethod(DeclarationKind.Class, null, null, null, null, null, null, foo_method1, Foo)
+                listener: handleVoidKeyword(void)
+                ensureIdentifierPotentiallyRecovered(void, methodDeclaration, false)
+                  listener: handleIdentifier(foo_method1, methodDeclaration)
+                parseQualifiedRestOpt(foo_method1, methodDeclarationContinuation)
+                parseMethodTypeVar(foo_method1)
+                  listener: handleNoTypeVariables(()
+                parseGetterOrFormalParameters(foo_method1, foo_method1, false, MemberKind.NonStaticMethod)
+                  parseFormalParameters(foo_method1, MemberKind.NonStaticMethod)
+                    parseFormalParametersRest((, MemberKind.NonStaticMethod)
+                      listener: beginFormalParameters((, MemberKind.NonStaticMethod)
+                      parseFormalParameter((, FormalParameterKind.requiredPositional, MemberKind.NonStaticMethod)
+                        parseMetadataStar(()
+                          listener: beginMetadataStar(dynamic)
+                          listener: endMetadataStar(0)
+                        listener: beginFormalParameter(dynamic, MemberKind.NonStaticMethod, null, null, null)
+                        listener: handleIdentifier(dynamic, typeReference)
+                        listener: handleNoTypeArguments(input)
+                        listener: handleType(dynamic, null)
+                        ensureIdentifier(dynamic, formalParameterDeclaration)
+                          listener: handleIdentifier(input, formalParameterDeclaration)
+                        listener: handleFormalParameterWithoutValue())
+                        listener: endFormalParameter(null, null, null, input, null, null, FormalParameterKind.requiredPositional, MemberKind.NonStaticMethod)
+                      listener: endFormalParameters(1, (, ), MemberKind.NonStaticMethod)
+                parseInitializersOpt())
+                  listener: handleNoInitializers()
+                parseAsyncModifierOpt())
+                  listener: handleAsyncModifier(null, null)
+                  inPlainSync()
+                inPlainSync()
+                parseFunctionBody(), false, true)
+                  listener: beginBlockFunctionBody({)
+                  notEofOrValue(}, if)
+                  parseStatement({)
+                    parseStatementX({)
+                      parseIfStatement({)
+                        listener: beginIfStatement(if)
+                        ensureParenthesizedCondition(if, allowCase: false)
+                          parseExpressionInParenthesisRest((, allowCase: false)
+                            parseExpression(()
+                              parsePrecedenceExpression((, 1, true, ConstantPatternContext.none)
+                                parseUnaryExpression((, true, ConstantPatternContext.none)
+                                  parsePrimary((, expression, ConstantPatternContext.none)
+                                    parseLiteralInt(()
+                                      listener: handleLiteralInt(1)
+                                listener: beginBinaryExpression(+)
+                                parsePrecedenceExpression(+, 14, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(+, true, ConstantPatternContext.none)
+                                    parsePrimary(+, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(+)
+                                        listener: handleLiteralInt(1)
+                                listener: endBinaryExpression(+, 1)
+                                listener: beginBinaryExpression(==)
+                                parsePrecedenceExpression(==, 8, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(==, true, ConstantPatternContext.none)
+                                    parsePrimary(==, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(==)
+                                        listener: handleLiteralInt(2)
+                                listener: endBinaryExpression(==, 2)
+                            ensureCloseParen(2, ()
+                            listener: handleParenthesizedCondition((, null, null)
+                        listener: beginThenStatement({)
+                        parseStatement())
+                          parseStatementX())
+                            parseBlock(), BlockKind(statement))
+                              ensureBlock(), BlockKind(statement))
+                              listener: beginBlock({, BlockKind(statement))
+                              notEofOrValue(}, })
+                              listener: endBlock(0, {, }, BlockKind(statement))
+                        listener: endThenStatement({, })
+                        listener: endIfStatement(if, null, })
+                  notEofOrValue(}, })
+                  listener: endBlockFunctionBody(1, {, })
+                listener: endClassMethod(null, void, (, null, })
+              listener: endMember()
+            notEofOrValue(}, void)
+            parseClassOrMixinOrExtensionOrEnumMemberImpl(}, DeclarationKind.Class, Foo)
+              parseMetadataStar(})
+                listener: beginMetadataStar(void)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseMethod(}, null, null, null, null, null, null, null, }, VoidType(), null, foo_method2, DeclarationKind.Class, Foo, false)
+                listener: beginMethod(DeclarationKind.Class, null, null, null, null, null, null, foo_method2, Foo)
+                listener: handleVoidKeyword(void)
+                ensureIdentifierPotentiallyRecovered(void, methodDeclaration, false)
+                  listener: handleIdentifier(foo_method2, methodDeclaration)
+                parseQualifiedRestOpt(foo_method2, methodDeclarationContinuation)
+                parseMethodTypeVar(foo_method2)
+                  listener: handleNoTypeVariables(()
+                parseGetterOrFormalParameters(foo_method2, foo_method2, false, MemberKind.NonStaticMethod)
+                  parseFormalParameters(foo_method2, MemberKind.NonStaticMethod)
+                    parseFormalParametersRest((, MemberKind.NonStaticMethod)
+                      listener: beginFormalParameters((, MemberKind.NonStaticMethod)
+                      listener: endFormalParameters(0, (, ), MemberKind.NonStaticMethod)
+                parseInitializersOpt())
+                  listener: handleNoInitializers()
+                parseAsyncModifierOpt())
+                  listener: handleAsyncModifier(null, null)
+                  inPlainSync()
+                inPlainSync()
+                parseFunctionBody(), false, true)
+                  listener: beginBlockFunctionBody({)
+                  notEofOrValue(}, if)
+                  parseStatement({)
+                    parseStatementX({)
+                      parseIfStatement({)
+                        listener: beginIfStatement(if)
+                        ensureParenthesizedCondition(if, allowCase: false)
+                          parseExpressionInParenthesisRest((, allowCase: false)
+                            parseExpression(()
+                              parsePrecedenceExpression((, 1, true, ConstantPatternContext.none)
+                                parseUnaryExpression((, true, ConstantPatternContext.none)
+                                  parsePrimary((, expression, ConstantPatternContext.none)
+                                    parseLiteralInt(()
+                                      listener: handleLiteralInt(1)
+                                listener: beginBinaryExpression(+)
+                                parsePrecedenceExpression(+, 14, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(+, true, ConstantPatternContext.none)
+                                    parsePrimary(+, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(+)
+                                        listener: handleLiteralInt(1)
+                                listener: endBinaryExpression(+, 1)
+                                listener: beginBinaryExpression(==)
+                                parsePrecedenceExpression(==, 8, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(==, true, ConstantPatternContext.none)
+                                    parsePrimary(==, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(==)
+                                        listener: handleLiteralInt(2)
+                                listener: endBinaryExpression(==, 2)
+                                listener: beginBinaryExpression(&&)
+                                parsePrecedenceExpression(&&, 7, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(&&, true, ConstantPatternContext.none)
+                                    parsePrimary(&&, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(&&)
+                                        listener: handleLiteralInt(1)
+                                  listener: beginBinaryExpression(+)
+                                  parsePrecedenceExpression(+, 14, true, ConstantPatternContext.none)
+                                    parseUnaryExpression(+, true, ConstantPatternContext.none)
+                                      parsePrimary(+, expression, ConstantPatternContext.none)
+                                        parseLiteralInt(+)
+                                          listener: handleLiteralInt(1)
+                                  listener: endBinaryExpression(+, 1)
+                                  listener: beginBinaryExpression(==)
+                                  parsePrecedenceExpression(==, 8, true, ConstantPatternContext.none)
+                                    parseUnaryExpression(==, true, ConstantPatternContext.none)
+                                      parsePrimary(==, expression, ConstantPatternContext.none)
+                                        parseLiteralInt(==)
+                                          listener: handleLiteralInt(2)
+                                  listener: endBinaryExpression(==, 2)
+                                listener: endBinaryExpression(&&, 2)
+                                listener: beginBinaryExpression(&&)
+                                parsePrecedenceExpression(&&, 7, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(&&, true, ConstantPatternContext.none)
+                                    parsePrimary(&&, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(&&)
+                                        listener: handleLiteralInt(1)
+                                  listener: beginBinaryExpression(+)
+                                  parsePrecedenceExpression(+, 14, true, ConstantPatternContext.none)
+                                    parseUnaryExpression(+, true, ConstantPatternContext.none)
+                                      parsePrimary(+, expression, ConstantPatternContext.none)
+                                        parseLiteralInt(+)
+                                          listener: handleLiteralInt(1)
+                                  listener: endBinaryExpression(+, 1)
+                                  listener: beginBinaryExpression(==)
+                                  parsePrecedenceExpression(==, 8, true, ConstantPatternContext.none)
+                                    parseUnaryExpression(==, true, ConstantPatternContext.none)
+                                      parsePrimary(==, expression, ConstantPatternContext.none)
+                                        parseLiteralInt(==)
+                                          listener: handleLiteralInt(2)
+                                  listener: endBinaryExpression(==, 2)
+                                listener: endBinaryExpression(&&, 2)
+                                listener: beginBinaryExpression(&&)
+                                parsePrecedenceExpression(&&, 7, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(&&, true, ConstantPatternContext.none)
+                                    parsePrimary(&&, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(&&)
+                                        listener: handleLiteralInt(1)
+                                  listener: beginBinaryExpression(+)
+                                  parsePrecedenceExpression(+, 14, true, ConstantPatternContext.none)
+                                    parseUnaryExpression(+, true, ConstantPatternContext.none)
+                                      parsePrimary(+, expression, ConstantPatternContext.none)
+                                        parseLiteralInt(+)
+                                          listener: handleLiteralInt(1)
+                                  listener: endBinaryExpression(+, 1)
+                                  listener: beginBinaryExpression(==)
+                                  parsePrecedenceExpression(==, 8, true, ConstantPatternContext.none)
+                                    parseUnaryExpression(==, true, ConstantPatternContext.none)
+                                      parsePrimary(==, expression, ConstantPatternContext.none)
+                                        parseLiteralInt(==)
+                                          listener: handleLiteralInt(2)
+                                  listener: endBinaryExpression(==, 2)
+                                listener: endBinaryExpression(&&, 2)
+                                listener: beginBinaryExpression(&&)
+                                parsePrecedenceExpression(&&, 7, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(&&, true, ConstantPatternContext.none)
+                                    parsePrimary(&&, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(&&)
+                                        listener: handleLiteralInt(1)
+                                  listener: beginBinaryExpression(+)
+                                  parsePrecedenceExpression(+, 14, true, ConstantPatternContext.none)
+                                    parseUnaryExpression(+, true, ConstantPatternContext.none)
+                                      parsePrimary(+, expression, ConstantPatternContext.none)
+                                        parseLiteralInt(+)
+                                          listener: handleLiteralInt(1)
+                                  listener: endBinaryExpression(+, 1)
+                                  listener: beginBinaryExpression(==)
+                                  parsePrecedenceExpression(==, 8, true, ConstantPatternContext.none)
+                                    parseUnaryExpression(==, true, ConstantPatternContext.none)
+                                      parsePrimary(==, expression, ConstantPatternContext.none)
+                                        parseLiteralInt(==)
+                                          listener: handleLiteralInt(2)
+                                  listener: endBinaryExpression(==, 2)
+                                listener: endBinaryExpression(&&, 2)
+                                listener: beginBinaryExpression(&&)
+                                parsePrecedenceExpression(&&, 7, true, ConstantPatternContext.none)
+                                  parseUnaryExpression(&&, true, ConstantPatternContext.none)
+                                    parsePrimary(&&, expression, ConstantPatternContext.none)
+                                      parseLiteralInt(&&)
+                                        listener: handleLiteralInt(1)
+                                  listener: beginBinaryExpression(+)
+                                  parsePrecedenceExpression(+, 14, true, ConstantPatternContext.none)
+                                    parseUnaryExpression(+, true, ConstantPatternContext.none)
+                                      parsePrimary(+, expression, ConstantPatternContext.none)
+                                        parseLiteralInt(+)
+                                          listener: handleLiteralInt(1)
+                                  listener: endBinaryExpression(+, 1)
+                                  listener: beginBinaryExpression(==)
+                                  parsePrecedenceExpression(==, 8, true, ConstantPatternContext.none)
+                                    parseUnaryExpression(==, true, ConstantPatternContext.none)
+                                      parsePrimary(==, expression, ConstantPatternContext.none)
+                                        parseLiteralInt(==)
+                                          listener: handleLiteralInt(2)
+                                  listener: endBinaryExpression(==, 2)
+                                listener: endBinaryExpression(&&, 2)
+                            ensureCloseParen(2, ()
+                            listener: handleParenthesizedCondition((, null, null)
+                        listener: beginThenStatement({)
+                        parseStatement())
+                          parseStatementX())
+                            parseBlock(), BlockKind(statement))
+                              ensureBlock(), BlockKind(statement))
+                              listener: beginBlock({, BlockKind(statement))
+                              notEofOrValue(}, })
+                              listener: endBlock(0, {, }, BlockKind(statement))
+                        listener: endThenStatement({, })
+                        listener: endIfStatement(if, null, })
+                  notEofOrValue(}, })
+                  listener: endBlockFunctionBody(1, {, })
+                listener: endClassMethod(null, void, (, null, })
+              listener: endMember()
+            notEofOrValue(}, })
+            listener: endClassOrMixinOrExtensionBody(DeclarationKind.Class, 2, {, })
+          listener: endClassDeclaration(class, })
+  listener: endTopLevelDeclaration(})
+  parseTopLevelDeclarationImpl(}, DirectiveContext(DirectiveState.Declarations))
+    parseMetadataStar(})
+      listener: beginMetadataStar(void)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(})
+      listener: beginTopLevelMember(void)
+      parseTopLevelMethod(}, null, null, }, VoidType(), null, toplevel_method1, false)
+        listener: beginTopLevelMethod(}, null, null)
+        listener: handleVoidKeyword(void)
+        ensureIdentifierPotentiallyRecovered(void, topLevelFunctionDeclaration, false)
+          listener: handleIdentifier(toplevel_method1, topLevelFunctionDeclaration)
+        parseMethodTypeVar(toplevel_method1)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(toplevel_method1, toplevel_method1, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(toplevel_method1, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          listener: beginBlockFunctionBody({)
+          notEofOrValue(}, })
+          listener: endBlockFunctionBody(0, {, })
+        listener: endTopLevelMethod(void, null, })
+  listener: endTopLevelDeclaration(})
+  parseTopLevelDeclarationImpl(}, DirectiveContext(DirectiveState.Declarations))
+    parseMetadataStar(})
+      listener: beginMetadataStar(void)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(})
+      listener: beginTopLevelMember(void)
+      parseTopLevelMethod(}, null, null, }, VoidType(), null, toplevel_method2, false)
+        listener: beginTopLevelMethod(}, null, null)
+        listener: handleVoidKeyword(void)
+        ensureIdentifierPotentiallyRecovered(void, topLevelFunctionDeclaration, false)
+          listener: handleIdentifier(toplevel_method2, topLevelFunctionDeclaration)
+        parseMethodTypeVar(toplevel_method2)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(toplevel_method2, toplevel_method2, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(toplevel_method2, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          listener: beginBlockFunctionBody({)
+          notEofOrValue(}, })
+          listener: endBlockFunctionBody(0, {, })
+        listener: endTopLevelMethod(void, null, })
+  listener: endTopLevelDeclaration(})
+  reportAllErrorTokens(UnmatchedToken({))
+    listener: handleErrorToken(UnmatchedToken({))
+    listener: handleRecoverableError(Message[UnmatchedToken, Can't find '}' to match '{'., null, {string: }, lexeme: {}], UnmatchedToken({), UnmatchedToken({))
+  listener: endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.outline.expect b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.outline.expect
new file mode 100644
index 0000000..96af17b
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.outline.expect
@@ -0,0 +1,5 @@
+Class: Foo
+Class method: Foo.foo_method1
+Class method: Foo.foo_method2
+Top-level method: toplevel_method1
+Top-level method: toplevel_method2
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.parser.expect b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.parser.expect
new file mode 100644
index 0000000..303ff3b
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.parser.expect
@@ -0,0 +1,49 @@
+class Foo {
+void foo_method1(dynamic input) {
+if (1 + 1 == 2) {
+
+}}
+
+void foo_method2() {
+if (1 + 1 == 2 &&
+1 + 1 == 2 &&
+1 + 1 == 2 &&
+1 + 1 == 2 &&
+1 + 1 == 2 &&
+1 + 1 == 2) {}
+}
+}
+
+void toplevel_method1() {
+
+}
+
+void toplevel_method2() {
+
+}
+
+
+[UnmatchedToken]class[KeywordToken] Foo[StringToken] {[BeginToken]
+void[KeywordToken] foo_method1[StringToken]([BeginToken]dynamic[KeywordToken] input[StringToken])[SimpleToken] {[BeginToken]
+if[KeywordToken] ([BeginToken]1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken])[SimpleToken] {[BeginToken]
+
+}[SyntheticToken]}[SimpleToken]
+
+void[KeywordToken] foo_method2[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+if[KeywordToken] ([BeginToken]1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken] &&[SimpleToken]
+1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken] &&[SimpleToken]
+1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken] &&[SimpleToken]
+1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken] &&[SimpleToken]
+1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken] &&[SimpleToken]
+1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken])[SimpleToken] {[BeginToken]}[SimpleToken]
+}[SimpleToken]
+}[SimpleToken]
+
+void[KeywordToken] toplevel_method1[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+
+}[SimpleToken]
+
+void[KeywordToken] toplevel_method2[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.scanner.expect b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.scanner.expect
new file mode 100644
index 0000000..303ff3b
--- /dev/null
+++ b/pkg/front_end/parser_testcases/error_recovery/with_outline/typing_15.dart.scanner.expect
@@ -0,0 +1,49 @@
+class Foo {
+void foo_method1(dynamic input) {
+if (1 + 1 == 2) {
+
+}}
+
+void foo_method2() {
+if (1 + 1 == 2 &&
+1 + 1 == 2 &&
+1 + 1 == 2 &&
+1 + 1 == 2 &&
+1 + 1 == 2 &&
+1 + 1 == 2) {}
+}
+}
+
+void toplevel_method1() {
+
+}
+
+void toplevel_method2() {
+
+}
+
+
+[UnmatchedToken]class[KeywordToken] Foo[StringToken] {[BeginToken]
+void[KeywordToken] foo_method1[StringToken]([BeginToken]dynamic[KeywordToken] input[StringToken])[SimpleToken] {[BeginToken]
+if[KeywordToken] ([BeginToken]1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken])[SimpleToken] {[BeginToken]
+
+}[SyntheticToken]}[SimpleToken]
+
+void[KeywordToken] foo_method2[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+if[KeywordToken] ([BeginToken]1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken] &&[SimpleToken]
+1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken] &&[SimpleToken]
+1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken] &&[SimpleToken]
+1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken] &&[SimpleToken]
+1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken] &&[SimpleToken]
+1[StringToken] +[SimpleToken] 1[StringToken] ==[SimpleToken] 2[StringToken])[SimpleToken] {[BeginToken]}[SimpleToken]
+}[SimpleToken]
+}[SimpleToken]
+
+void[KeywordToken] toplevel_method1[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+
+}[SimpleToken]
+
+void[KeywordToken] toplevel_method2[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+
+}[SimpleToken]
+[SimpleToken]