add extension method header parser recovery

Change-Id: Ia7424a467014471c01eb30604a062430de42f84d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103580
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index 40b194d..cf368bf 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -1453,6 +1453,20 @@
             ));
   }
 
+  void test_missing_on() {
+    var unit = parseCompilationUnit('extension E', errors: [
+      expectedError(ParserErrorCode.EXPECTED_TOKEN, 10, 1),
+      expectedError(ParserErrorCode.EXPECTED_TYPE_NAME, 11, 0),
+      expectedError(ParserErrorCode.MISSING_CLASS_BODY, 11, 0),
+    ]);
+    expect(unit.declarations, hasLength(1));
+    var extension = unit.declarations[0] as ExtensionDeclaration;
+    expect(extension.name.name, 'E');
+    expect(extension.onKeyword.lexeme, 'on');
+    expect((extension.extendedType as NamedType).name.name, '');
+    expect(extension.members, hasLength(0));
+  }
+
   void test_simple() {
     var unit = parseCompilationUnit('extension E on C { }');
     expect(unit.declarations, hasLength(1));
@@ -1463,6 +1477,18 @@
     expect(extension.members, hasLength(0));
   }
 
+  void test_simple_extends() {
+    var unit = parseCompilationUnit('extension E extends C { }', errors: [
+      expectedError(ParserErrorCode.EXPECTED_INSTEAD, 12, 7),
+    ]);
+    expect(unit.declarations, hasLength(1));
+    var extension = unit.declarations[0] as ExtensionDeclaration;
+    expect(extension.name.name, 'E');
+    expect(extension.onKeyword.lexeme, 'extends');
+    expect((extension.extendedType as NamedType).name.name, 'C');
+    expect(extension.members, hasLength(0));
+  }
+
   void test_simple_not_enabled() {
     parseCompilationUnit('extension E on C { }',
         errors: [
@@ -1471,6 +1497,18 @@
         ],
         featureSet: FeatureSet.forTesting(sdkVersion: '2.3.0'));
   }
+
+  void test_simple_with() {
+    var unit = parseCompilationUnit('extension E with C { }', errors: [
+      expectedError(ParserErrorCode.EXPECTED_INSTEAD, 12, 4),
+    ]);
+    expect(unit.declarations, hasLength(1));
+    var extension = unit.declarations[0] as ExtensionDeclaration;
+    expect(extension.name.name, 'E');
+    expect(extension.onKeyword.lexeme, 'with');
+    expect((extension.extendedType as NamedType).name.name, 'C');
+    expect(extension.members, hasLength(0));
+  }
 }
 
 /**
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index 38542074..536c0cb 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -2066,8 +2066,14 @@
     Token onKeyword = token.next;
     if (!optional('on', onKeyword)) {
       // Recovery
-      // TODO(danrubel): implement this.
-      throw "Internal error: extension recovery not implemented yet.";
+      if (optional('extends', onKeyword) || optional('with', onKeyword)) {
+        reportRecoverableError(
+            onKeyword, fasta.templateExpectedInstead.withArguments('on'));
+      } else {
+        reportRecoverableError(
+            token, fasta.templateExpectedAfterButGot.withArguments('on'));
+        onKeyword = rewriter.insertSyntheticKeyword(token, Keyword.ON);
+      }
     }
     TypeInfo typeInfo = computeType(onKeyword, true);
     token = typeInfo.ensureTypeNotVoid(onKeyword, this);