[CFE] Fuzzer using Dart.G

This is the first commit of this effort. It's not polished and mostly
generates nonsense output that, though, should not crash the compiler.
Further work might add options to create less-nonsense output).

Change-Id: Ic7f140ea6f248bfa9a0e62ca37326def1d0a5086
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/403601
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
diff --git a/pkg/front_end/lib/src/util/parser_ast_helper.dart b/pkg/front_end/lib/src/util/parser_ast_helper.dart
index 28d33a6..2b126be 100644
--- a/pkg/front_end/lib/src/util/parser_ast_helper.dart
+++ b/pkg/front_end/lib/src/util/parser_ast_helper.dart
@@ -42,6 +42,37 @@
     }
   }
 
+  void debugPrint() {
+    StringBuffer sb = new StringBuffer();
+    _debugPrintImpl(0, sb);
+    print(sb.toString());
+  }
+
+  void _debugPrintImpl(int indentation, StringBuffer sb) {
+    sb.write(" " * indentation);
+    sb.write(what);
+    sb.write(type.name);
+    Token? tokenWithSmallestOffset;
+    for (Object? value in deprecatedArguments.values) {
+      if (value is Token) {
+        if (tokenWithSmallestOffset == null ||
+            value.charOffset < tokenWithSmallestOffset.charOffset) {
+          tokenWithSmallestOffset = value;
+        }
+      }
+    }
+    if (tokenWithSmallestOffset != null) {
+      sb.write(" (${tokenWithSmallestOffset.lexeme} @ "
+          "${tokenWithSmallestOffset.charOffset})");
+    }
+    sb.writeln();
+    List<ParserAstNode>? children = this.children;
+    if (children == null) return;
+    for (ParserAstNode child in children) {
+      child._debugPrintImpl(indentation + 2, sb);
+    }
+  }
+
   // TODO(jensj): Compare two ASTs.
 }
 
@@ -11886,3 +11917,1491 @@
   void visitPatternAssignmentHandle(PatternAssignmentHandle node) =>
       node.visitChildren(this);
 }
+
+class RecursiveParserAstVisitorWithDefaultNodeAsync
+    implements ParserAstVisitor<Future<void>> {
+  Future<void> defaultNode(ParserAstNode node) async {
+    List<ParserAstNode>? children = node.children;
+    if (children == null) return;
+    for (ParserAstNode child in children) {
+      await child.accept(this);
+    }
+  }
+
+  @override
+  Future<void> visitArgumentsBegin(ArgumentsBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitArgumentsEnd(ArgumentsEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitObjectPatternFieldsHandle(ObjectPatternFieldsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAsyncModifierHandle(AsyncModifierHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAwaitExpressionBegin(AwaitExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAwaitExpressionEnd(AwaitExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInvalidAwaitExpressionEnd(InvalidAwaitExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitBlockBegin(BlockBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitBlockEnd(BlockEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitInvalidTopLevelBlockHandle(
+          InvalidTopLevelBlockHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitCascadeBegin(CascadeBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitCascadeEnd(CascadeEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitCaseExpressionBegin(CaseExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitCaseExpressionEnd(CaseExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassOrMixinOrExtensionBodyBegin(
+          ClassOrMixinOrExtensionBodyBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassOrMixinOrExtensionBodyEnd(
+          ClassOrMixinOrExtensionBodyEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassOrMixinOrNamedMixinApplicationPreludeBegin(
+          ClassOrMixinOrNamedMixinApplicationPreludeBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassDeclarationBegin(ClassDeclarationBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassExtendsHandle(ClassExtendsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitImplementsHandle(ImplementsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassHeaderHandle(ClassHeaderHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRecoverDeclarationHeaderHandle(
+          RecoverDeclarationHeaderHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassDeclarationEnd(ClassDeclarationEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMixinDeclarationBegin(MixinDeclarationBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMixinOnHandle(MixinOnHandle node) => defaultNode(node);
+
+  @override
+  Future<void> visitMixinHeaderHandle(MixinHeaderHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRecoverMixinHeaderHandle(RecoverMixinHeaderHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMixinDeclarationEnd(MixinDeclarationEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitUncategorizedTopLevelDeclarationBegin(
+          UncategorizedTopLevelDeclarationBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionDeclarationPreludeBegin(
+          ExtensionDeclarationPreludeBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionDeclarationBegin(ExtensionDeclarationBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionDeclarationEnd(ExtensionDeclarationEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionTypeDeclarationBegin(
+          ExtensionTypeDeclarationBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionTypeDeclarationEnd(
+          ExtensionTypeDeclarationEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitPrimaryConstructorBegin(PrimaryConstructorBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitPrimaryConstructorEnd(PrimaryConstructorEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoPrimaryConstructorHandle(
+          NoPrimaryConstructorHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitCombinatorsBegin(CombinatorsBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitCombinatorsEnd(CombinatorsEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitCompilationUnitBegin(CompilationUnitBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitDirectivesOnlyHandle(DirectivesOnlyHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitCompilationUnitEnd(CompilationUnitEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConstLiteralBegin(ConstLiteralBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConstLiteralEnd(ConstLiteralEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitConstructorReferenceBegin(ConstructorReferenceBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConstructorReferenceEnd(ConstructorReferenceEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitDoWhileStatementBegin(DoWhileStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitDoWhileStatementEnd(DoWhileStatementEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitDoWhileStatementBodyBegin(DoWhileStatementBodyBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitDoWhileStatementBodyEnd(DoWhileStatementBodyEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitWhileStatementBodyBegin(WhileStatementBodyBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitWhileStatementBodyEnd(WhileStatementBodyEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEnumBegin(EnumBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitEnumEnd(EnumEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitEnumConstructorEnd(EnumConstructorEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEnumElementsHandle(EnumElementsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEnumHeaderHandle(EnumHeaderHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEnumElementHandle(EnumElementHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEnumFactoryMethodEnd(EnumFactoryMethodEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExportBegin(ExportBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitExportEnd(ExportEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitExtraneousExpressionHandle(
+          ExtraneousExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExpressionStatementHandle(ExpressionStatementHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFactoryMethodBegin(FactoryMethodBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassFactoryMethodEnd(ClassFactoryMethodEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMixinFactoryMethodEnd(MixinFactoryMethodEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionFactoryMethodEnd(ExtensionFactoryMethodEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionTypeFactoryMethodEnd(
+          ExtensionTypeFactoryMethodEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFormalParameterBegin(FormalParameterBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFormalParameterEnd(FormalParameterEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoFormalParametersHandle(NoFormalParametersHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFormalParametersBegin(FormalParametersBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFormalParametersEnd(FormalParametersEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassFieldsEnd(ClassFieldsEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitMixinFieldsEnd(MixinFieldsEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitExtensionFieldsEnd(ExtensionFieldsEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionTypeFieldsEnd(ExtensionTypeFieldsEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEnumFieldsEnd(EnumFieldsEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitEnumMethodEnd(EnumMethodEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitForInitializerEmptyStatementHandle(
+          ForInitializerEmptyStatementHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForInitializerExpressionStatementHandle(
+          ForInitializerExpressionStatementHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForInitializerLocalVariableDeclarationHandle(
+          ForInitializerLocalVariableDeclarationHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForInitializerPatternVariableAssignmentHandle(
+          ForInitializerPatternVariableAssignmentHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForStatementBegin(ForStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForLoopPartsHandle(ForLoopPartsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForStatementEnd(ForStatementEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitForStatementBodyBegin(ForStatementBodyBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForStatementBodyEnd(ForStatementBodyEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForInLoopPartsHandle(ForInLoopPartsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForInEnd(ForInEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitForInExpressionBegin(ForInExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForInExpressionEnd(ForInExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForInBodyBegin(ForInBodyBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitForInBodyEnd(ForInBodyEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitNamedFunctionExpressionBegin(
+          NamedFunctionExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNamedFunctionExpressionEnd(
+          NamedFunctionExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLocalFunctionDeclarationBegin(
+          LocalFunctionDeclarationBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLocalFunctionDeclarationEnd(
+          LocalFunctionDeclarationEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitBlockFunctionBodyBegin(BlockFunctionBodyBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitBlockFunctionBodyEnd(BlockFunctionBodyEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoFunctionBodyHandle(NoFunctionBodyHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFunctionBodySkippedHandle(FunctionBodySkippedHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFunctionNameBegin(FunctionNameBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFunctionNameEnd(FunctionNameEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitTypedefBegin(TypedefBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitTypedefEnd(TypedefEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitClassWithClauseHandle(ClassWithClauseHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassNoWithClauseHandle(ClassNoWithClauseHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEnumWithClauseHandle(EnumWithClauseHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEnumNoWithClauseHandle(EnumNoWithClauseHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMixinWithClauseHandle(MixinWithClauseHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNamedMixinApplicationBegin(
+          NamedMixinApplicationBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNamedMixinApplicationWithClauseHandle(
+          NamedMixinApplicationWithClauseHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNamedMixinApplicationEnd(NamedMixinApplicationEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitHideBegin(HideBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitHideEnd(HideEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitIdentifierListHandle(IdentifierListHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTypeListBegin(TypeListBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitTypeListEnd(TypeListEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitIfStatementBegin(IfStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitIfStatementEnd(IfStatementEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitThenStatementBegin(ThenStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitThenStatementEnd(ThenStatementEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitElseStatementBegin(ElseStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitElseStatementEnd(ElseStatementEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitImportBegin(ImportBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitImportPrefixHandle(ImportPrefixHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitImportEnd(ImportEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitRecoverImportHandle(RecoverImportHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConditionalUrisBegin(ConditionalUrisBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConditionalUrisEnd(ConditionalUrisEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConditionalUriBegin(ConditionalUriBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConditionalUriEnd(ConditionalUriEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitDottedNameHandle(DottedNameHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitImplicitCreationExpressionBegin(
+          ImplicitCreationExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitImplicitCreationExpressionEnd(
+          ImplicitCreationExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInitializedIdentifierBegin(
+          InitializedIdentifierBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInitializedIdentifierEnd(InitializedIdentifierEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFieldInitializerBegin(FieldInitializerBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFieldInitializerEnd(FieldInitializerEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoFieldInitializerHandle(NoFieldInitializerHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitVariableInitializerBegin(VariableInitializerBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitVariableInitializerEnd(VariableInitializerEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoVariableInitializerHandle(
+          NoVariableInitializerHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInitializerBegin(InitializerBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInitializerEnd(InitializerEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitInitializersBegin(InitializersBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInitializersEnd(InitializersEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitNoInitializersHandle(NoInitializersHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInvalidExpressionHandle(InvalidExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInvalidFunctionBodyHandle(InvalidFunctionBodyHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInvalidTypeReferenceHandle(
+          InvalidTypeReferenceHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLabelHandle(LabelHandle node) => defaultNode(node);
+
+  @override
+  Future<void> visitLabeledStatementBegin(LabeledStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLabeledStatementEnd(LabeledStatementEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLibraryAugmentationBegin(LibraryAugmentationBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLibraryAugmentationEnd(LibraryAugmentationEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLibraryNameBegin(LibraryNameBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLibraryNameEnd(LibraryNameEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitLiteralMapEntryHandle(LiteralMapEntryHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMapPatternEntryHandle(MapPatternEntryHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralStringBegin(LiteralStringBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInterpolationExpressionHandle(
+          InterpolationExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralStringEnd(LiteralStringEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAdjacentStringLiteralsHandle(
+          AdjacentStringLiteralsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMemberBegin(MemberBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitInvalidMemberHandle(InvalidMemberHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMemberEnd(MemberEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitMethodBegin(MethodBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitClassMethodEnd(ClassMethodEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitMixinMethodEnd(MixinMethodEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitExtensionMethodEnd(ExtensionMethodEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionTypeMethodEnd(ExtensionTypeMethodEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitClassConstructorEnd(ClassConstructorEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMixinConstructorEnd(MixinConstructorEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionConstructorEnd(ExtensionConstructorEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExtensionTypeConstructorEnd(
+          ExtensionTypeConstructorEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMetadataStarBegin(MetadataStarBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMetadataStarEnd(MetadataStarEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitMetadataBegin(MetadataBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitMetadataEnd(MetadataEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitOptionalFormalParametersBegin(
+          OptionalFormalParametersBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitOptionalFormalParametersEnd(
+          OptionalFormalParametersEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitPartBegin(PartBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitPartEnd(PartEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitPartOfBegin(PartOfBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitPartOfEnd(PartOfEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitRedirectingFactoryBodyBegin(
+          RedirectingFactoryBodyBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRedirectingFactoryBodyEnd(RedirectingFactoryBodyEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitReturnStatementBegin(ReturnStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNativeFunctionBodyHandle(NativeFunctionBodyHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNativeFunctionBodyIgnoredHandle(
+          NativeFunctionBodyIgnoredHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNativeFunctionBodySkippedHandle(
+          NativeFunctionBodySkippedHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEmptyFunctionBodyHandle(EmptyFunctionBodyHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExpressionFunctionBodyHandle(
+          ExpressionFunctionBodyHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitReturnStatementEnd(ReturnStatementEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSendHandle(SendHandle node) => defaultNode(node);
+
+  @override
+  Future<void> visitShowBegin(ShowBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitShowEnd(ShowEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitSwitchStatementBegin(SwitchStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchStatementEnd(SwitchStatementEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchExpressionBegin(SwitchExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchExpressionEnd(SwitchExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchBlockBegin(SwitchBlockBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchBlockEnd(SwitchBlockEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitSwitchExpressionBlockBegin(
+          SwitchExpressionBlockBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchExpressionBlockEnd(SwitchExpressionBlockEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralSymbolBegin(LiteralSymbolBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralSymbolEnd(LiteralSymbolEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitThrowExpressionHandle(ThrowExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRethrowStatementBegin(RethrowStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRethrowStatementEnd(RethrowStatementEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTopLevelDeclarationEnd(TopLevelDeclarationEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInvalidTopLevelDeclarationHandle(
+          InvalidTopLevelDeclarationHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTopLevelMemberBegin(TopLevelMemberBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFieldsBegin(FieldsBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitTopLevelFieldsEnd(TopLevelFieldsEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTopLevelMethodBegin(TopLevelMethodBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTopLevelMethodEnd(TopLevelMethodEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTryStatementBegin(TryStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitCatchClauseBegin(CatchClauseBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitCatchClauseEnd(CatchClauseEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitCatchBlockHandle(CatchBlockHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFinallyBlockHandle(FinallyBlockHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTryStatementEnd(TryStatementEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitTypeHandle(TypeHandle node) => defaultNode(node);
+
+  @override
+  Future<void> visitNonNullAssertExpressionHandle(
+          NonNullAssertExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNullAssertPatternHandle(NullAssertPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNullCheckPatternHandle(NullCheckPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAssignedVariablePatternHandle(
+          AssignedVariablePatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitDeclaredVariablePatternHandle(
+          DeclaredVariablePatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitWildcardPatternHandle(WildcardPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoNameHandle(NoNameHandle node) => defaultNode(node);
+
+  @override
+  Future<void> visitRecordTypeBegin(RecordTypeBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitRecordTypeEnd(RecordTypeEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitRecordTypeEntryBegin(RecordTypeEntryBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRecordTypeEntryEnd(RecordTypeEntryEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRecordTypeNamedFieldsBegin(
+          RecordTypeNamedFieldsBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRecordTypeNamedFieldsEnd(RecordTypeNamedFieldsEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFunctionTypeBegin(FunctionTypeBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFunctionTypeEnd(FunctionTypeEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitTypeArgumentsBegin(TypeArgumentsBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTypeArgumentsEnd(TypeArgumentsEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInvalidTypeArgumentsHandle(
+          InvalidTypeArgumentsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoTypeArgumentsHandle(NoTypeArgumentsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTypeVariableBegin(TypeVariableBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTypeVariablesDefinedHandle(
+          TypeVariablesDefinedHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTypeVariableEnd(TypeVariableEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitTypeVariablesBegin(TypeVariablesBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitTypeVariablesEnd(TypeVariablesEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFunctionExpressionBegin(FunctionExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFunctionExpressionEnd(FunctionExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitVariablesDeclarationBegin(VariablesDeclarationBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitVariablesDeclarationEnd(VariablesDeclarationEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitWhileStatementBegin(WhileStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitWhileStatementEnd(WhileStatementEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAsOperatorTypeBegin(AsOperatorTypeBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAsOperatorTypeEnd(AsOperatorTypeEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAsOperatorHandle(AsOperatorHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitCastPatternHandle(CastPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAssignmentExpressionHandle(
+          AssignmentExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitBinaryExpressionBegin(BinaryExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitBinaryExpressionEnd(BinaryExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitBinaryPatternBegin(BinaryPatternBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitBinaryPatternEnd(BinaryPatternEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEndingBinaryExpressionHandle(
+          EndingBinaryExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConditionalExpressionBegin(
+          ConditionalExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConditionalExpressionColonHandle(
+          ConditionalExpressionColonHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConditionalExpressionEnd(ConditionalExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConstExpressionBegin(ConstExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConstExpressionEnd(ConstExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConstFactoryHandle(ConstFactoryHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForControlFlowBegin(ForControlFlowBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForControlFlowEnd(ForControlFlowEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitForInControlFlowEnd(ForInControlFlowEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitIfControlFlowBegin(IfControlFlowBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitThenControlFlowHandle(ThenControlFlowHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitElseControlFlowHandle(ElseControlFlowHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitIfControlFlowEnd(IfControlFlowEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitIfElseControlFlowEnd(IfElseControlFlowEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSpreadExpressionHandle(SpreadExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNullAwareElementHandle(NullAwareElementHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRestPatternHandle(RestPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFunctionTypedFormalParameterBegin(
+          FunctionTypedFormalParameterBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFunctionTypedFormalParameterEnd(
+          FunctionTypedFormalParameterEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitIdentifierHandle(IdentifierHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitIndexedExpressionHandle(IndexedExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitIsOperatorTypeBegin(IsOperatorTypeBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitIsOperatorTypeEnd(IsOperatorTypeEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitIsOperatorHandle(IsOperatorHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralBoolHandle(LiteralBoolHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitBreakStatementHandle(BreakStatementHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitContinueStatementHandle(ContinueStatementHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitEmptyStatementHandle(EmptyStatementHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAssertBegin(AssertBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitAssertEnd(AssertEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitLiteralDoubleHandle(LiteralDoubleHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralDoubleWithSeparatorsHandle(
+          LiteralDoubleWithSeparatorsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralIntHandle(LiteralIntHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralIntWithSeparatorsHandle(
+          LiteralIntWithSeparatorsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralListHandle(LiteralListHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitListPatternHandle(ListPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralSetOrMapHandle(LiteralSetOrMapHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitMapPatternHandle(MapPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitLiteralNullHandle(LiteralNullHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNativeClauseHandle(NativeClauseHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNamedArgumentHandle(NamedArgumentHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitPatternFieldHandle(PatternFieldHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNamedRecordFieldHandle(NamedRecordFieldHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNewExpressionBegin(NewExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNewExpressionEnd(NewExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoArgumentsHandle(NoArgumentsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoConstructorReferenceContinuationAfterTypeArgumentsHandle(
+          NoConstructorReferenceContinuationAfterTypeArgumentsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoTypeNameInConstructorReferenceHandle(
+          NoTypeNameInConstructorReferenceHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNoTypeHandle(NoTypeHandle node) => defaultNode(node);
+
+  @override
+  Future<void> visitNoTypeVariablesHandle(NoTypeVariablesHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitOperatorHandle(OperatorHandle node) => defaultNode(node);
+
+  @override
+  Future<void> visitSwitchCaseNoWhenClauseHandle(
+          SwitchCaseNoWhenClauseHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchExpressionCasePatternHandle(
+          SwitchExpressionCasePatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSymbolVoidHandle(SymbolVoidHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitOperatorNameHandle(OperatorNameHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInvalidOperatorNameHandle(InvalidOperatorNameHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitParenthesizedConditionHandle(
+          ParenthesizedConditionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitPatternBegin(PatternBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitPatternGuardBegin(PatternGuardBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitParenthesizedExpressionOrRecordLiteralBegin(
+          ParenthesizedExpressionOrRecordLiteralBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchCaseWhenClauseBegin(SwitchCaseWhenClauseBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRecordLiteralEnd(RecordLiteralEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRecordPatternHandle(RecordPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitPatternEnd(PatternEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitPatternGuardEnd(PatternGuardEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitParenthesizedExpressionEnd(
+          ParenthesizedExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchCaseWhenClauseEnd(SwitchCaseWhenClauseEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitParenthesizedPatternHandle(
+          ParenthesizedPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConstantPatternBegin(ConstantPatternBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitConstantPatternEnd(ConstantPatternEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitObjectPatternHandle(ObjectPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitQualifiedHandle(QualifiedHandle node) => defaultNode(node);
+
+  @override
+  Future<void> visitStringPartHandle(StringPartHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSuperExpressionHandle(SuperExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitAugmentSuperExpressionHandle(
+          AugmentSuperExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchCaseBegin(SwitchCaseBegin node) => defaultNode(node);
+
+  @override
+  Future<void> visitSwitchCaseEnd(SwitchCaseEnd node) => defaultNode(node);
+
+  @override
+  Future<void> visitSwitchExpressionCaseBegin(SwitchExpressionCaseBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitSwitchExpressionCaseEnd(SwitchExpressionCaseEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitThisExpressionHandle(ThisExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitUnaryPostfixAssignmentExpressionHandle(
+          UnaryPostfixAssignmentExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitUnaryPrefixExpressionHandle(
+          UnaryPrefixExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRelationalPatternHandle(RelationalPatternHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitUnaryPrefixAssignmentExpressionHandle(
+          UnaryPrefixAssignmentExpressionHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFormalParameterDefaultValueExpressionBegin(
+          FormalParameterDefaultValueExpressionBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFormalParameterDefaultValueExpressionEnd(
+          FormalParameterDefaultValueExpressionEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitValuedFormalParameterHandle(
+          ValuedFormalParameterHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitFormalParameterWithoutValueHandle(
+          FormalParameterWithoutValueHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitVoidKeywordHandle(VoidKeywordHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitVoidKeywordWithTypeArgumentsHandle(
+          VoidKeywordWithTypeArgumentsHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitYieldStatementBegin(YieldStatementBegin node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitYieldStatementEnd(YieldStatementEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInvalidYieldStatementEnd(InvalidYieldStatementEnd node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitRecoverableErrorHandle(RecoverableErrorHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitExperimentNotEnabledHandle(
+          ExperimentNotEnabledHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitErrorTokenHandle(ErrorTokenHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitUnescapeErrorHandle(UnescapeErrorHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitInvalidStatementHandle(InvalidStatementHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitScriptHandle(ScriptHandle node) => defaultNode(node);
+
+  @override
+  Future<void> visitTypeArgumentApplicationHandle(
+          TypeArgumentApplicationHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitNewAsIdentifierHandle(NewAsIdentifierHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitPatternVariableDeclarationStatementHandle(
+          PatternVariableDeclarationStatementHandle node) =>
+      defaultNode(node);
+
+  @override
+  Future<void> visitPatternAssignmentHandle(PatternAssignmentHandle node) =>
+      defaultNode(node);
+}
diff --git a/pkg/front_end/test/crashing_test_case_minimizer_impl.dart b/pkg/front_end/test/crashing_test_case_minimizer_impl.dart
index a2e75d6..56f69b5 100644
--- a/pkg/front_end/test/crashing_test_case_minimizer_impl.dart
+++ b/pkg/front_end/test/crashing_test_case_minimizer_impl.dart
@@ -1403,6 +1403,12 @@
             decl.token.offset - 1, decl.token.offset + decl.token.length));
         shouldCompile = true;
         what = "script";
+      } else if (child.isExtensionType()) {
+        ExtensionTypeDeclarationEnd decl = child.asExtensionType();
+        helper.replacements.add(new _Replacement(
+            decl.extensionKeyword.offset - 1, decl.endToken.charEnd));
+        shouldCompile = true;
+        what = "extension type";
       }
 
       if (shouldCompile) {
@@ -1426,80 +1432,7 @@
 
             if (!success) {
               // Also try to remove members one at a time.
-              for (ParserAstNode child in body.children!) {
-                shouldCompile = false;
-                if (child is MemberEnd) {
-                  if (child.isClassConstructor()) {
-                    ClassConstructorEnd memberDecl =
-                        child.getClassConstructor();
-                    helper.replacements.add(new _Replacement(
-                        memberDecl.beginToken.offset - 1,
-                        memberDecl.endToken.offset + 1));
-                    what = "class constructor";
-                    shouldCompile = true;
-                  } else if (child.isClassFields()) {
-                    ClassFieldsEnd memberDecl = child.getClassFields();
-                    helper.replacements.add(new _Replacement(
-                        memberDecl.beginToken.offset - 1,
-                        memberDecl.endToken.offset + 1));
-                    what = "class fields";
-                    shouldCompile = true;
-                  } else if (child.isClassMethod()) {
-                    ClassMethodEnd memberDecl = child.getClassMethod();
-                    helper.replacements.add(new _Replacement(
-                        memberDecl.beginToken.offset - 1,
-                        memberDecl.endToken.offset + 1));
-                    what = "class method";
-                    shouldCompile = true;
-                  } else if (child.isClassFactoryMethod()) {
-                    ClassFactoryMethodEnd memberDecl =
-                        child.getClassFactoryMethod();
-                    helper.replacements.add(new _Replacement(
-                        memberDecl.beginToken.offset - 1,
-                        memberDecl.endToken.offset + 1));
-                    what = "class factory method";
-                    shouldCompile = true;
-                  } else {
-                    // throw "$child --- ${child.children}";
-                    continue;
-                  }
-                } else if (child.isMetadata()) {
-                  MetadataStarEnd decl = child.asMetadata();
-                  List<MetadataEnd> metadata = decl.getMetadataEntries();
-                  if (metadata.isNotEmpty) {
-                    helper.replacements.add(new _Replacement(
-                        metadata.first.beginToken.offset - 1,
-                        metadata.last.endToken.charEnd));
-                    shouldCompile = true;
-                  }
-                  what = "metadata";
-                }
-                if (shouldCompile) {
-                  success = await _tryReplaceAndCompile(
-                      helper, uri, initialComponent, what);
-                  if (helper.shouldQuit) return;
-                  if (!success) {
-                    BlockFunctionBodyEnd? decl;
-                    if (child is MemberEnd) {
-                      if (child.isClassMethod()) {
-                        decl = child.getClassMethod().getBlockFunctionBody();
-                      } else if (child.isClassConstructor()) {
-                        decl =
-                            child.getClassConstructor().getBlockFunctionBody();
-                      }
-                    }
-                    if (decl != null &&
-                        decl.beginToken.offset + 2 < decl.endToken.offset) {
-                      helper.replacements.add(new _Replacement(
-                          decl.beginToken.offset, decl.endToken.offset));
-                      what = "class member content";
-                      await _tryReplaceAndCompile(
-                          helper, uri, initialComponent, what);
-                      if (helper.shouldQuit) return;
-                    }
-                  }
-                }
-              }
+              await _deleteBlocksHelper(body, helper, uri, initialComponent);
             }
 
             // Try to remove "extends", "implements" etc.
@@ -1549,80 +1482,38 @@
             }
 
             if (!success) {
-              // Also try to remove members one at a time.
-              for (ParserAstNode child in body.children!) {
-                shouldCompile = false;
-                if (child is MemberEnd) {
-                  if (child.isMixinConstructor()) {
-                    MixinConstructorEnd memberDecl =
-                        child.getMixinConstructor();
-                    helper.replacements.add(new _Replacement(
-                        memberDecl.beginToken.offset - 1,
-                        memberDecl.endToken.offset + 1));
-                    what = "mixin constructor";
-                    shouldCompile = true;
-                  } else if (child.isMixinFields()) {
-                    MixinFieldsEnd memberDecl = child.getMixinFields();
-                    helper.replacements.add(new _Replacement(
-                        memberDecl.beginToken.offset - 1,
-                        memberDecl.endToken.offset + 1));
-                    what = "mixin fields";
-                    shouldCompile = true;
-                  } else if (child.isMixinMethod()) {
-                    MixinMethodEnd memberDecl = child.getMixinMethod();
-                    helper.replacements.add(new _Replacement(
-                        memberDecl.beginToken.offset - 1,
-                        memberDecl.endToken.offset + 1));
-                    what = "mixin method";
-                    shouldCompile = true;
-                  } else if (child.isMixinFactoryMethod()) {
-                    MixinFactoryMethodEnd memberDecl =
-                        child.getMixinFactoryMethod();
-                    helper.replacements.add(new _Replacement(
-                        memberDecl.beginToken.offset - 1,
-                        memberDecl.endToken.offset + 1));
-                    what = "mixin factory method";
-                    shouldCompile = true;
-                  } else {
-                    // throw "$child --- ${child.children}";
-                    continue;
-                  }
-                } else if (child.isMetadata()) {
-                  MetadataStarEnd decl = child.asMetadata();
-                  List<MetadataEnd> metadata = decl.getMetadataEntries();
-                  if (metadata.isNotEmpty) {
-                    helper.replacements.add(new _Replacement(
-                        metadata.first.beginToken.offset - 1,
-                        metadata.last.endToken.charEnd));
-                    shouldCompile = true;
-                  }
-                  what = "metadata";
-                }
-                if (shouldCompile) {
-                  success = await _tryReplaceAndCompile(
-                      helper, uri, initialComponent, what);
-                  if (helper.shouldQuit) return;
-                  if (!success) {
-                    BlockFunctionBodyEnd? decl;
-                    if (child is MemberEnd) {
-                      if (child.isClassMethod()) {
-                        decl = child.getClassMethod().getBlockFunctionBody();
-                      } else if (child.isClassConstructor()) {
-                        decl =
-                            child.getClassConstructor().getBlockFunctionBody();
-                      }
-                    }
-                    if (decl != null &&
-                        decl.beginToken.offset + 2 < decl.endToken.offset) {
-                      helper.replacements.add(new _Replacement(
-                          decl.beginToken.offset, decl.endToken.offset));
-                      what = "class member content";
-                      await _tryReplaceAndCompile(
-                          helper, uri, initialComponent, what);
-                      if (helper.shouldQuit) return;
-                    }
-                  }
-                }
+              await _deleteBlocksHelper(body, helper, uri, initialComponent);
+            }
+          } else if (child.isExtensionType()) {
+            // Also try to remove all content of the extension type.
+            ExtensionTypeDeclarationEnd decl = child.asExtensionType();
+            ClassOrMixinOrExtensionBodyEnd body =
+                decl.getClassOrMixinOrExtensionBody();
+            if (body.beginToken.offset + 2 < body.endToken.offset) {
+              helper.replacements.add(new _Replacement(
+                  body.beginToken.offset, body.endToken.offset));
+              what = "extension type body";
+              success = await _tryReplaceAndCompile(
+                  helper, uri, initialComponent, what);
+              if (helper.shouldQuit) return;
+            }
+
+            if (!success) {
+              await _deleteBlocksHelper(body, helper, uri, initialComponent);
+            }
+          } else if (child.isTopLevelMethod()) {
+            // Try to remove parameters.
+            TopLevelMethodEnd decl = child.asTopLevelMethod();
+            FormalParametersEnd? formal =
+                decl.children?.whereType<FormalParametersEnd>().firstOrNull;
+            if (formal != null) {
+              if (formal.beginToken.offset + 2 < formal.endToken.offset) {
+                helper.replacements.add(new _Replacement(
+                    formal.beginToken.offset, formal.endToken.offset));
+                what = "top level formals";
+                success = await _tryReplaceAndCompile(
+                    helper, uri, initialComponent, what);
+                if (helper.shouldQuit) return;
               }
             }
           }
@@ -1631,6 +1522,171 @@
     }
   }
 
+  Future<void> _deleteBlocksHelper(
+      ClassOrMixinOrExtensionBodyEnd body,
+      _CompilationHelperClass helper,
+      final Uri uri,
+      Component initialComponent) async {
+    for (ParserAstNode child in body.children!) {
+      bool shouldCompile = false;
+      String what = "";
+      if (child is MemberEnd) {
+        if (child.isClassConstructor()) {
+          ClassConstructorEnd memberDecl = child.getClassConstructor();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "class constructor";
+          shouldCompile = true;
+        } else if (child.isClassFields()) {
+          ClassFieldsEnd memberDecl = child.getClassFields();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "class fields";
+          shouldCompile = true;
+        } else if (child.isClassMethod()) {
+          ClassMethodEnd memberDecl = child.getClassMethod();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "class method";
+          shouldCompile = true;
+        } else if (child.isClassFactoryMethod()) {
+          ClassFactoryMethodEnd memberDecl = child.getClassFactoryMethod();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "class factory method";
+          shouldCompile = true;
+        } else if (child.isMixinConstructor()) {
+          MixinConstructorEnd memberDecl = child.getMixinConstructor();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "mixin constructor";
+          shouldCompile = true;
+        } else if (child.isMixinFields()) {
+          MixinFieldsEnd memberDecl = child.getMixinFields();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "mixin fields";
+          shouldCompile = true;
+        } else if (child.isMixinMethod()) {
+          MixinMethodEnd memberDecl = child.getMixinMethod();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "mixin method";
+          shouldCompile = true;
+        } else if (child.isMixinFactoryMethod()) {
+          MixinFactoryMethodEnd memberDecl = child.getMixinFactoryMethod();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "mixin factory method";
+          shouldCompile = true;
+        } else if (child.isExtensionTypeConstructor()) {
+          var memberDecl = child.getExtensionTypeConstructor();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "extension type constructor";
+          shouldCompile = true;
+        } else if (child.isExtensionTypeFields()) {
+          ExtensionTypeFieldsEnd memberDecl = child.getExtensionTypeFields();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "extension type fields";
+          shouldCompile = true;
+        } else if (child.isExtensionTypeMethod()) {
+          ExtensionTypeMethodEnd memberDecl = child.getExtensionTypeMethod();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "extension type method";
+          shouldCompile = true;
+        } else if (child.isExtensionTypeFactoryMethod()) {
+          ExtensionTypeFactoryMethodEnd memberDecl =
+              child.getExtensionTypeFactoryMethod();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "extension type factory method";
+          shouldCompile = true;
+        } else if (child.isExtensionConstructor()) {
+          var memberDecl = child.getExtensionConstructor();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "extension constructor";
+          shouldCompile = true;
+        } else if (child.isExtensionFields()) {
+          ExtensionFieldsEnd memberDecl = child.getExtensionFields();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "extension fields";
+          shouldCompile = true;
+        } else if (child.isExtensionMethod()) {
+          ExtensionMethodEnd memberDecl = child.getExtensionMethod();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "extension method";
+          shouldCompile = true;
+        } else if (child.isExtensionFactoryMethod()) {
+          ExtensionFactoryMethodEnd memberDecl =
+              child.getExtensionFactoryMethod();
+          helper.replacements.add(new _Replacement(
+              memberDecl.beginToken.offset - 1,
+              memberDecl.endToken.offset + 1));
+          what = "extension factory method";
+          shouldCompile = true;
+        } else {
+          // throw "$child --- ${child.children}";
+          continue;
+        }
+      } else if (child.isMetadata()) {
+        MetadataStarEnd decl = child.asMetadata();
+        List<MetadataEnd> metadata = decl.getMetadataEntries();
+        if (metadata.isNotEmpty) {
+          helper.replacements.add(new _Replacement(
+              metadata.first.beginToken.offset - 1,
+              metadata.last.endToken.charEnd));
+          shouldCompile = true;
+        }
+        what = "metadata";
+      }
+      if (shouldCompile) {
+        bool success =
+            await _tryReplaceAndCompile(helper, uri, initialComponent, what);
+        if (helper.shouldQuit) return;
+        if (!success) {
+          BlockFunctionBodyEnd? decl;
+          if (child is MemberEnd) {
+            if (child.isClassMethod()) {
+              decl = child.getClassMethod().getBlockFunctionBody();
+            } else if (child.isClassConstructor()) {
+              decl = child.getClassConstructor().getBlockFunctionBody();
+            }
+            // TODO(jensj): The other ones too maybe?
+          }
+          if (decl != null &&
+              decl.beginToken.offset + 2 < decl.endToken.offset) {
+            helper.replacements.add(
+                new _Replacement(decl.beginToken.offset, decl.endToken.offset));
+            what = "class member content";
+            await _tryReplaceAndCompile(helper, uri, initialComponent, what);
+            if (helper.shouldQuit) return;
+          }
+        }
+      }
+    }
+  }
+
   Future<bool> _tryReplaceAndCompile(_CompilationHelperClass data, Uri uri,
       Component initialComponent, String what) async {
     if (await _shouldQuit()) {
@@ -1820,6 +1876,11 @@
   }
 
   Version _getLanguageVersion(Uri uri, {bool crashOnFail = true}) {
+    if (_latestCrashingKnownInitialBuilders == null) {
+      // It crashed on the first compile so we have no builders.
+      // We'll just return something.
+      return ExperimentalFlag.nonNullable.enabledVersion;
+    }
     Uri asImportUri = _getImportUri(uri);
     LibraryBuilder? libraryBuilder =
         _latestCrashingKnownInitialBuilders![asImportUri];
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 4709da3..6113a0f 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -38,6 +38,8 @@
 amended
 amortized
 analyses
+analytics
+analyzerinput
 andahalf
 animal
 animated
@@ -126,6 +128,7 @@
 cafebabe
 calloc
 camel
+cap
 capacity
 capitalized
 caption
@@ -202,6 +205,7 @@
 cumulative
 cursor
 cuts
+cutting
 cx
 da
 dacoharkes
@@ -246,6 +250,7 @@
 diagnosticable
 dictionaries
 dictionary
+dicy
 differentiate
 dijkstra
 dijkstras
@@ -314,6 +319,7 @@
 exercises
 exitcode
 exiting
+exotic
 expanded
 expansive
 explainer
@@ -349,6 +355,7 @@
 forbidden
 forces
 foreground
+forever
 forgot
 forrest
 forth
@@ -367,6 +374,7 @@
 futures
 fuzz
 fuzzed
+fuzzer
 fuzzing
 fx
 gallery
@@ -469,6 +477,7 @@
 jk
 jlcontreras
 joo
+jsonrpc
 jumped
 kernels
 kitty
@@ -491,12 +500,14 @@
 listening
 listing
 ln
+locale
 locating
 logd
 logs
 loo
 lookahead
 loopback
+lsp
 ma
 mac
 maker
@@ -513,6 +524,7 @@
 migrations
 mimicking
 minimize
+minimized
 minimizer
 minimizing
 minitest
@@ -559,6 +571,7 @@
 nonexisting
 noo
 noted
+notification
 noting
 nottest
 nq
@@ -590,12 +603,14 @@
 ot
 outbound
 outliers
+outstanding
 overflows
 overhead
 overlay
 overly
 ox
 pack
+packet
 paging
 paint
 parallax
@@ -678,6 +693,7 @@
 refusing
 regards
 regenerate
+regex
 regressions
 reify
 reject
@@ -685,6 +701,7 @@
 remap
 remapping
 remaps
+removeable
 rendition
 reorder
 reordering
@@ -727,6 +744,7 @@
 sdks
 secondary
 secondtest
+secure
 seemingly
 segment
 selection
@@ -752,6 +770,7 @@
 silence
 simplistic
 slashes
+sleep
 slight
 sliver
 smoke
@@ -773,6 +792,7 @@
 sqrt
 squared
 ss
+ssls
 sssp
 stacks
 stashed
@@ -788,6 +808,7 @@
 strip
 strongest
 stub's
+stuck
 stupid
 subcommand
 subdir
@@ -813,6 +834,7 @@
 taskset
 te
 templates
+testfile
 theoretically
 there'll
 thereby
@@ -889,6 +911,7 @@
 virtually
 visualization
 vp
+vscode
 vt
 vte
 waited
@@ -906,6 +929,7 @@
 wording
 workers
 workflow
+workspace
 worlds
 worse
 wrongly
diff --git a/pkg/front_end/tool/fuzz/Dart.g b/pkg/front_end/tool/fuzz/Dart.g
new file mode 100644
index 0000000..d4a7f9d
--- /dev/null
+++ b/pkg/front_end/tool/fuzz/Dart.g
@@ -0,0 +1,2288 @@
+// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// CHANGES:
+//
+// v0.51 Support a `switchExpression` with no cases.
+//
+// v0.50 Add support for digit separators in numeric literals.
+//
+// v0.49 Add support for static and top-level members with no implementation.
+//
+// v0.48 Add support for enhanced parts.
+//
+// v0.47 Make `augment` a built-in identifier (this happened in the feature
+// specification v1.10, but wasn't done here at the time).
+//
+// v0.46 Rename `libraryDefinition` to `libraryDeclaration`, as in the
+// language specification. Add support for libraries with imports.
+//
+// v0.45 Update rule about augmenting extension type declaration to omit
+// the primary constructor.
+//
+// v0.44 Support null-aware elements.
+//
+// v0.43 Change rule structure such that the association of metadata
+// with non-terminals can be explained in a simple and consistent way.
+// The derivable terms do not change. Remove `metadata` from the kind
+// of `forLoopParts` where the iteration variable is an existing variable
+// in scope (this is not implemented, is inconsistent anyway).
+//
+// v0.42 Support updated augmented `extensionDeclaration`.
+//
+// v0.41 Add missing `enumEntry` update for augmentations.
+//
+// v0.40 Include support for augmentation libraries.
+//
+// v0.39 Include latest changes to mixin related class modifiers.
+//
+// v0.38 Broaden `initializerExpression` to match implemented behavior.
+//
+// v0.37 Correct `libraryExport` to use `configurableUri`, not `uri`.
+//
+// v0.36 Update syntax from `inline class` to `extension type`, including
+// a special case of primary constructors.
+//
+// v0.35 Change named optional parameter syntax to require '=', that is,
+// remove the support for ':' as in `void f({int i: 1})`.
+//
+// v0.34 Add support for inline classes.
+//
+// v0.33 This commit does not change the derived language at all. It just
+// changes several rules to use the regexp-like grammar operators to simplify
+// onParts, recordLiteralNoConst, functionTypeTails, and functionType.
+//
+// v0.32 Remove unused non-terminal `patterns`.
+//
+// v0.31 Inline `identifierNotFUNCTION` into `identifier`. Replace all
+// other references with `identifier` to match the spec.
+//
+// v0.30 Add support for the class modifiers `sealed`, `final`, `base`,
+// `interface`, and for `mixin class` declarations. Also add support for
+// unnamed libraries (`library;`). Introduce `otherIdentifier` to help
+// maintaining consistency when the grammar is modified to mention any words
+// that weren't previously mentioned, yet are not reserved or built-in.
+//
+// v0.29 Add an alternative in the `primary` rule to enable method invocations
+// of the form `super(...)` and `super<...>(...)`. This was added to the
+// language specification in May 21, b26e7287c318c0112610fe8b7e175289792dfde2,
+// but the corresponding update here wasn't done here at the time.
+//
+// v0.28 Add support for `new` in `enumEntry`, e.g., `enum E { x.new(); }`.
+// Add `identifierOrNew` non-terminal to simplify the grammar.
+//
+// v0.27 Remove unused non-terminals; make handling of interpolation in URIs
+// consistent with the language specification. Make `partDeclaration` a
+// start symbol in addition to `libraryDefinition` (such that no special
+// precautions are needed in order to parse a part file). Corrected spacing
+// in several rules.
+//
+// v0.26 Add missing `metadata` in `partDeclaration`.
+//
+// v0.25 Update pattern rules following changes to the patterns feature
+// specification since v0.24.
+//
+// v0.24 Change constant pattern rules to allow Symbols and negative numbers.
+//
+// v0.23 Change logical pattern rules to || and &&.
+//
+// v0.22 Change pattern rules, following updated feature specification.
+//
+// v0.21 Add support for patterns.
+//
+// v0.20 Adjust record syntax such that () is allowed (denoting the empty
+// record type and the empty record value).
+//
+// v0.19 Add support for super parameters, named arguments everywhere, and
+// records.
+//
+// v0.18 Add support for enhanced `enum` declarations.
+//
+// v0.17 (58d917e7573c359580ade43845004dbbc62220d5) Correct `uri` to allow
+// multi-line strings (raw and non-raw).
+//
+// v0.16 (284695f1937c262523a9a11b9084213f889c83e0) Correct instance variable
+// declaration syntax such that `covariant late final` is allowed.
+//
+// v0.15 (6facd6dfdafa2953e8523348220d3129ea884678) Add support for
+// constructor tearoffs and explicitly instantiated function tearoffs and
+// type literals.
+//
+// v0.14 (f65c20124edd9e04f7b3a6f014f40c16f51052f6) Correct `partHeader`
+// to allow uri syntax in a `PART OF` directive.
+//
+// v0.13 (bb5cb79a2fd57d6a480b922bc650d5cd15948753) Introduce non-terminals
+// `builtinIdentifier` and `reservedWord`; update `typeAlias` to enable
+// non-function type aliases; add missing `metadata` to formal parameter
+// declarations; correct `symbolLiteral` to allow `VOID`;
+
+// v0.12 (82403371ac00ddf004be60fa7b705474d2864509) Cf. language issue #1341:
+// correct `metadata`. Change `qualifiedName` such that it only includes the
+// cases with a '.'; the remaining case is added where `qualifiedName` is used.
+//
+// v0.11 (67c703063d5b68c9e132edbaf34dfe375851f5a6) Corrections, mainly:
+// `fieldFormalParameter` now allows `?` on the parameter type; cascade was
+// reorganized in the spec, it is now reorganized similarly here; `?` was
+// removed from argumentPart (null-aware invocation was never added).
+//
+// v0.10 (8ccdb9ae796d543e4ad8f339c847c02b09018d2d) Simplify grammar by making
+// `constructorInvocation` an alternative in `primary`.
+//
+// v0.9 (f4d7951a88e1b738e22b768c3bc72bf1a1062365) Introduce abstract and
+// external variables.
+//
+// v0.8 (a9ea9365ad8a3e3b59115bd889a55b6aa2c5a5fa) Change null-aware
+// invocations of `operator []` and `operator []=` to not have a period.
+//
+// v0.7 (6826faf583f6a543b1a0e2e85bd6a8042607ce00) Introduce extension and
+// mixin declarations. Revise rules about string literals and string
+// interpolation. Reorganize "keywords" (built-in identifiers, reserved words,
+// other words that are specified in the grammar and not parsed as IDENTIFIER)
+// into explicitly marked groups. Change the cascade syntax to be
+// compositional.
+//
+// v0.6 (a58052974ec2b4b334922c5227b043ed2b9c2cc5) Introduce syntax associated
+// with null safety.
+//
+// v0.5 (56793b3d4714d4818d855a72074d5295489aef3f) Stop treating `ASYNC` as a
+// conditional reserved word (only `AWAIT` and `YIELD` get this treatment).
+//
+// v0.4 Added support for 'unified collections' (spreads and control flow
+// in collection literals).
+//
+// v0.3 Updated to use ANTLR v4 rather than antlr3.
+//
+// v0.2 Changed top level variable declarations to avoid redundant and
+// misleading occurrence of (FINAL|CONST).
+//
+// v0.1 First version available in the SDK github repository. Covers the
+// Dart language as specified in the language specification based on the
+// many grammar rule snippets. That grammar was then adjusted to remove
+// known issues (e.g., misplaced metadata) and to resolve ambiguities.
+
+grammar Dart;
+
+@parser::header{
+import java.util.Stack;
+}
+
+@lexer::header{
+import java.util.Stack;
+}
+
+@parser::members {
+  static String filePath = null;
+  static boolean errorHasOccurred = false;
+
+  /// Must be invoked before the first error is reported for a library.
+  /// Will print the name of the library and indicate that it has errors.
+  static void prepareForErrors() {
+    errorHasOccurred = true;
+    System.err.println("Syntax error in " + filePath + ":");
+  }
+
+  /// Parse library, return true if success, false if errors occurred.
+  public boolean parseLibrary(String filePath) throws RecognitionException {
+    this.filePath = filePath;
+    errorHasOccurred = false;
+    startSymbol();
+    return !errorHasOccurred;
+  }
+
+  // Enable the parser to treat AWAIT/YIELD as keywords in the body of an
+  // `async`, `async*`, or `sync*` function. Access via methods below.
+  private Stack<Boolean> asyncEtcAreKeywords = new Stack<Boolean>();
+  { asyncEtcAreKeywords.push(false); }
+
+  // Use this to indicate that we are now entering an `async`, `async*`,
+  // or `sync*` function.
+  void startAsyncFunction() { asyncEtcAreKeywords.push(true); }
+
+  // Use this to indicate that we are now entering a function which is
+  // neither `async`, `async*`, nor `sync*`.
+  void startNonAsyncFunction() { asyncEtcAreKeywords.push(false); }
+
+  // Use this to indicate that we are now leaving any function.
+  void endFunction() { asyncEtcAreKeywords.pop(); }
+
+  // Whether we can recognize AWAIT/YIELD as an identifier/typeIdentifier.
+  boolean asyncEtcPredicate(int tokenId) {
+    if (tokenId == AWAIT || tokenId == YIELD) {
+      return !asyncEtcAreKeywords.peek();
+    }
+    return false;
+  }
+}
+
+@lexer::members{
+  public static final int BRACE_NORMAL = 1;
+  public static final int BRACE_SINGLE = 2;
+  public static final int BRACE_DOUBLE = 3;
+  public static final int BRACE_THREE_SINGLE = 4;
+  public static final int BRACE_THREE_DOUBLE = 5;
+
+  // Enable the parser to handle string interpolations via brace matching.
+  // The top of the `braceLevels` stack describes the most recent unmatched
+  // '{'. This is needed in order to enable/disable certain lexer rules.
+  //
+  //   NORMAL: Most recent unmatched '{' was not string literal related.
+  //   SINGLE: Most recent unmatched '{' was `'...${`.
+  //   DOUBLE: Most recent unmatched '{' was `"...${`.
+  //   THREE_SINGLE: Most recent unmatched '{' was `'''...${`.
+  //   THREE_DOUBLE: Most recent unmatched '{' was `"""...${`.
+  //
+  // Access via functions below.
+  private Stack<Integer> braceLevels = new Stack<Integer>();
+
+  // Whether we are currently in a string literal context, and which one.
+  boolean currentBraceLevel(int braceLevel) {
+    if (braceLevels.empty()) return false;
+    return braceLevels.peek() == braceLevel;
+  }
+
+  // Use this to indicate that we are now entering a specific '{...}'.
+  // Call it after accepting the '{'.
+  void enterBrace() {
+    braceLevels.push(BRACE_NORMAL);
+  }
+  void enterBraceSingleQuote() {
+    braceLevels.push(BRACE_SINGLE);
+  }
+  void enterBraceDoubleQuote() {
+    braceLevels.push(BRACE_DOUBLE);
+  }
+  void enterBraceThreeSingleQuotes() {
+    braceLevels.push(BRACE_THREE_SINGLE);
+  }
+  void enterBraceThreeDoubleQuotes() {
+    braceLevels.push(BRACE_THREE_DOUBLE);
+  }
+
+  // Use this to indicate that we are now exiting a specific '{...}',
+  // no matter which kind. Call it before accepting the '}'.
+  void exitBrace() {
+      // We might raise a parse error here if the stack is empty, but the
+      // parsing rules should ensure that we get a parse error anyway, and
+      // it is not a big problem for the spec parser even if it misinterprets
+      // the brace structure of some programs with syntax errors.
+      if (!braceLevels.empty()) braceLevels.pop();
+  }
+}
+
+// ---------------------------------------- Grammar rules.
+
+startSymbol
+    :    libraryDeclaration
+    |    partDeclaration
+    ;
+
+libraryDeclaration
+    :    FEFF? SCRIPT_TAG?
+         libraryName?
+         importOrExport*
+         partDirective*
+         (metadata topLevelDefinition)*
+         EOF
+    ;
+
+topLevelDefinition
+    :    classDeclaration
+    |    mixinDeclaration
+    |    extensionTypeDeclaration
+    |    extensionDeclaration
+    |    enumType
+    |    typeAlias
+    |    AUGMENT? EXTERNAL functionSignature ';'
+    |    AUGMENT? EXTERNAL getterSignature ';'
+    |    AUGMENT? EXTERNAL setterSignature ';'
+    |    AUGMENT? EXTERNAL finalVarOrType identifierList ';'
+    |    AUGMENT? getterSignature (functionBody | ';')
+    |    AUGMENT? setterSignature (functionBody | ';')
+    |    AUGMENT? functionSignature (functionBody | ';')
+    |    AUGMENT? (FINAL | CONST) type? initializedIdentifierList ';'
+    |    AUGMENT? LATE FINAL type? initializedIdentifierList ';'
+    |    AUGMENT? LATE? varOrType initializedIdentifierList ';'
+    ;
+
+declaredIdentifier
+    :    COVARIANT? finalConstVarOrType identifier
+    ;
+
+finalConstVarOrType
+    :    LATE? FINAL type?
+    |    CONST type?
+    |    LATE? varOrType
+    ;
+
+finalVarOrType
+    :    FINAL type?
+    |    varOrType
+    ;
+
+varOrType
+    :    VAR
+    |    type
+    ;
+
+initializedIdentifier
+    :    identifier ('=' expression)?
+    ;
+
+initializedIdentifierList
+    :    initializedIdentifier (',' initializedIdentifier)*
+    ;
+
+functionSignature
+    :    type? identifier formalParameterPart
+    ;
+
+functionBody
+    :    '=>' { startNonAsyncFunction(); } expression { endFunction(); } ';'
+    |    { startNonAsyncFunction(); } block { endFunction(); }
+    |    ASYNC '=>'
+         { startAsyncFunction(); } expression { endFunction(); } ';'
+    |    (ASYNC | ASYNC '*' | SYNC '*')
+         { startAsyncFunction(); } block { endFunction(); }
+    ;
+
+block
+    :    LBRACE statements RBRACE
+    ;
+
+formalParameterPart
+    :    typeParameters? formalParameterList
+    ;
+
+formalParameterList
+    :    '(' ')'
+    |    '(' normalFormalParameters ','? ')'
+    |    '(' normalFormalParameters ',' optionalOrNamedFormalParameters ')'
+    |    '(' optionalOrNamedFormalParameters ')'
+    ;
+
+normalFormalParameters
+    :    normalFormalParameter (',' normalFormalParameter)*
+    ;
+
+optionalOrNamedFormalParameters
+    :    optionalPositionalFormalParameters
+    |    namedFormalParameters
+    ;
+
+optionalPositionalFormalParameters
+    :    '[' defaultFormalParameter (',' defaultFormalParameter)* ','? ']'
+    ;
+
+namedFormalParameters
+    :    LBRACE defaultNamedParameter (',' defaultNamedParameter)* ','? RBRACE
+    ;
+
+normalFormalParameter
+    :    metadata normalFormalParameterNoMetadata
+    ;
+
+normalFormalParameterNoMetadata
+    :    functionFormalParameter
+    |    fieldFormalParameter
+    |    simpleFormalParameter
+    |    superFormalParameter
+    ;
+
+// NB: It is an anomaly that a functionFormalParameter cannot be FINAL.
+functionFormalParameter
+    :    COVARIANT? type? identifier formalParameterPart '?'?
+    ;
+
+simpleFormalParameter
+    :    declaredIdentifier
+    |    COVARIANT? identifier
+    ;
+
+// NB: It is an anomaly that VAR can be a return type (`var this.x()`).
+fieldFormalParameter
+    :    finalConstVarOrType? THIS '.' identifier (formalParameterPart '?'?)?
+    ;
+
+superFormalParameter
+    :    type? SUPER '.' identifier (formalParameterPart '?'?)?
+    ;
+
+defaultFormalParameter
+    :    normalFormalParameter ('=' expression)?
+    ;
+
+defaultNamedParameter
+    :    metadata REQUIRED? normalFormalParameterNoMetadata ('=' expression)?
+    ;
+
+typeWithParameters
+    :    typeIdentifier typeParameters?
+    ;
+
+classDeclaration
+    :    AUGMENT? (classModifiers | mixinClassModifiers)
+         CLASS typeWithParameters superclass? interfaces?
+         LBRACE (metadata classMemberDeclaration)* RBRACE
+    |    classModifiers MIXIN? CLASS mixinApplicationClass
+    ;
+
+classModifiers
+    :    SEALED
+    |    ABSTRACT? (BASE | INTERFACE | FINAL)?
+    ;
+
+mixinClassModifiers
+    :    ABSTRACT? BASE? MIXIN
+    ;
+
+superclass
+    :    EXTENDS typeNotVoidNotFunction mixins?
+    |    mixins
+    ;
+
+mixins
+    :    WITH typeNotVoidNotFunctionList
+    ;
+
+interfaces
+    :    IMPLEMENTS typeNotVoidNotFunctionList
+    ;
+
+classMemberDeclaration
+    :    AUGMENT? methodSignature functionBody
+    |    AUGMENT? declaration ';'
+    ;
+
+mixinApplicationClass
+    :    typeWithParameters '=' mixinApplication ';'
+    ;
+
+mixinDeclaration
+    :    AUGMENT? BASE? MIXIN typeWithParameters
+         (ON typeNotVoidNotFunctionList)? interfaces?
+         LBRACE (metadata mixinMemberDeclaration)* RBRACE
+    ;
+
+// TODO: We might want to make this more strict.
+mixinMemberDeclaration
+    :    classMemberDeclaration
+    ;
+
+extensionTypeDeclaration
+    :    EXTENSION TYPE CONST? typeWithParameters
+         representationDeclaration interfaces?
+         LBRACE (metadata extensionTypeMemberDeclaration)* RBRACE
+    |    AUGMENT EXTENSION TYPE typeWithParameters interfaces?
+         LBRACE (metadata extensionTypeMemberDeclaration)* RBRACE
+    ;
+
+representationDeclaration
+    :    ('.' identifierOrNew)? '(' metadata typedIdentifier ')'
+    ;
+
+
+// TODO: We might want to make this more strict.
+extensionTypeMemberDeclaration
+    :    classMemberDeclaration
+    ;
+
+extensionDeclaration
+    :    EXTENSION typeIdentifierNotType? typeParameters? ON type extensionBody
+    |    AUGMENT EXTENSION typeIdentifierNotType typeParameters? extensionBody
+    ;
+
+extensionBody
+    :    LBRACE (metadata extensionMemberDeclaration)* RBRACE
+    ;
+
+// TODO: We might want to make this more strict.
+extensionMemberDeclaration
+    :    classMemberDeclaration
+    ;
+
+methodSignature
+    :    constructorSignature initializers
+    |    factoryConstructorSignature
+    |    STATIC? functionSignature
+    |    STATIC? getterSignature
+    |    STATIC? setterSignature
+    |    operatorSignature
+    |    constructorSignature
+    ;
+
+declaration
+    :    EXTERNAL? factoryConstructorSignature
+    |    EXTERNAL constantConstructorSignature
+    |    EXTERNAL constructorSignature
+    |    EXTERNAL? STATIC? getterSignature
+    |    EXTERNAL? STATIC? setterSignature
+    |    EXTERNAL? STATIC? functionSignature
+    |    EXTERNAL (STATIC? finalVarOrType | COVARIANT varOrType) identifierList
+    |    EXTERNAL? operatorSignature
+    |    ABSTRACT (finalVarOrType | COVARIANT varOrType) identifierList
+    |    STATIC (FINAL | CONST) type? initializedIdentifierList
+    |    STATIC LATE FINAL type? initializedIdentifierList
+    |    STATIC LATE? varOrType initializedIdentifierList
+    |    COVARIANT LATE FINAL type? identifierList
+    |    COVARIANT LATE? varOrType initializedIdentifierList
+    |    LATE? (FINAL type? | varOrType) initializedIdentifierList
+    |    redirectingFactoryConstructorSignature
+    |    constantConstructorSignature (redirection | initializers)?
+    |    constructorSignature (redirection | initializers)?
+    ;
+
+operatorSignature
+    :    type? OPERATOR operator formalParameterList
+    ;
+
+operator
+    :    '~'
+    |    binaryOperator
+    |    '[' ']'
+    |    '[' ']' '='
+    ;
+
+binaryOperator
+    :    multiplicativeOperator
+    |    additiveOperator
+    |    shiftOperator
+    |    relationalOperator
+    |    '=='
+    |    bitwiseOperator
+    ;
+
+getterSignature
+    :    type? GET identifier
+    ;
+
+setterSignature
+    :    type? SET identifier formalParameterList
+    ;
+
+constructorSignature
+    :    constructorName formalParameterList
+    ;
+
+constructorName
+    :    typeIdentifier ('.' identifierOrNew)?
+    ;
+
+// TODO: Add this in the language specification, use it in grammar rules.
+identifierOrNew
+    :    identifier
+    |    NEW
+    ;
+
+redirection
+    :    ':' THIS ('.' identifierOrNew)? arguments
+    ;
+
+initializers
+    :    ':' initializerListEntry (',' initializerListEntry)*
+    ;
+
+initializerListEntry
+    :    SUPER arguments
+    |    SUPER '.' identifierOrNew arguments
+    |    fieldInitializer
+    |    assertion
+    ;
+
+fieldInitializer
+    :    (THIS '.')? identifier '=' initializerExpression
+    ;
+
+initializerExpression
+    :    throwExpression
+    |    assignableExpression assignmentOperator expression
+    |    conditionalExpression
+    |    cascade
+    ;
+
+factoryConstructorSignature
+    :    CONST? FACTORY constructorName formalParameterList
+    ;
+
+redirectingFactoryConstructorSignature
+    :    CONST? FACTORY constructorName formalParameterList '='
+         constructorDesignation
+    ;
+
+constantConstructorSignature
+    :    CONST constructorName formalParameterList
+    ;
+
+mixinApplication
+    :    typeNotVoidNotFunction mixins interfaces?
+    ;
+
+enumType
+    :    AUGMENT? ENUM typeWithParameters mixins? interfaces? LBRACE
+         enumEntry (',' enumEntry)* (',')?
+         (';' (metadata classMemberDeclaration)*)?
+         RBRACE
+    ;
+
+enumEntry
+    :    metadata AUGMENT? identifier argumentPart?
+    |    metadata AUGMENT? identifier typeArguments?
+         '.' identifierOrNew arguments
+    ;
+
+typeParameter
+    :    metadata typeIdentifier (EXTENDS typeNotVoid)?
+    ;
+
+typeParameters
+    :    '<' typeParameter (',' typeParameter)* '>'
+    ;
+
+metadata
+    :    ('@' metadatum)*
+    ;
+
+metadatum
+    :    constructorDesignation arguments
+    |    identifier
+    |    qualifiedName
+    ;
+
+expression
+    :    patternAssignment
+    |    functionExpression
+    |    throwExpression
+    |    assignableExpression assignmentOperator expression
+    |    conditionalExpression
+    |    cascade
+    ;
+
+expressionWithoutCascade
+    :    functionExpressionWithoutCascade
+    |    throwExpressionWithoutCascade
+    |    assignableExpression assignmentOperator expressionWithoutCascade
+    |    conditionalExpression
+    ;
+
+expressionList
+    :    expression (',' expression)*
+    ;
+
+primary
+    :    thisExpression
+    |    SUPER unconditionalAssignableSelector
+    |    SUPER argumentPart
+    |    functionPrimary
+    |    literal
+    |    identifier
+    |    newExpression
+    |    constObjectExpression
+    |    constructorInvocation
+    |    '(' expression ')'
+    |    constructorTearoff
+    |    switchExpression
+    ;
+
+constructorInvocation
+    :    typeName typeArguments '.' NEW arguments
+    |    typeName '.' NEW arguments
+    ;
+
+literal
+    :    nullLiteral
+    |    booleanLiteral
+    |    numericLiteral
+    |    stringLiteral
+    |    symbolLiteral
+    |    setOrMapLiteral
+    |    listLiteral
+    |    recordLiteral
+    ;
+
+nullLiteral
+    :    NULL
+    ;
+
+numericLiteral
+    :    NUMBER
+    |    HEX_NUMBER
+    ;
+
+booleanLiteral
+    :    TRUE
+    |    FALSE
+    ;
+
+stringLiteral
+    :    (multiLineString | singleLineString)+
+    ;
+
+setOrMapLiteral
+    :    CONST? typeArguments? LBRACE elements? RBRACE
+    ;
+
+listLiteral
+    :    CONST? typeArguments? '[' elements? ']'
+    ;
+
+recordLiteral
+    :    CONST? recordLiteralNoConst
+    ;
+
+recordLiteralNoConst
+    :    '(' ')'
+    |    '(' expression ',' ')'
+    |    '(' label expression ','? ')'
+    |    '(' recordField (',' recordField)+ ','? ')'
+    ;
+
+recordField
+    :    label? expression
+    ;
+
+elements
+    :    element (',' element)* ','?
+    ;
+
+element
+    :    nullAwareExpressionElement
+    |    nullAwareMapElement
+    |    expressionElement
+    |    mapElement
+    |    spreadElement
+    |    ifElement
+    |    forElement
+    ;
+
+nullAwareExpressionElement
+    :    '?' expression
+    ;
+
+nullAwareMapElement
+    :    '?' expression ':' '?'? expression
+    |    expression ':' '?' expression
+    ;
+
+expressionElement
+    :    expression
+    ;
+
+mapElement
+    :    expression ':' expression
+    ;
+
+spreadElement
+    :    ('...' | '...?') expression
+    ;
+
+ifElement
+    :    ifCondition element (ELSE element)?
+    ;
+
+forElement
+    :    AWAIT? FOR '(' forLoopParts ')' element
+    ;
+
+constructorTearoff
+    :    typeName typeArguments? '.' NEW
+    ;
+
+switchExpression
+    :    SWITCH '(' expression ')'
+         LBRACE (switchExpressionCase (',' switchExpressionCase)* ','?)? RBRACE
+    ;
+
+switchExpressionCase
+    :    guardedPattern '=>' expression
+    ;
+
+throwExpression
+    :    THROW expression
+    ;
+
+throwExpressionWithoutCascade
+    :    THROW expressionWithoutCascade
+    ;
+
+functionExpression
+    :    formalParameterPart functionExpressionBody
+    ;
+
+functionExpressionBody
+    :    '=>' { startNonAsyncFunction(); } expression { endFunction(); }
+    |    ASYNC '=>' { startAsyncFunction(); } expression { endFunction(); }
+    ;
+
+functionExpressionWithoutCascade
+    :    formalParameterPart functionExpressionWithoutCascadeBody
+    ;
+
+functionExpressionWithoutCascadeBody
+    :    '=>' { startNonAsyncFunction(); }
+         expressionWithoutCascade { endFunction(); }
+    |    ASYNC '=>' { startAsyncFunction(); }
+         expressionWithoutCascade { endFunction(); }
+    ;
+
+functionPrimary
+    :    formalParameterPart functionPrimaryBody
+    ;
+
+functionPrimaryBody
+    :    { startNonAsyncFunction(); } block { endFunction(); }
+    |    (ASYNC | ASYNC '*' | SYNC '*')
+         { startAsyncFunction(); } block { endFunction(); }
+    ;
+
+thisExpression
+    :    THIS
+    ;
+
+newExpression
+    :    NEW constructorDesignation arguments
+    ;
+
+constObjectExpression
+    :    CONST constructorDesignation arguments
+    ;
+
+arguments
+    :    '(' (argumentList ','?)? ')'
+    ;
+
+argumentList
+    :    argument (',' argument)*
+    ;
+
+argument
+    :    label? expression
+    ;
+
+cascade
+    :     cascade '..' cascadeSection
+    |     conditionalExpression ('?..' | '..') cascadeSection
+    ;
+
+cascadeSection
+    :    cascadeSelector cascadeSectionTail
+    ;
+
+cascadeSelector
+    :    '[' expression ']'
+    |    identifier
+    ;
+
+cascadeSectionTail
+    :    cascadeAssignment
+    |    selector* (assignableSelector cascadeAssignment)?
+    ;
+
+cascadeAssignment
+    :    assignmentOperator expressionWithoutCascade
+    ;
+
+assignmentOperator
+    :    '='
+    |    compoundAssignmentOperator
+    ;
+
+compoundAssignmentOperator
+    :    '*='
+    |    '/='
+    |    '~/='
+    |    '%='
+    |    '+='
+    |    '-='
+    |    '<<='
+    |    '>' '>' '>' '='
+    |    '>' '>' '='
+    |    '&='
+    |    '^='
+    |    '|='
+    |    '??='
+    ;
+
+conditionalExpression
+    :    ifNullExpression
+         ('?' expressionWithoutCascade ':' expressionWithoutCascade)?
+    ;
+
+ifNullExpression
+    :    logicalOrExpression ('??' logicalOrExpression)*
+    ;
+
+logicalOrExpression
+    :    logicalAndExpression ('||' logicalAndExpression)*
+    ;
+
+logicalAndExpression
+    :    equalityExpression ('&&' equalityExpression)*
+    ;
+
+equalityExpression
+    :    relationalExpression (equalityOperator relationalExpression)?
+    |    SUPER equalityOperator relationalExpression
+    ;
+
+equalityOperator
+    :    '=='
+    |    '!='
+    ;
+
+relationalExpression
+    :    bitwiseOrExpression
+         (typeTest | typeCast | relationalOperator bitwiseOrExpression)?
+    |    SUPER relationalOperator bitwiseOrExpression
+    ;
+
+relationalOperator
+    :    '>' '='
+    |    '>'
+    |    '<='
+    |    '<'
+    ;
+
+bitwiseOrExpression
+    :    bitwiseXorExpression ('|' bitwiseXorExpression)*
+    |    SUPER ('|' bitwiseXorExpression)+
+    ;
+
+bitwiseXorExpression
+    :    bitwiseAndExpression ('^' bitwiseAndExpression)*
+    |    SUPER ('^' bitwiseAndExpression)+
+    ;
+
+bitwiseAndExpression
+    :    shiftExpression ('&' shiftExpression)*
+    |    SUPER ('&' shiftExpression)+
+    ;
+
+bitwiseOperator
+    :    '&'
+    |    '^'
+    |    '|'
+    ;
+
+shiftExpression
+    :    additiveExpression (shiftOperator additiveExpression)*
+    |    SUPER (shiftOperator additiveExpression)+
+    ;
+
+shiftOperator
+    :    '<<'
+    |    '>' '>' '>'
+    |    '>' '>'
+    ;
+
+additiveExpression
+    :    multiplicativeExpression (additiveOperator multiplicativeExpression)*
+    |    SUPER (additiveOperator multiplicativeExpression)+
+    ;
+
+additiveOperator
+    :    '+'
+    |    '-'
+    ;
+
+multiplicativeExpression
+    :    unaryExpression (multiplicativeOperator unaryExpression)*
+    |    SUPER (multiplicativeOperator unaryExpression)+
+    ;
+
+multiplicativeOperator
+    :    '*'
+    |    '/'
+    |    '%'
+    |    '~/'
+    ;
+
+unaryExpression
+    :    prefixOperator unaryExpression
+    |    awaitExpression
+    |    postfixExpression
+    |    (minusOperator | tildeOperator) SUPER
+    |    incrementOperator assignableExpression
+    ;
+
+prefixOperator
+    :    minusOperator
+    |    negationOperator
+    |    tildeOperator
+    ;
+
+minusOperator
+    :    '-'
+    ;
+
+negationOperator
+    :    '!'
+    ;
+
+tildeOperator
+    :    '~'
+    ;
+
+awaitExpression
+    :    AWAIT unaryExpression
+    ;
+
+postfixExpression
+    :    assignableExpression postfixOperator
+    |    primary selector*
+    ;
+
+postfixOperator
+    :    incrementOperator
+    ;
+
+selector
+    :    '!'
+    |    assignableSelector
+    |    argumentPart
+    |    typeArguments
+    ;
+
+argumentPart
+    :    typeArguments? arguments
+    ;
+
+incrementOperator
+    :    '++'
+    |    '--'
+    ;
+
+assignableExpression
+    :    SUPER unconditionalAssignableSelector
+    |    primary assignableSelectorPart
+    |    identifier
+    ;
+
+assignableSelectorPart
+    :    selector* assignableSelector
+    ;
+
+unconditionalAssignableSelector
+    :    '[' expression ']'
+    |    '.' identifier
+    ;
+
+assignableSelector
+    :    unconditionalAssignableSelector
+    |    '?.' identifier
+    |    '?' '[' expression ']'
+    ;
+
+identifier
+    :    IDENTIFIER
+    |    builtInIdentifier
+    |    otherIdentifier
+    |    { asyncEtcPredicate(getCurrentToken().getType()) }? (AWAIT|YIELD)
+    ;
+
+qualifiedName
+    :    typeIdentifier '.' identifierOrNew
+    |    typeIdentifier '.' typeIdentifier '.' identifierOrNew
+    ;
+
+typeIdentifierNotType
+    :    IDENTIFIER
+    |    DYNAMIC // Built-in identifier that can be used as a type.
+    |    otherIdentifierNotType // Occur in grammar rules, are not built-in.
+    |    { asyncEtcPredicate(getCurrentToken().getType()) }? (AWAIT|YIELD)
+    ;
+
+typeIdentifier
+    :    typeIdentifierNotType
+    |    TYPE
+    ;
+
+typeTest
+    :    isOperator typeNotVoid
+    ;
+
+isOperator
+    :    IS '!'?
+    ;
+
+typeCast
+    :    asOperator typeNotVoid
+    ;
+
+asOperator
+    :    AS
+    ;
+
+pattern
+    :    logicalOrPattern
+    ;
+
+logicalOrPattern
+    :    logicalAndPattern ('||' logicalAndPattern)*
+    ;
+
+logicalAndPattern
+    :    relationalPattern ('&&' relationalPattern)*
+    ;
+
+relationalPattern
+    :    (equalityOperator | relationalOperator) bitwiseOrExpression
+    |    unaryPattern
+    ;
+
+unaryPattern
+    :    castPattern
+    |    nullCheckPattern
+    |    nullAssertPattern
+    |    primaryPattern
+    ;
+
+primaryPattern
+    :    constantPattern
+    |    variablePattern
+    |    parenthesizedPattern
+    |    listPattern
+    |    mapPattern
+    |    recordPattern
+    |    objectPattern
+    ;
+
+castPattern
+    :    primaryPattern AS type
+    ;
+
+nullCheckPattern
+    :    primaryPattern '?'
+    ;
+
+nullAssertPattern
+    :    primaryPattern '!'
+    ;
+
+constantPattern
+    :    booleanLiteral
+    |    nullLiteral
+    |    '-'? numericLiteral
+    |    stringLiteral
+    |    symbolLiteral
+    |    identifier
+    |    qualifiedName
+    |    constObjectExpression
+    |    CONST typeArguments? '[' elements? ']'
+    |    CONST typeArguments? LBRACE elements? RBRACE
+    |    CONST '(' expression ')'
+    ;
+
+variablePattern
+    :    (VAR | FINAL | FINAL? type)? identifier
+    ;
+
+parenthesizedPattern
+    :    '(' pattern ')'
+    ;
+
+listPattern
+    :    typeArguments? '[' listPatternElements? ']'
+    ;
+
+listPatternElements
+    :    listPatternElement (',' listPatternElement)* ','?
+    ;
+
+listPatternElement
+    :    pattern
+    |    restPattern
+    ;
+
+restPattern
+    :    '...' pattern?
+    ;
+
+mapPattern
+    :    typeArguments? LBRACE mapPatternEntries? RBRACE
+    ;
+
+mapPatternEntries
+    :    mapPatternEntry (',' mapPatternEntry)* ','?
+    ;
+
+mapPatternEntry
+    :    expression ':' pattern
+    |    '...'
+    ;
+
+recordPattern
+    :    '(' patternFields? ')'
+    ;
+
+patternFields
+    :    patternField (',' patternField)* ','?
+    ;
+
+patternField
+    :    (identifier? ':')? pattern
+    ;
+
+objectPattern
+    :    (typeName typeArguments? | typeNamedFunction) '(' patternFields? ')'
+    ;
+
+patternVariableDeclaration
+    :    outerPatternDeclarationPrefix '=' expression
+    ;
+
+outerPattern
+    :    parenthesizedPattern
+    |    listPattern
+    |    mapPattern
+    |    recordPattern
+    |    objectPattern
+    ;
+
+outerPatternDeclarationPrefix
+    :    (FINAL | VAR) outerPattern
+    ;
+
+patternAssignment
+    :    outerPattern '=' expression
+    ;
+
+statements
+    :    statement*
+    ;
+
+statement
+    :    label* nonLabelledStatement
+    ;
+
+// Exception in the language specification: An expressionStatement cannot
+// start with LBRACE. We force anything that starts with LBRACE to be a block,
+// which will prevent an expressionStatement from starting with LBRACE, and
+// which will not interfere with the recognition of any other case. If we
+// add another statement which can start with LBRACE we must adjust this
+// check.
+nonLabelledStatement
+    :    block
+    |    localVariableDeclaration
+    |    forStatement
+    |    whileStatement
+    |    doStatement
+    |    switchStatement
+    |    ifStatement
+    |    rethrowStatement
+    |    tryStatement
+    |    breakStatement
+    |    continueStatement
+    |    returnStatement
+    |    localFunctionDeclaration
+    |    assertStatement
+    |    yieldStatement
+    |    yieldEachStatement
+    |    expressionStatement
+    ;
+
+expressionStatement
+    :    expression? ';'
+    ;
+
+localVariableDeclaration
+    :    metadata initializedVariableDeclaration ';'
+    |    metadata patternVariableDeclaration ';'
+    ;
+
+initializedVariableDeclaration
+    :    declaredIdentifier ('=' expression)? (',' initializedIdentifier)*
+    ;
+
+localFunctionDeclaration
+    :    metadata functionSignature functionBody
+    ;
+
+ifStatement
+    :    ifCondition statement (ELSE statement)?
+    ;
+
+ifCondition
+    :    IF '(' expression (CASE guardedPattern)? ')'
+    ;
+
+forStatement
+    :    AWAIT? FOR '(' forLoopParts ')' statement
+    ;
+
+forLoopParts
+    :    forInLoopPrefix IN expression
+    |    forInitializerStatement expression? ';' expressionList?
+    ;
+
+forInLoopPrefix
+    :    metadata declaredIdentifier
+    |    metadata outerPatternDeclarationPrefix
+    |    identifier
+    ;
+
+// The localVariableDeclaration cannot be CONST, but that can
+// be enforced in a later phase, and the grammar allows it.
+forInitializerStatement
+    :    localVariableDeclaration
+    |    expression? ';'
+    ;
+
+whileStatement
+    :    WHILE '(' expression ')' statement
+    ;
+
+doStatement
+    :    DO statement WHILE '(' expression ')' ';'
+    ;
+
+switchStatement
+    :    SWITCH '(' expression ')'
+         LBRACE switchStatementCase* switchStatementDefault? RBRACE
+    ;
+
+switchStatementCase
+    :    label* CASE guardedPattern ':' statements
+    ;
+
+guardedPattern
+    :    pattern (WHEN expression)?
+    ;
+
+switchStatementDefault
+    :    label* DEFAULT ':' statements
+    ;
+
+rethrowStatement
+    :    RETHROW ';'
+    ;
+
+tryStatement
+    :    TRY block (onPart+ finallyPart? | finallyPart)
+    ;
+
+onPart
+    :    catchPart block
+    |    ON typeNotVoid catchPart? block
+    ;
+
+catchPart
+    :    CATCH '(' identifier (',' identifier)? ')'
+    ;
+
+finallyPart
+    :    FINALLY block
+    ;
+
+returnStatement
+    :    RETURN expression? ';'
+    ;
+
+label
+    :    identifier ':'
+    ;
+
+breakStatement
+    :    BREAK identifier? ';'
+    ;
+
+continueStatement
+    :    CONTINUE identifier? ';'
+    ;
+
+yieldStatement
+    :    YIELD expression ';'
+    ;
+
+yieldEachStatement
+    :    YIELD '*' expression ';'
+    ;
+
+assertStatement
+    :    assertion ';'
+    ;
+
+assertion
+    :    ASSERT '(' expression (',' expression)? ','? ')'
+    ;
+
+libraryName
+    :    metadata libraryNameBody ';'
+    ;
+
+libraryNameBody
+    :    LIBRARY dottedIdentifierList?
+    |    AUGMENT LIBRARY uri
+    ;
+
+dottedIdentifierList
+    :    identifier ('.' identifier)*
+    ;
+
+importOrExport
+    :    libraryImport
+    |    libraryAugmentImport
+    |    libraryExport
+    ;
+
+libraryImport
+    :    metadata importSpecification
+    ;
+
+libraryAugmentImport
+    :    metadata IMPORT AUGMENT uri ';'
+    ;
+
+importSpecification
+    :    IMPORT configurableUri (DEFERRED? AS typeIdentifier)? combinator* ';'
+    ;
+
+combinator
+    :    SHOW identifierList
+    |    HIDE identifierList
+    ;
+
+identifierList
+    :    identifier (',' identifier)*
+    ;
+
+libraryExport
+    :    metadata EXPORT configurableUri combinator* ';'
+    ;
+
+partDirective
+    :    metadata PART configurableUri ';'
+    ;
+
+partHeader
+    :    metadata PART OF uri ';'
+    ;
+
+partDeclaration
+    :    FEFF? partHeader
+         importOrExport*
+         partDirective*
+         (metadata topLevelDefinition)*
+         EOF
+    ;
+
+uri
+    :    stringLiteral
+    ;
+
+configurableUri
+    :    uri configurationUri*
+    ;
+
+configurationUri
+    :    IF '(' uriTest ')' uri
+    ;
+
+uriTest
+    :    dottedIdentifierList ('==' stringLiteral)?
+    ;
+
+type
+    :    functionType '?'?
+    |    typeNotFunction
+    ;
+
+typeNotVoid
+    :    functionType '?'?
+    |    recordType '?'?
+    |    typeNotVoidNotFunction '?'?
+    ;
+
+typeNotFunction
+    :    typeNotVoidNotFunction '?'?
+    |    recordType '?'?
+    |    VOID
+    ;
+
+typeNamedFunction
+    :    (typeIdentifier '.')? FUNCTION
+    ;
+
+typeNotVoidNotFunction
+    :    typeName typeArguments?
+    |    typeNamedFunction
+    ;
+
+typeName
+    :    typeIdentifier ('.' typeIdentifier)?
+    ;
+
+typeArguments
+    :    '<' typeList '>'
+    ;
+
+typeList
+    :    type (',' type)*
+    ;
+
+recordType
+    :    '(' ')'
+    |    '(' recordTypeFields ',' recordTypeNamedFields ')'
+    |    '(' recordTypeFields ','? ')'
+    |    '(' recordTypeNamedFields ')'
+    ;
+
+recordTypeFields
+    :    recordTypeField (',' recordTypeField)*
+    ;
+
+recordTypeField
+    :    metadata type identifier?
+    ;
+
+recordTypeNamedFields
+    :    LBRACE recordTypeNamedField (',' recordTypeNamedField)* ','? RBRACE
+    ;
+
+recordTypeNamedField
+    :    metadata typedIdentifier
+    ;
+
+typeNotVoidNotFunctionList
+    :    typeNotVoidNotFunction (',' typeNotVoidNotFunction)*
+    ;
+
+typeAlias
+    :    AUGMENT? TYPEDEF typeWithParameters '=' type ';'
+    |    AUGMENT? TYPEDEF functionTypeAlias
+    ;
+
+functionTypeAlias
+    :    functionPrefix formalParameterPart ';'
+    ;
+
+functionPrefix
+    :    type identifier
+    |    identifier
+    ;
+
+functionTypeTail
+    :    FUNCTION typeParameters? parameterTypeList
+    ;
+
+functionTypeTails
+    :    (functionTypeTail '?'?)* functionTypeTail
+    ;
+
+functionType
+    :    typeNotFunction? functionTypeTails
+    ;
+
+parameterTypeList
+    :    '(' ')'
+    |    '(' normalParameterTypes ',' optionalParameterTypes ')'
+    |    '(' normalParameterTypes ','? ')'
+    |    '(' optionalParameterTypes ')'
+    ;
+
+normalParameterTypes
+    :    normalParameterType (',' normalParameterType)*
+    ;
+
+normalParameterType
+    :    metadata typedIdentifier
+    |    metadata type
+    ;
+
+optionalParameterTypes
+    :    optionalPositionalParameterTypes
+    |    namedParameterTypes
+    ;
+
+optionalPositionalParameterTypes
+    :    '[' normalParameterTypes ','? ']'
+    ;
+
+namedParameterTypes
+    :    LBRACE namedParameterType (',' namedParameterType)* ','? RBRACE
+    ;
+
+namedParameterType
+    :    metadata REQUIRED? typedIdentifier
+    ;
+
+typedIdentifier
+    :    type identifier
+    ;
+
+constructorDesignation
+    :    typeIdentifier
+    |    qualifiedName
+    |    typeName typeArguments ('.' identifierOrNew)?
+    ;
+
+symbolLiteral
+    :    '#' (operator | (identifier ('.' identifier)*) | VOID)
+    ;
+
+singleLineString
+    :    RAW_SINGLE_LINE_STRING
+    |    SINGLE_LINE_STRING_SQ_BEGIN_END
+    |    SINGLE_LINE_STRING_SQ_BEGIN_MID expression
+         (SINGLE_LINE_STRING_SQ_MID_MID expression)*
+         SINGLE_LINE_STRING_SQ_MID_END
+    |    SINGLE_LINE_STRING_DQ_BEGIN_END
+    |    SINGLE_LINE_STRING_DQ_BEGIN_MID expression
+         (SINGLE_LINE_STRING_DQ_MID_MID expression)*
+         SINGLE_LINE_STRING_DQ_MID_END
+    ;
+
+multiLineString
+    :    RAW_MULTI_LINE_STRING
+    |    MULTI_LINE_STRING_SQ_BEGIN_END
+    |    MULTI_LINE_STRING_SQ_BEGIN_MID expression
+         (MULTI_LINE_STRING_SQ_MID_MID expression)*
+         MULTI_LINE_STRING_SQ_MID_END
+    |    MULTI_LINE_STRING_DQ_BEGIN_END
+    |    MULTI_LINE_STRING_DQ_BEGIN_MID expression
+         (MULTI_LINE_STRING_DQ_MID_MID expression)*
+         MULTI_LINE_STRING_DQ_MID_END
+    ;
+
+reservedWord
+    :    ASSERT
+    |    BREAK
+    |    CASE
+    |    CATCH
+    |    CLASS
+    |    CONST
+    |    CONTINUE
+    |    DEFAULT
+    |    DO
+    |    ELSE
+    |    ENUM
+    |    EXTENDS
+    |    FALSE
+    |    FINAL
+    |    FINALLY
+    |    FOR
+    |    IF
+    |    IN
+    |    IS
+    |    NEW
+    |    NULL
+    |    RETHROW
+    |    RETURN
+    |    SUPER
+    |    SWITCH
+    |    THIS
+    |    THROW
+    |    TRUE
+    |    TRY
+    |    VAR
+    |    VOID
+    |    WHILE
+    |    WITH
+    ;
+
+builtInIdentifier
+    :    ABSTRACT
+    |    AS
+    |    AUGMENT
+    |    COVARIANT
+    |    DEFERRED
+    |    DYNAMIC
+    |    EXPORT
+    |    EXTENSION
+    |    EXTERNAL
+    |    FACTORY
+    |    FUNCTION
+    |    GET
+    |    IMPLEMENTS
+    |    IMPORT
+    |    INTERFACE
+    |    LATE
+    |    LIBRARY
+    |    OPERATOR
+    |    MIXIN
+    |    PART
+    |    REQUIRED
+    |    SET
+    |    STATIC
+    |    TYPEDEF
+    ;
+
+otherIdentifierNotType
+    :    ASYNC
+    |    BASE
+    |    HIDE
+    |    OF
+    |    ON
+    |    SEALED
+    |    SHOW
+    |    SYNC
+    |    WHEN
+    ;
+
+otherIdentifier
+    :    otherIdentifierNotType
+    |    TYPE
+    ;
+
+// ---------------------------------------- Lexer rules.
+
+fragment
+LETTER
+    :    'a' .. 'z'
+    |    'A' .. 'Z'
+    ;
+
+fragment
+DIGIT
+    :    '0' .. '9'
+    ;
+
+fragment
+EXPONENT
+    :    ('e' | 'E') ('+' | '-')? DIGITS
+    ;
+
+fragment
+DIGITS
+    :    DIGIT ('_'* DIGIT)*
+    ;
+
+fragment
+HEX_DIGIT
+    :    ('a' | 'b' | 'c' | 'd' | 'e' | 'f')
+    |    ('A' | 'B' | 'C' | 'D' | 'E' | 'F')
+    |    DIGIT
+    ;
+
+fragment
+HEX_DIGITS
+    :    HEX_DIGIT ('_'* HEX_DIGIT)*
+    ;
+
+// Reserved words (if updated, update `reservedWord` as well).
+
+ASSERT
+    :    'assert'
+    ;
+
+BREAK
+    :    'break'
+    ;
+
+CASE
+    :    'case'
+    ;
+
+CATCH
+    :    'catch'
+    ;
+
+CLASS
+    :    'class'
+    ;
+
+CONST
+    :    'const'
+    ;
+
+CONTINUE
+    :    'continue'
+    ;
+
+DEFAULT
+    :    'default'
+    ;
+
+DO
+    :    'do'
+    ;
+
+ELSE
+    :    'else'
+    ;
+
+ENUM
+    :    'enum'
+    ;
+
+EXTENDS
+    :    'extends'
+    ;
+
+FALSE
+    :    'false'
+    ;
+
+FINAL
+    :    'final'
+    ;
+
+FINALLY
+    :    'finally'
+    ;
+
+FOR
+    :    'for'
+    ;
+
+IF
+    :    'if'
+    ;
+
+IN
+    :    'in'
+    ;
+
+IS
+    :    'is'
+    ;
+
+NEW
+    :    'new'
+    ;
+
+NULL
+    :    'null'
+    ;
+
+RETHROW
+    :    'rethrow'
+    ;
+
+RETURN
+    :    'return'
+    ;
+
+SUPER
+    :    'super'
+    ;
+
+SWITCH
+    :    'switch'
+    ;
+
+THIS
+    :    'this'
+    ;
+
+THROW
+    :    'throw'
+    ;
+
+TRUE
+    :    'true'
+    ;
+
+TRY
+    :    'try'
+    ;
+
+VAR
+    :    'var'
+    ;
+
+VOID
+    :    'void'
+    ;
+
+WHILE
+    :    'while'
+    ;
+
+WITH
+    :    'with'
+    ;
+
+// Built-in identifiers (if updated, update `builtInIdentifier` as well).
+
+ABSTRACT
+    :    'abstract'
+    ;
+
+AS
+    :    'as'
+    ;
+
+AUGMENT
+    :    'augment'
+    ;
+
+COVARIANT
+    :    'covariant'
+    ;
+
+DEFERRED
+    :    'deferred'
+    ;
+
+DYNAMIC
+    :    'dynamic'
+    ;
+
+EXPORT
+    :    'export'
+    ;
+
+EXTENSION
+    :    'extension'
+    ;
+
+EXTERNAL
+    :    'external'
+    ;
+
+FACTORY
+    :    'factory'
+    ;
+
+FUNCTION
+    :    'Function'
+    ;
+
+GET
+    :    'get'
+    ;
+
+IMPLEMENTS
+    :    'implements'
+    ;
+
+IMPORT
+    :    'import'
+    ;
+
+INTERFACE
+    :    'interface'
+    ;
+
+LATE
+    :    'late'
+    ;
+
+LIBRARY
+    :    'library'
+    ;
+
+OPERATOR
+    :    'operator'
+    ;
+
+MIXIN
+    :    'mixin'
+    ;
+
+PART
+    :    'part'
+    ;
+
+REQUIRED
+    :    'required'
+    ;
+
+SET
+    :    'set'
+    ;
+
+STATIC
+    :    'static'
+    ;
+
+TYPEDEF
+    :    'typedef'
+    ;
+
+// "Contextual keywords".
+
+AWAIT
+    :    'await'
+    ;
+
+YIELD
+    :    'yield'
+    ;
+
+// Other words used in the grammar (if updated, update `otherIdentifier`, too).
+
+ASYNC
+    :    'async'
+    ;
+
+BASE
+    :    'base'
+    ;
+
+HIDE
+    :    'hide'
+    ;
+
+OF
+    :    'of'
+    ;
+
+ON
+    :    'on'
+    ;
+
+SEALED
+    :    'sealed'
+    ;
+
+SHOW
+    :    'show'
+    ;
+
+SYNC
+    :    'sync'
+    ;
+
+TYPE
+    :    'type'
+    ;
+
+WHEN
+    :    'when'
+    ;
+
+// Lexical tokens that are not words.
+
+NUMBER
+    :    DIGITS ('.' DIGITS)? EXPONENT?
+    |    '.' DIGITS EXPONENT?
+    ;
+
+HEX_NUMBER
+    :    '0x' HEX_DIGITS
+    |    '0X' HEX_DIGITS
+    ;
+
+RAW_SINGLE_LINE_STRING
+    :    'r' '\'' (~('\'' | '\r' | '\n'))* '\''
+    |    'r' '"' (~('"' | '\r' | '\n'))* '"'
+    ;
+
+RAW_MULTI_LINE_STRING
+    :    'r' '"""' (.)*? '"""'
+    |    'r' '\'\'\'' (.)*? '\'\'\''
+    ;
+
+fragment
+SIMPLE_STRING_INTERPOLATION
+    :    '$' IDENTIFIER_NO_DOLLAR
+    ;
+
+fragment
+ESCAPE_SEQUENCE
+    :    '\\n'
+    |    '\\r'
+    |    '\\b'
+    |    '\\t'
+    |    '\\v'
+    |    '\\x' HEX_DIGIT HEX_DIGIT
+    |    '\\u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
+    |    '\\u{' HEX_DIGIT_SEQUENCE '}'
+    ;
+
+fragment
+HEX_DIGIT_SEQUENCE
+    :    HEX_DIGIT HEX_DIGIT? HEX_DIGIT?
+         HEX_DIGIT? HEX_DIGIT? HEX_DIGIT?
+    ;
+
+fragment
+STRING_CONTENT_COMMON
+    :    ~('\\' | '\'' | '"' | '$' | '\r' | '\n')
+    |    ESCAPE_SEQUENCE
+    |    '\\' ~('n' | 'r' | 'b' | 't' | 'v' | 'x' | 'u' | '\r' | '\n')
+    |    SIMPLE_STRING_INTERPOLATION
+    ;
+
+fragment
+STRING_CONTENT_SQ
+    :    STRING_CONTENT_COMMON
+    |    '"'
+    ;
+
+SINGLE_LINE_STRING_SQ_BEGIN_END
+    :    '\'' STRING_CONTENT_SQ* '\''
+    ;
+
+SINGLE_LINE_STRING_SQ_BEGIN_MID
+    :    '\'' STRING_CONTENT_SQ* '${' { enterBraceSingleQuote(); }
+    ;
+
+SINGLE_LINE_STRING_SQ_MID_MID
+    :    { currentBraceLevel(BRACE_SINGLE) }?
+         { exitBrace(); } '}' STRING_CONTENT_SQ* '${'
+         { enterBraceSingleQuote(); }
+    ;
+
+SINGLE_LINE_STRING_SQ_MID_END
+    :    { currentBraceLevel(BRACE_SINGLE) }?
+         { exitBrace(); } '}' STRING_CONTENT_SQ* '\''
+    ;
+
+fragment
+STRING_CONTENT_DQ
+    :    STRING_CONTENT_COMMON
+    |    '\''
+    ;
+
+SINGLE_LINE_STRING_DQ_BEGIN_END
+    :    '"' STRING_CONTENT_DQ* '"'
+    ;
+
+SINGLE_LINE_STRING_DQ_BEGIN_MID
+    :    '"' STRING_CONTENT_DQ* '${' { enterBraceDoubleQuote(); }
+    ;
+
+SINGLE_LINE_STRING_DQ_MID_MID
+    :    { currentBraceLevel(BRACE_DOUBLE) }?
+         { exitBrace(); } '}' STRING_CONTENT_DQ* '${'
+         { enterBraceDoubleQuote(); }
+    ;
+
+SINGLE_LINE_STRING_DQ_MID_END
+    :    { currentBraceLevel(BRACE_DOUBLE) }?
+         { exitBrace(); } '}' STRING_CONTENT_DQ* '"'
+    ;
+
+fragment
+QUOTES_SQ
+    :
+    |    '\''
+    |    '\'\''
+    ;
+
+// Read string contents, which may be almost anything, but stop when seeing
+// '\'\'\'' and when seeing '${'. We do this by allowing all other
+// possibilities including escapes, simple interpolation, and fewer than
+// three '\''.
+fragment
+STRING_CONTENT_TSQ
+    :    QUOTES_SQ
+         (STRING_CONTENT_COMMON | '"' | '\r' | '\n' | '\\\r' | '\\\n')
+    ;
+
+MULTI_LINE_STRING_SQ_BEGIN_END
+    :    '\'\'\'' STRING_CONTENT_TSQ* '\'\'\''
+    ;
+
+MULTI_LINE_STRING_SQ_BEGIN_MID
+    :    '\'\'\'' STRING_CONTENT_TSQ* QUOTES_SQ '${'
+         { enterBraceThreeSingleQuotes(); }
+    ;
+
+MULTI_LINE_STRING_SQ_MID_MID
+    :    { currentBraceLevel(BRACE_THREE_SINGLE) }?
+         { exitBrace(); } '}' STRING_CONTENT_TSQ* QUOTES_SQ '${'
+         { enterBraceThreeSingleQuotes(); }
+    ;
+
+MULTI_LINE_STRING_SQ_MID_END
+    :    { currentBraceLevel(BRACE_THREE_SINGLE) }?
+         { exitBrace(); } '}' STRING_CONTENT_TSQ* '\'\'\''
+    ;
+
+fragment
+QUOTES_DQ
+    :
+    |    '"'
+    |    '""'
+    ;
+
+// Read string contents, which may be almost anything, but stop when seeing
+// '"""' and when seeing '${'. We do this by allowing all other possibilities
+// including escapes, simple interpolation, and fewer-than-three '"'.
+fragment
+STRING_CONTENT_TDQ
+    :    QUOTES_DQ
+         (STRING_CONTENT_COMMON | '\'' | '\r' | '\n' | '\\\r' | '\\\n')
+    ;
+
+MULTI_LINE_STRING_DQ_BEGIN_END
+    :    '"""' STRING_CONTENT_TDQ* '"""'
+    ;
+
+MULTI_LINE_STRING_DQ_BEGIN_MID
+    :    '"""' STRING_CONTENT_TDQ* QUOTES_DQ '${'
+         { enterBraceThreeDoubleQuotes(); }
+    ;
+
+MULTI_LINE_STRING_DQ_MID_MID
+    :    { currentBraceLevel(BRACE_THREE_DOUBLE) }?
+         { exitBrace(); } '}' STRING_CONTENT_TDQ* QUOTES_DQ '${'
+         { enterBraceThreeDoubleQuotes(); }
+    ;
+
+MULTI_LINE_STRING_DQ_MID_END
+    :    { currentBraceLevel(BRACE_THREE_DOUBLE) }?
+         { exitBrace(); } '}' STRING_CONTENT_TDQ* '"""'
+    ;
+
+LBRACE
+    :    '{' { enterBrace(); }
+    ;
+
+RBRACE
+    :    { currentBraceLevel(BRACE_NORMAL) }? { exitBrace(); } '}'
+    ;
+
+fragment
+IDENTIFIER_START_NO_DOLLAR
+    :    LETTER
+    |    '_'
+    ;
+
+fragment
+IDENTIFIER_PART_NO_DOLLAR
+    :    IDENTIFIER_START_NO_DOLLAR
+    |    DIGIT
+    ;
+
+fragment
+IDENTIFIER_NO_DOLLAR
+    :    IDENTIFIER_START_NO_DOLLAR IDENTIFIER_PART_NO_DOLLAR*
+    ;
+
+fragment
+IDENTIFIER_START
+    :    IDENTIFIER_START_NO_DOLLAR
+    |    '$'
+    ;
+
+fragment
+IDENTIFIER_PART
+    :    IDENTIFIER_START
+    |    DIGIT
+    ;
+
+SCRIPT_TAG
+    :    '#!' (~('\r' | '\n'))* NEWLINE
+    ;
+
+IDENTIFIER
+    :    IDENTIFIER_START IDENTIFIER_PART*
+    ;
+
+SINGLE_LINE_COMMENT
+    :    '//' (~('\r' | '\n'))* NEWLINE?
+         { skip(); }
+    ;
+
+MULTI_LINE_COMMENT
+    :    '/*' (MULTI_LINE_COMMENT | .)*? '*/'
+         { skip(); }
+    ;
+
+fragment
+NEWLINE
+    :    ('\r' | '\n' | '\r\n')
+    ;
+
+FEFF
+    :    '\uFEFF'
+    ;
+
+WS
+    :    (' ' | '\t' | '\r' | '\n')+
+         { skip(); }
+    ;
diff --git a/pkg/front_end/tool/fuzz/analyzer_helper.dart b/pkg/front_end/tool/fuzz/analyzer_helper.dart
new file mode 100644
index 0000000..bdd4419
--- /dev/null
+++ b/pkg/front_end/tool/fuzz/analyzer_helper.dart
@@ -0,0 +1,489 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async";
+import "dart:convert";
+import "dart:io";
+import "dart:typed_data";
+
+Future<(Object, StackTrace)?> compileWithAnalyzer(AnalyzerHelper analyzerHelper,
+    String program, Uri fileUri, int version) async {
+  try {
+    ZoneSpecification specification =
+        new ZoneSpecification(print: (_1, _2, _3, String line) {
+      // Swallow!
+    });
+    await runZoned(() async {
+      Stopwatch stopwatch = new Stopwatch()..start();
+      await analyzerHelper.changeFileContent(fileUri, program, version);
+      print("Analyzer compile took ${stopwatch.elapsedMilliseconds} ms.");
+    }, zoneSpecification: specification);
+    return null;
+  } catch (e, st) {
+    print("Analyzer crashed on input.");
+    return (e, st);
+  }
+}
+
+Future<void> main() async {
+  AnalyzerHelper analyzerHelper = new AnalyzerHelper();
+  Directory root = Directory.systemTemp.createTempSync("fuzzer");
+  File f = new File.fromUri(root.uri.resolve("testfile.dart"));
+  f.writeAsStringSync("");
+  int version = 1;
+
+  await analyzerHelper.setup(root.uri);
+
+  await analyzerHelper.changeFileContent(
+      f.uri, "void main() { foo; }", version++);
+
+  await analyzerHelper.changeFileContent(f.uri, "void main() { }", version++);
+
+  await analyzerHelper.changeFileContent(
+      f.uri, "void main() { foo; } get foo => 42; ", version++);
+
+  analyzerHelper.shutdown();
+  root.deleteSync(recursive: true);
+}
+
+class AnalyzerHelper {
+  static const bool debug = false;
+  static const int _printSizeCap = 300;
+  late final Process _p;
+  late final Timer _periodicTimer;
+  bool _processShutDown = false;
+  bool _processExited = false;
+
+  Future<void> setup(Uri rootUri) async {
+    _checkCorrectDart();
+    Uri sdkUri = Uri.base
+        .resolveUri(Uri.file(Platform.resolvedExecutable))
+        .resolve("..");
+    if (!Directory.fromUri(rootUri).existsSync()) {
+      throw "Directory $rootUri doesn't exist. "
+          "Specify existing directory with --root=";
+    }
+
+    if (!Directory.fromUri(sdkUri).existsSync()) {
+      throw "Directory $sdkUri doesn't exist. "
+          "Specify existing directory with --sdk=";
+    }
+
+    if (!debug) {
+      _p = await Process.start(Platform.resolvedExecutable, [
+        "language-server",
+        "--lsp",
+      ]);
+    } else {
+      _p = await Process.start(Platform.resolvedExecutable, [
+        "--enable-vm-service",
+        "--profiler",
+        "pkg/analysis_server/bin/server.dart",
+        "--lsp",
+        "--port=9101"
+      ]);
+    }
+    // ignore: unawaited_futures
+    _p.exitCode.then((_) {
+      _processExited = true;
+      if (!_processShutDown) {
+        shutdown();
+        // Make sure we don't wait forever.
+        _setAnalyzing(false);
+      }
+    });
+    _p.stdout.listen(_listenToStdout);
+    _periodicTimer =
+        Timer.periodic(const Duration(seconds: 1), _checkLongRunningRequests);
+    await _initialize(sdkUri, rootUri, []);
+  }
+
+  void shutdown() {
+    _processShutDown = true;
+    _periodicTimer.cancel();
+    _p.kill();
+  }
+
+  Future<void> _initialize(
+      Uri sdkUri, Uri rootUri, List<Uri> additionalWorkspaceUris) async {
+    OutstandingRequest? request = await _send(
+        Messages.initMessage(pid, rootUri, additionalWorkspaceUris));
+    await request?.completer.future;
+    _resetAnalyzingBool();
+    await _send(Messages.initNotification);
+    await _send(Messages.initMore(sdkUri));
+    await _waitForAnalysisToComplete();
+  }
+
+  Set<Uri> _openFiles = {};
+
+  Future<void> changeFileContent(
+      Uri file, String newContent, int fileVersion) async {
+    _resetAnalyzingBool();
+    if (_openFiles.add(file)) {
+      await _send(Messages.openFile(file, fileVersion, newContent));
+    } else {
+      await _send(Messages.changeFileContent(file, newContent, fileVersion));
+    }
+
+    await _waitForAnalysisToComplete();
+  }
+
+  void _checkCorrectDart() {
+    Uri exe = Uri.base.resolveUri(Uri.file(Platform.resolvedExecutable));
+    Uri librariesDart =
+        exe.resolve("../lib/_internal/sdk_library_metadata/lib/libraries.dart");
+    if (!File.fromUri(librariesDart).existsSync()) {
+      throw "Execute with a dart that has "
+          "'../lib/_internal/sdk_library_metadata/lib/libraries.dart' "
+          "available (e.g. out/ReleaseX64/dart-sdk/bin/dart)";
+    }
+  }
+
+  void _checkLongRunningRequests(timer) {
+    bool reportedSomething = false;
+    for (MapEntry<int, OutstandingRequest> waitingFor
+        in _outstandingRequestsWithId.entries) {
+      if (waitingFor.value.stopwatch.elapsed > const Duration(seconds: 1)) {
+        if (!reportedSomething) {
+          print("----");
+          reportedSomething = true;
+        }
+        print("==> Has been waiting for ${waitingFor.key} for "
+            "${waitingFor.value.stopwatch.elapsed}");
+      }
+    }
+    if (reportedSomething) {
+      print("----");
+    }
+  }
+
+  int? _headerContentLength;
+
+  bool? _currentlyAnalyzing;
+  Completer<void> _analyzingCompleter = Completer();
+
+  final _buffer = <int>[];
+
+  /// There's something weird about getting (several) id 3's that wasn't
+  /// requested...
+  int _largestIdSeen = 3;
+
+  RegExp _newLineRegExp = RegExp("\r?\n");
+
+  Map<int, OutstandingRequest> _outstandingRequestsWithId = {};
+
+  bool _printedVmServiceStuff = false;
+
+  int _verbosity = 0;
+
+  void _setAnalyzing(bool b) {
+    _analyzingCompleter.complete(b);
+    _currentlyAnalyzing = b;
+    _analyzingCompleter = Completer<bool>();
+  }
+
+  void _resetAnalyzingBool() {
+    _currentlyAnalyzing = null;
+  }
+
+  Future<void> _waitForAnalysisToComplete() async {
+    // Wait until it's done analyzing.
+    if (_currentlyAnalyzing == null) {
+      await _analyzingCompleter.future;
+    }
+    Stopwatch stopwatch = Stopwatch()..start();
+    while (_currentlyAnalyzing == true) {
+      await _analyzingCompleter.future;
+    }
+    print("isAnalyzing is now done after ${stopwatch.elapsed}");
+    if (_processExited) {
+      // TODO(jensj): We should extract the correct stacktrace somehow.
+      throw "Process exited.";
+    }
+  }
+
+  void _listenToStdout(List<int> event) {
+    // General idea taken from
+    // pkg/analysis_server/lib/src/lsp/lsp_packet_transformer.dart
+    for (int element in event) {
+      _buffer.add(element);
+      if (_verbosity > 3 &&
+          _buffer.length >= 1000 &&
+          _buffer.length % 1000 == 0) {
+        print("DEBUG MESSAGE: Stdout buffer with length "
+            "${_buffer.length} so far: "
+            "${utf8.decode(_buffer)}");
+      }
+      if (_headerContentLength == null && _endsWithCrLfCrLf()) {
+        String headerRaw = utf8.decode(_buffer);
+        _buffer.clear();
+        // Use a regex that makes the "\r" optional to handle "The Dart VM
+        // service is listening on [..." message - at least on linux - being \n
+        // terminated which would otherwise mean that we'd be stuck because no
+        // message would start with "Content-Length:".
+        List<String> headers = headerRaw.split(_newLineRegExp);
+        for (String header in headers) {
+          if (!_printedVmServiceStuff &&
+              header.startsWith("The Dart VM service")) {
+            print("\n\n$header\n\n");
+            _printedVmServiceStuff = true;
+          }
+          if (header.startsWith("Content-Length:")) {
+            String contentLength =
+                header.substring("Content-Length:".length).trim();
+            _headerContentLength = int.parse(contentLength);
+            break;
+          }
+        }
+      } else if (_headerContentLength != null &&
+          _buffer.length == _headerContentLength!) {
+        String messageString = utf8.decode(_buffer);
+        _buffer.clear();
+        _headerContentLength = null;
+        Map<String, dynamic> message =
+            json.decode(messageString) as Map<String, dynamic>;
+
+        // {"jsonrpc":"2.0","method":"$/analyzerStatus","params":{"isAnalyzing":false}}
+        dynamic method = message["method"];
+        if (method == r"$/analyzerStatus") {
+          dynamic params = message["params"];
+          if (params is Map) {
+            dynamic isAnalyzing = params["isAnalyzing"];
+            if (isAnalyzing is bool) {
+              _setAnalyzing(isAnalyzing);
+              if (_verbosity > 0) {
+                print("Got analyzerStatus isAnalyzing = $isAnalyzing");
+              }
+            }
+          }
+        }
+        dynamic possibleId = message["id"];
+        if (possibleId is int) {
+          if (possibleId > _largestIdSeen) {
+            _largestIdSeen = possibleId;
+          }
+
+          if (_verbosity > 0) {
+            if (messageString.length > _printSizeCap) {
+              print("Got message "
+                  "${messageString.substring(0, _printSizeCap)}...");
+            } else {
+              print("Got message $messageString");
+            }
+          }
+
+          OutstandingRequest? outstandingRequest =
+              _outstandingRequestsWithId.remove(possibleId);
+          if (outstandingRequest != null) {
+            outstandingRequest.stopwatch.stop();
+            outstandingRequest.completer.complete(message);
+            if (_verbosity > 2) {
+              print(" => Got response for $possibleId in "
+                  "${outstandingRequest.stopwatch.elapsed}");
+            }
+          }
+        } else if (_verbosity > 1) {
+          if (messageString.length > _printSizeCap) {
+            print("Got message "
+                "${messageString.substring(0, _printSizeCap)}...");
+          } else {
+            print("Got message $messageString");
+          }
+        }
+      }
+    }
+  }
+
+  Future<OutstandingRequest?> _send(Map<String, dynamic> json) async {
+    if (_processExited) throw "Process is gone.";
+    // Mostly copied from
+    // pkg/analysis_server/lib/src/lsp/channel/lsp_byte_stream_channel.dart
+    String jsonEncodedBody = jsonEncode(json);
+    Uint8List utf8EncodedBody = utf8.encode(jsonEncodedBody);
+    String header = "Content-Length: ${utf8EncodedBody.length}\r\n"
+        "Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n";
+    Uint8List asciiEncodedHeader = ascii.encode(header);
+
+    OutstandingRequest? result;
+
+    dynamic possibleId = json["id"];
+    if (possibleId is int) {
+      if (possibleId > _largestIdSeen) {
+        _largestIdSeen = possibleId;
+      }
+      result = OutstandingRequest();
+      _outstandingRequestsWithId[possibleId] = result;
+      if (_verbosity > 2) {
+        print("Sending message with id $possibleId");
+      }
+    }
+
+    // Header is always ascii, body is always utf8!
+    _p.stdin.add(asciiEncodedHeader);
+    _p.stdin.add(utf8EncodedBody);
+    await _p.stdin.flush();
+    if (_verbosity > 2) {
+      print("\n\nMessage sent...\n\n");
+      print("jsonEncodedBody: $jsonEncodedBody");
+    }
+    return result;
+  }
+
+  /// Copied from pkg/analysis_server/lib/src/lsp/lsp_packet_transformer.dart.
+  bool _endsWithCrLfCrLf() {
+    int l = _buffer.length;
+    return l > 4 &&
+        _buffer[l - 1] == 10 &&
+        _buffer[l - 2] == 13 &&
+        _buffer[l - 3] == 10 &&
+        _buffer[l - 4] == 13;
+  }
+}
+
+class Location {
+  final Uri uri;
+  final int line;
+  final int column;
+
+  Location(this.uri, this.line, this.column);
+
+  @override
+  String toString() => "Location[$uri:$line:$column]";
+}
+
+// Messages taken from what VSCode sent.
+class Messages {
+  static Map<String, dynamic> initNotification = {
+    "jsonrpc": "2.0",
+    "method": "initialized",
+    "params": {}
+  };
+
+  static Map<String, dynamic> gotoDef(int id, Location location) {
+    return {
+      "jsonrpc": "2.0",
+      "id": id,
+      "method": "textDocument/definition",
+      "params": {
+        "textDocument": {"uri": "${location.uri}"},
+        "position": {"line": location.line, "character": location.column}
+      }
+    };
+  }
+
+  static Map<String, dynamic> implementation(int id, Location location) {
+    return {
+      "jsonrpc": "2.0",
+      "id": id,
+      "method": "textDocument/implementation",
+      "params": {
+        "textDocument": {"uri": "${location.uri}"},
+        "position": {"line": location.line, "character": location.column}
+      }
+    };
+  }
+
+  static Map<String, dynamic> initMessage(
+      int processId, Uri rootUri, List<Uri> additionalWorkspaceUris) {
+    String rootPath = rootUri.toFilePath();
+    String name = rootUri.pathSegments.last;
+    if (name.isEmpty) {
+      name = rootUri.pathSegments[rootUri.pathSegments.length - 2];
+    }
+    return {
+      "id": 0,
+      "jsonrpc": "2.0",
+      "method": "initialize",
+      "params": {
+        "processId": processId,
+        "clientInfo": {"name": "lspTestScript", "version": "0.0.1"},
+        "locale": "en",
+        "rootPath": rootPath,
+        "rootUri": "$rootUri",
+        "capabilities": {},
+        "initializationOptions": {},
+        "workspaceFolders": [
+          {"uri": "$rootUri", "name": name},
+          ...additionalWorkspaceUris.map((uri) {
+            String name = uri.pathSegments.last;
+            if (name.isEmpty) {
+              name = uri.pathSegments[uri.pathSegments.length - 2];
+            }
+            return {
+              "uri": "$uri",
+              "name": name,
+            };
+          })
+        ]
+      }
+    };
+  }
+
+  static Map<String, dynamic> initMore(Uri sdkUri) {
+    String sdkPath = sdkUri.toFilePath();
+    return {
+      // "id": 1,
+      "jsonrpc": "2.0",
+      "result": [
+        {
+          "useLsp": true,
+          "sdkPath": sdkPath,
+          "allowAnalytics": false,
+        }
+      ]
+    };
+  }
+
+  static Map<String, dynamic> references(int id, Location location) {
+    return {
+      "jsonrpc": "2.0",
+      "id": id,
+      "method": "textDocument/references",
+      "params": {
+        "textDocument": {"uri": "${location.uri}"},
+        "position": {"line": location.line, "character": location.column},
+        "context": {"includeDeclaration": true}
+      }
+    };
+  }
+
+  static Map<String, dynamic> openFile(
+      Uri file, int fileVersion, String content) {
+    return {
+      "jsonrpc": "2.0",
+      "method": "textDocument/didOpen",
+      "params": {
+        "textDocument": {
+          "uri": "$file",
+          "languageId": "dart",
+          "version": fileVersion,
+          "text": content
+        },
+      }
+    };
+  }
+
+  static Map<String, dynamic> changeFileContent(
+      Uri file, String newContent, int fileVersion) {
+    return {
+      "jsonrpc": "2.0",
+      "method": "textDocument/didChange",
+      "params": {
+        "textDocument": {"uri": "$file", "version": fileVersion},
+        "contentChanges": [
+          {"text": newContent}
+        ]
+      }
+    };
+  }
+}
+
+class OutstandingRequest {
+  final Stopwatch stopwatch = Stopwatch();
+  final Completer<Map<String, dynamic>> completer = Completer();
+  OutstandingRequest() {
+    stopwatch.start();
+  }
+}
diff --git a/pkg/front_end/tool/fuzz/compile_helper.dart b/pkg/front_end/tool/fuzz/compile_helper.dart
new file mode 100644
index 0000000..efec043
--- /dev/null
+++ b/pkg/front_end/tool/fuzz/compile_helper.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async' show ZoneSpecification, runZoned;
+
+import 'dart:io' show File;
+
+import 'package:front_end/src/api_prototype/compiler_options.dart'
+    show CompilerOptions;
+
+import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
+    show DiagnosticMessage;
+import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart';
+
+import "package:front_end/src/api_prototype/memory_file_system.dart"
+    show MemoryFileSystem;
+
+import 'package:front_end/src/compute_platform_binaries_location.dart'
+    show computePlatformBinariesLocation;
+
+import 'package:kernel/kernel.dart' show Component;
+
+import "../../test/incremental_utils.dart" as util;
+
+import "../../test/incremental_suite.dart" as incrementalTest;
+
+import '../../test/utils/io_utils.dart' show computeRepoDirUri;
+
+final Uri repoDir = computeRepoDirUri();
+
+class Helper {
+  MemoryFileSystem? fs;
+  CompilerOptions? options;
+  incrementalTest.TestIncrementalCompiler? compiler;
+  late final Uri base;
+  late final Uri sdkSummary;
+  late final List<int> sdkSummaryData;
+
+  Future<void> setup() async {
+    final Uri sdkRoot = computePlatformBinariesLocation(forceBuildDir: true);
+    base = Uri.parse("org-dartlang-test:///");
+    sdkSummary = base.resolve("vm_platform_strong.dill");
+    Uri platformUri = sdkRoot.resolve("vm_platform_strong.dill");
+    sdkSummaryData = await new File.fromUri(platformUri).readAsBytes();
+  }
+
+  Future<(Object, StackTrace)?> compile(String program) async {
+    if (fs == null) {
+      fs = new MemoryFileSystem(base);
+      fs!.entityForUri(sdkSummary).writeAsBytesSync(sdkSummaryData);
+    }
+
+    Uri uri = base.resolve("main.dart");
+    fs!.entityForUri(uri).writeAsStringSync(program);
+    Map<String, int> diagnostics = {};
+    options ??= incrementalTest.getOptions();
+    options!.fileSystem = fs!;
+    options!.sdkRoot = null;
+    options!.sdkSummary = sdkSummary;
+    options!.omitPlatform = true;
+    options!.onDiagnostic = (DiagnosticMessage message) {
+      diagnostics[message.severity.toString()] =
+          (diagnostics[message.severity.toString()] ?? 0) + 1;
+    };
+
+    compiler ??= new incrementalTest.TestIncrementalCompiler(options!, uri);
+    compiler!.invalidate(uri);
+
+    try {
+      ZoneSpecification specification =
+          new ZoneSpecification(print: (_1, _2, _3, String line) {
+        // Swallow!
+      });
+      await runZoned(() async {
+        Stopwatch stopwatch = new Stopwatch()..start();
+        IncrementalCompilerResult result = await compiler!
+            .computeDelta(entryPoints: [uri], fullComponent: true);
+        Component component = result.component;
+
+        util.throwOnEmptyMixinBodies(component);
+        await util.throwOnInsufficientUriToSource(component);
+        print("Compile took ${stopwatch.elapsedMilliseconds} ms. "
+            "Got $diagnostics");
+      }, zoneSpecification: specification);
+      return null;
+    } catch (e, st) {
+      print("Crashed on input.");
+      options = null;
+      compiler = null;
+      return (e, st);
+    }
+  }
+}
diff --git a/pkg/front_end/tool/fuzz/fuzz.dart b/pkg/front_end/tool/fuzz/fuzz.dart
new file mode 100644
index 0000000..01a0c6f
--- /dev/null
+++ b/pkg/front_end/tool/fuzz/fuzz.dart
@@ -0,0 +1,142 @@
+// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io' show Directory, File, sleep, stdin;
+
+import '../../test/utils/io_utils.dart' show computeRepoDirUri;
+
+import 'analyzer_helper.dart';
+import 'compile_helper.dart';
+import "gReader.dart" as gReader;
+import 'stacktrace_utils.dart';
+
+final Uri repoDir = computeRepoDirUri();
+int _worldNum = 0;
+int _cfeCrashes = 0;
+int _analyzerCrashes = 0;
+Stopwatch _stopwatch = new Stopwatch();
+Stopwatch _createStopwatch = new Stopwatch();
+Stopwatch _cfeStopwatch = new Stopwatch();
+Stopwatch _analyzerStopwatch = new Stopwatch();
+
+Future<void> main() async {
+  _setupStdin();
+  try {
+    _stopwatch.start();
+    Helper helper = new Helper();
+    await helper.setup();
+    AnalyzerHelper analyzerHelper = new AnalyzerHelper();
+    Directory root = Directory.systemTemp.createTempSync("fuzzer");
+    File analyzerFileHelper =
+        new File.fromUri(root.uri.resolve("testfile.dart"));
+    analyzerFileHelper.writeAsStringSync("");
+    await analyzerHelper.setup(root.uri);
+    gReader.initialize(repoDir.resolve("pkg/front_end/tool/fuzz/Dart.g"));
+    while (!_quit) {
+      _worldNum++;
+      if (_worldNum > 100000) break;
+      print("----------------");
+      print("World #$_worldNum");
+      print("----------------");
+
+      _createStopwatch.start();
+      String data = gReader.createRandomProgram(includeWhy: false);
+      _createStopwatch.stop();
+
+      _cfeStopwatch.start();
+      (Object, StackTrace)? result = await helper.compile(data);
+      _cfeStopwatch.stop();
+      if (result != null) {
+        _cfeCrashes++;
+        var (Object e, StackTrace st) = result;
+        String category = categorize(st);
+        Directory d =
+            new Directory.fromUri(repoDir.resolve("fuzzDumps/$category/"));
+        d.createSync(recursive: true);
+        File f = new File.fromUri(
+            repoDir.resolve("fuzzDumps/$category/$_worldNum.input"));
+        f.writeAsStringSync(data);
+        print("Crashed on input. Input dumped into $f");
+      }
+
+      _analyzerStopwatch.start();
+      (Object, StackTrace)? resultAnalyzer = await compileWithAnalyzer(
+          analyzerHelper, data, analyzerFileHelper.uri, _worldNum);
+      _analyzerStopwatch.stop();
+      if (resultAnalyzer != null) {
+        _analyzerCrashes++;
+        var (Object e, StackTrace st) = resultAnalyzer;
+        String category = categorize(st);
+        Directory d =
+            new Directory.fromUri(repoDir.resolve("fuzzDumps/$category/"));
+        d.createSync(recursive: true);
+        File f = new File.fromUri(
+            repoDir.resolve("fuzzDumps/$category/$_worldNum.analyzerinput"));
+        f.writeAsStringSync(data);
+        print("Analyzer crashed on input. Input dumped into $f");
+
+        analyzerHelper = new AnalyzerHelper();
+        await analyzerHelper.setup(root.uri);
+      }
+    }
+    print("Done.");
+    analyzerHelper.shutdown();
+    printInfo(false);
+  } finally {
+    await _resetStdin();
+  }
+}
+
+void printInfo(bool addSleep) {
+  int countWorlds = _worldNum - 1;
+  print("Processed $countWorlds random programs in ${_stopwatch.elapsed}.");
+  if (countWorlds > 0) {
+    print("CFE crashes: $_cfeCrashes "
+        "(${((_cfeCrashes * 100) / countWorlds).toStringAsFixed(2)}%)");
+    print("Analyzer crashes: $_analyzerCrashes "
+        "(${((_analyzerCrashes * 100) / countWorlds).toStringAsFixed(2)}%)");
+  }
+  print("Spend ${_createStopwatch.elapsed} creating random programs.");
+  print("Spend ${_cfeStopwatch.elapsed} compiling with the CFE.");
+  print("Spend ${_analyzerStopwatch.elapsed} compiling with the Analyzer.");
+  if (addSleep) sleep(const Duration(seconds: 3));
+}
+
+bool? _oldEchoMode;
+bool? _oldLineMode;
+StreamSubscription<List<int>>? _stdinSubscription;
+bool _quit = false;
+
+Future<void> _resetStdin() async {
+  try {
+    stdin.echoMode = _oldEchoMode!;
+  } catch (e) {}
+  try {
+    stdin.lineMode = _oldLineMode!;
+  } catch (e) {}
+  await _stdinSubscription!.cancel();
+}
+
+void _setupStdin() {
+  try {
+    _oldEchoMode = stdin.echoMode;
+    _oldLineMode = stdin.lineMode;
+    stdin.echoMode = false;
+    stdin.lineMode = false;
+  } catch (e) {
+    print("Trying to setup 'stdin' failed. Continuing anyway, "
+        "but 'q' and 'i' might not work.");
+  }
+  _stdinSubscription = stdin.listen((List<int> event) {
+    if (event.length == 1 && event.single == "q".codeUnits.single) {
+      print("\n\nGot told to quit!\n\n");
+      _quit = true;
+    } else if (event.length == 1 && event.single == "i".codeUnits.single) {
+      printInfo(true);
+    } else {
+      print("\n\nGot stdin input: $event\n\n");
+    }
+  });
+}
diff --git a/pkg/front_end/tool/fuzz/gReader.dart b/pkg/front_end/tool/fuzz/gReader.dart
new file mode 100644
index 0000000..308be95
--- /dev/null
+++ b/pkg/front_end/tool/fuzz/gReader.dart
@@ -0,0 +1,1140 @@
+// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:convert' show utf8;
+import 'dart:io' show File, Platform, stderr;
+import 'dart:math' show Random;
+
+import 'package:_fe_analyzer_shared/src/scanner/characters.dart';
+
+List<Node> stack = [];
+Map<String, Node> data = {};
+
+void main() {
+  initialize(Platform.script.resolve("Dart.g"));
+  print(createRandomProgram(includeWhy: true));
+}
+
+void initialize(Uri dartG) {
+  parseDartG(dartG);
+  postParse();
+}
+
+String createRandomProgram({required bool includeWhy}) {
+  ProgramCreator programCreator = new ProgramCreator(createWhy: includeWhy);
+  programCreator.path.add("startSymbol");
+  data["startSymbol"]!.accept(programCreator);
+  if (includeWhy) {
+    return "${programCreator.sb.toString()}"
+        "\n\n\n\n"
+        "${programCreator.sb.toStringWhy()}";
+  } else {
+    return programCreator.sb.toString();
+  }
+}
+
+void postParse() {
+  EntryChecker entryChecker = new EntryChecker();
+  for (MapEntry<String, Node> entry in data.entries) {
+    entryChecker.parent = entry.key;
+    entry.value.accept(entryChecker);
+  }
+  DepthMarker depthMarker = new DepthMarker();
+  for (int i = 0; i < 2; i++) {
+    for (MapEntry<String, Node> entry in data.entries) {
+      entry.value.accept(depthMarker);
+    }
+    depthMarker.recurse = true;
+  }
+  SanityChecker sanityChecker = new SanityChecker();
+  for (MapEntry<String, Node> entry in data.entries) {
+    entry.value.accept(sanityChecker);
+  }
+}
+
+void parseDartG(Uri dartG) {
+  File f = new File.fromUri(dartG);
+  List<int> input = f.readAsBytesSync();
+  List<Token> tokens = tokenize(input);
+
+  Token? currentToken = tokens.first;
+  while (currentToken != null) {
+    currentToken = parseDef(currentToken);
+  }
+  // Attempt to have less exotic identifiers and types etc.
+  data["identifier"] = new Literal("foo");
+  data["typeIdentifier"] = new Literal("foo");
+  data["typeNotVoid"] = new Literal("foo");
+  data["singleLineString"] = new Literal("'ssls'"); // small single line string
+  data["argument"] = new Literal("42");
+}
+
+class StringBufferWrapper {
+  final bool createWhy;
+  final StringBuffer sb = new StringBuffer();
+  final StringBuffer sbWhy = new StringBuffer();
+  int get length => sb.length;
+  final ProgramCreator parent;
+  int currentNewlines = 1;
+  String prev = "";
+
+  StringBufferWrapper(this.createWhy, this.parent);
+
+  void addNewline() {
+    if (parent.singleLineStringDepth > 0) return;
+    if (parent.numericLiteralDepth > 0) return;
+    if (currentNewlines >= 2) return;
+    sb.writeln();
+    if (createWhy) {
+      sbWhy.writeln();
+    }
+    currentNewlines++;
+  }
+
+  void write(String s, String why) {
+    if (currentNewlines > 0 || parent.numericLiteralDepth > 0) {
+      // No space
+    } else if (prev == "" || prev == "@" || prev == "." || prev == "(") {
+      // No space.
+    } else if (s == "." || s == "," || s == "(" || s == ")" || s == ";") {
+      // No space.
+    } else {
+      sb.write(" ");
+    }
+    currentNewlines = 0;
+    prev = s;
+    sb.write(s);
+    if (createWhy) {
+      sbWhy.writeln("$s: ${parent.path.join("/")}");
+    }
+  }
+
+  void writeCharCode(int charCode, String why) {
+    write(new String.fromCharCode(charCode), why);
+  }
+
+  @override
+  String toString() => sb.toString();
+  String toStringWhy() => sbWhy.toString();
+}
+
+class WrappedRandom {
+  final Random random = new Random.secure();
+
+  int nextInt(int max) {
+    int result = random.nextInt(max);
+    return result;
+  }
+
+  bool nextBool() {
+    bool result = random.nextBool();
+    return result;
+  }
+}
+
+class ProgramCreator implements Visitor {
+  final WrappedRandom random = new WrappedRandom();
+  late final StringBufferWrapper sb;
+  int depth = 0;
+  int? currentExpressionStart;
+  int? currentTopLevelStart;
+  int singleLineStringDepth = 0;
+  int numericLiteralDepth = 0;
+  final List<String> path = [];
+
+  bool isDepthHigh() => depth > 1000;
+  bool isOutputLarge() =>
+      sb.length > 14000 ||
+      (currentTopLevelStart != null &&
+          (sb.length - currentTopLevelStart!) > 700);
+  bool isBigExpression() =>
+      currentExpressionStart != null &&
+      (sb.length - currentExpressionStart!) >= 20;
+
+  ProgramCreator({required bool createWhy}) {
+    sb = new StringBufferWrapper(createWhy, this);
+  }
+
+  @override
+  void defaultNode(Node node) {
+    throw "Should never get to defaultNode";
+  }
+
+  @override
+  void visitAnyCharNode(AnyCharNode node) {
+    int from = $SPACE;
+    int to = $z;
+    sb.writeCharCode(random.nextInt(to - from + 1) + from, "visitAnyCharNode");
+  }
+
+  @override
+  void visitDummyNode(DummyNode node) {
+    throw "Should never get to visitDummyNode";
+  }
+
+  @override
+  void visitEmpty(Empty node) {
+    // do nothing
+  }
+
+  @override
+  void visitEndOfFileNode(EndOfFileNode node) {
+    // do nothing
+  }
+
+  @override
+  void visitLeaf(Leaf node) {
+    depth++;
+    final String what = node.token.value;
+    path.add(what);
+    if (what == "singleLineString") {
+      singleLineStringDepth++;
+    } else if (what == "numericLiteral") {
+      numericLiteralDepth++;
+    }
+    int? oldExpressionStart = currentExpressionStart;
+    if (what == "expression" && oldExpressionStart == null) {
+      // Only write if it's not already set.
+      currentExpressionStart = sb.length;
+    }
+    int? oldTopLevelStart = currentTopLevelStart;
+    if (what == "topLevelDefinition" && oldTopLevelStart == null) {
+      // Only write if it's not already set.
+      currentTopLevelStart = sb.length;
+    }
+
+    const newLinesAround = {
+      "topLevelDefinition",
+      "libraryName",
+      "partDirective"
+    };
+    const newLinesAfter = {"metadatum"};
+    bool addNewlinesAround = newLinesAround.contains(what);
+    bool addNewlinesAfter = newLinesAfter.contains(what);
+
+    if (addNewlinesAround) {
+      sb.addNewline();
+    }
+
+    Node linked = data[what]!;
+    linked.accept(this);
+
+    if (addNewlinesAround || addNewlinesAfter) {
+      sb.addNewline();
+    }
+
+    currentExpressionStart = oldExpressionStart;
+    currentTopLevelStart = oldTopLevelStart;
+
+    if (what == "singleLineString") {
+      singleLineStringDepth--;
+    } else if (what == "numericLiteral") {
+      numericLiteralDepth--;
+    }
+
+    if (path.removeLast() != what) throw "Bad remove!";
+    depth--;
+  }
+
+  @override
+  void visitLiteral(Literal node) {
+    sb.write(node.content, "visitLiteral");
+  }
+
+  @override
+  void visitOneOrMore(OneOrMore node) {
+    depth++;
+    int count = random.nextInt(3) + 1;
+    if (isDepthHigh() || isOutputLarge() || isBigExpression()) count = 1;
+    for (int i = 0; i < count; i++) {
+      node.visitChildren(this);
+    }
+    depth--;
+  }
+
+  @override
+  void visitOptional(Optional node) {
+    if (isDepthHigh() || isOutputLarge() || isBigExpression()) return;
+
+    depth++;
+    if (random.nextInt(10) < 2) {
+      node.visitChildren(this);
+    }
+    depth--;
+  }
+
+  @override
+  void visitOrNode(OrNode node) {
+    depth++;
+    List<Node> nodes = node.nodesFiltered;
+    if (nodes.length == 1) {
+      nodes[0].accept(this);
+    } else {
+      // In practise some should be more likely than others.
+      if (nodes.length == 2 &&
+          nodes[0] is Leaf &&
+          (nodes[0] as Leaf).token.value == "libraryDeclaration" &&
+          nodes[1] is Leaf &&
+          (nodes[1] as Leaf).token.value == "partDeclaration") {
+        // libraryDeclaration should be more likely.
+        if (random.nextInt(100) < 95) {
+          nodes[0].accept(this);
+        } else {
+          nodes[1].accept(this);
+        }
+      } else {
+        Node? chosen;
+        bool pickSmallestDepth = false;
+        if (isBigExpression() || isDepthHigh() || isOutputLarge()) {
+          // Choose the one with the smallest depth.
+          pickSmallestDepth = true;
+        } else if (random.nextBool() &&
+            1 + 1 == 3 /* i.e. currently disabled */) {
+          // If depth and length is not too big yet, and if several nodes has
+          // depth <= 10, choose randomly among those.
+          List<Node> smallDepthNodes = [];
+          for (Node childNode in nodes) {
+            if (childNode.depth! > 0 && (childNode.depth! <= 10)) {
+              smallDepthNodes.add(childNode);
+            }
+          }
+          if (smallDepthNodes.isNotEmpty) {
+            chosen = smallDepthNodes[random.nextInt(smallDepthNodes.length)];
+          }
+        }
+
+        if (pickSmallestDepth) {
+          chosen = nodes[0];
+          for (Node childNode in nodes) {
+            if (childNode.depth! > 0 &&
+                (childNode.depth! < chosen!.depth! || chosen.depth! < 0)) {
+              chosen = childNode;
+            }
+          }
+          if (chosen!.depth! < 0) {
+            throw "Not good";
+          }
+        }
+        if (chosen == null) {
+          chosen = nodes[random.nextInt(nodes.length)];
+        }
+
+        chosen.accept(this);
+      }
+    }
+    depth--;
+  }
+
+  @override
+  void visitRangeNode(RangeNode node) {
+    String a = node.a.content;
+    String b = node.b.content;
+    if (a.length != 1 || b.length != 1) {
+      throw "Unexpected range: $node ('$a' '$b')";
+    }
+    int from = a.codeUnitAt(0);
+    int to = b.codeUnitAt(0);
+    sb.writeCharCode(random.nextInt(to - from + 1) + from, "visitRangeNode");
+  }
+
+  @override
+  void visitSequence(Sequence node) {
+    depth++;
+    node.visitChildren(this);
+    if (node.nodes.isNotEmpty) {
+      Node last = node.nodes.last;
+      if (last is Literal && last.content == ";") {
+        sb.addNewline();
+      }
+    }
+    depth--;
+  }
+
+  @override
+  void visitTildeNode(TildeNode node) {
+    Set<int> no = {};
+    List<Node> getNodes(Node node) {
+      List<Node> result = [];
+      if (node is Sequence) {
+        for (Node childNode in node.nodes) {
+          result.addAll(getNodes(childNode));
+        }
+      } else if (node is OrNode) {
+        for (Node childNode in node.nodes) {
+          result.addAll(getNodes(childNode));
+        }
+      } else if (node is Literal) {
+        result.add(node);
+      } else {
+        throw "Can't get nodes of ${node} (${node.runtimeType})";
+      }
+      return result;
+    }
+
+    List<Node> nodes = getNodes(node.a);
+    for (Node childNode in nodes) {
+      if (childNode is! Literal) {
+        throw "Not a literal...: ${childNode.runtimeType}: $node";
+      }
+      Literal literal = childNode;
+      String s = literal.content;
+      for (int i = 0; i < s.length; i++) {
+        no.add(s.codeUnitAt(i));
+      }
+    }
+
+    int from = $SPACE;
+    int to = $z;
+    int charCode = random.nextInt(to - from + 1) + from;
+    while (no.contains(charCode)) {
+      charCode = random.nextInt(to - from + 1) + from;
+    }
+    sb.writeCharCode(charCode, "visitTildeNode");
+  }
+
+  @override
+  void visitZeroOrMore(ZeroOrMore node) {
+    depth++;
+    int count = random.nextInt(3);
+    if (node.a is Sequence &&
+        (node.a as Sequence)
+            .nodes
+            .where(
+                (Node e) => e is Leaf && e.token.value == "topLevelDefinition")
+            .isNotEmpty) {
+      // We likely want more top level definitions.
+      count = random.nextInt(20);
+    } else if (node.a is Sequence &&
+        (node.a as Sequence)
+            .nodes
+            .where((Node e) => e is Leaf && e.token.value == "metadatum")
+            .isNotEmpty) {
+      // We likely don't want too many metadata entries...
+      if (random.nextInt(100) < 90) {
+        count = 0;
+      }
+    }
+    if (isDepthHigh() || isOutputLarge() || isBigExpression()) {
+      count = 0;
+    }
+    for (int i = 0; i < count; i++) {
+      node.visitChildren(this);
+
+      if (isBigExpression()) {
+        break;
+      }
+    }
+    depth--;
+  }
+}
+
+class SanityChecker extends Visitor {
+  ProgramCreator programCreator = new ProgramCreator(createWhy: false);
+
+  @override
+  void defaultNode(Node node) {
+    if (node.depth == null) throw "$node (${node.runtimeType}) has null depth";
+    if (node is TildeNode) {
+      programCreator.visitTildeNode(node);
+    }
+    super.defaultNode(node);
+  }
+}
+
+class DepthMarker implements Visitor {
+  bool recurse = false;
+
+  @override
+  void defaultNode(Node node) {
+    throw "Should never get here.";
+  }
+
+  @override
+  void visitAnyCharNode(AnyCharNode node) {
+    node.depth = 1;
+    if (recurse) node.visitChildren(this);
+  }
+
+  @override
+  void visitDummyNode(DummyNode node) {
+    node.depth = 1;
+    if (recurse) node.visitChildren(this);
+  }
+
+  @override
+  void visitEmpty(Empty node) {
+    node.depth = 1;
+    if (recurse) node.visitChildren(this);
+  }
+
+  @override
+  void visitEndOfFileNode(EndOfFileNode node) {
+    node.depth = 1;
+    if (recurse) node.visitChildren(this);
+  }
+
+  @override
+  void visitLeaf(Leaf node) {
+    Node linked = data[node.token.value]!;
+    if (linked.depth != null && linked.depth! > 0) {
+      node.depth = linked.depth! + 1;
+    } else if (node.depth == null) {
+      node.depth = -1;
+      linked.accept(this);
+      if (linked.depth! > 0) {
+        node.depth = linked.depth! + 1;
+      } else {
+        // Recursive... Keep the -1 depth and see that as "infinite".
+      }
+    } else {
+      // Recursive... Keep the -1 depth and see that as "infinite".
+    }
+  }
+
+  @override
+  void visitLiteral(Literal node) {
+    node.depth = 1;
+    if (recurse) node.visitChildren(this);
+  }
+
+  @override
+  void visitOneOrMore(OneOrMore node) {
+    node.visitChildren(this);
+    node.depth = node.a.depth! + 1;
+  }
+
+  @override
+  void visitOptional(Optional node) {
+    node.depth = 1;
+    if (recurse) node.visitChildren(this);
+  }
+
+  @override
+  void visitOrNode(OrNode node) {
+    node.visitChildren(this);
+    int min = node.nodes.first.depth!;
+    for (Node node in node.nodes) {
+      if (node.depth! > 0 && (node.depth! < min || min < 0)) min = node.depth!;
+    }
+    node.depth = min + 1;
+  }
+
+  @override
+  void visitRangeNode(RangeNode node) {
+    node.depth = 1;
+    if (recurse) node.visitChildren(this);
+  }
+
+  @override
+  void visitSequence(Sequence node) {
+    node.visitChildren(this);
+    int max = node.nodes.first.depth!;
+    for (Node childNode in node.nodes) {
+      if (childNode.depth! > max) max = childNode.depth!;
+      if (childNode.depth == -1) max = -1;
+      if (max == -1) {
+        node.depth = -1;
+        return;
+      }
+    }
+    node.depth = max + 1;
+  }
+
+  @override
+  void visitTildeNode(TildeNode node) {
+    // Naively assume that this is true.
+    node.depth = 1;
+    if (recurse) node.visitChildren(this);
+  }
+
+  @override
+  void visitZeroOrMore(ZeroOrMore node) {
+    node.depth = 1;
+    if (recurse) node.visitChildren(this);
+  }
+}
+
+class EntryChecker extends Visitor {
+  String? parent;
+
+  @override
+  void visitLeaf(Leaf node) {
+    if (!data.containsKey(node.token.value)) {
+      throw "Reference to unknown entry '${node.token.value}' from '$parent'";
+    }
+  }
+}
+
+Token? parseDef(Token token) {
+  if (token.value == "grammar" &&
+      token.next?.next?.value == ";" &&
+      token.next?.next?.next != null) {
+    // Skip something like 'grammar Dart;'.
+    token = token.next!.next!.next!;
+  }
+  if (token.value == "fragment") {
+    // Skip 'fragment'.
+    token = token.next!;
+  }
+  while (token.value == "@") {
+    // Skip injected java code.
+    while (token.value != "{") {
+      token = token.next!;
+    }
+    int count = 1;
+    token = token.next!;
+    while (count > 0) {
+      if (token.value == "{") {
+        count++;
+      } else if (token.value == "}") {
+        count--;
+      }
+      token = token.next!;
+      if (count == 0) {
+        break;
+      }
+    }
+  }
+  Token name = token;
+  token = token.next!;
+  if (token.value != ":") throw "Expected ':' at around ${token.offset}";
+  token = token.next!;
+  token = parseContent(token, depth: 1);
+  if (token.value != ";") throw "Expected ';' at around offset ${token.offset}";
+
+  data[name.value] = stack.removeLast();
+  if (stack.isNotEmpty) throw "Expected stack to be empty";
+
+  return token.next;
+}
+
+void join() {
+  if (stack.length > 1) {
+    List<Node> sequenceList = [];
+    for (Node node in stack) {
+      if (node is! DummyNode) sequenceList.add(node);
+    }
+    if (sequenceList.length > 1) {
+      // If something like "'a' .. 'z'" make that into a RangeNode.
+      if (sequenceList.length == 3 &&
+          sequenceList[0] is Literal &&
+          sequenceList[1] is Leaf &&
+          (sequenceList[1] as Leaf).token.value == ".." &&
+          sequenceList[2] is Literal) {
+        RangeNode range = new RangeNode(
+            sequenceList[0] as Literal, sequenceList[2] as Literal);
+        stack = [range];
+      } else {
+        Sequence sequence = new Sequence(sequenceList);
+        stack = [sequence];
+      }
+    } else {
+      stack = sequenceList;
+    }
+  }
+}
+
+Token parseContent(Token token, {required int depth}) {
+  Token originalToken = token;
+  try {
+    while (token.value != ";" && token.value != ")") {
+      if (token.value == "|" && stack.isEmpty) {
+        stderr.writeln("Warning: Seemingly stray '|' at ${token.offset}");
+        token = token.next!;
+      }
+      if (token.value == "|") {
+        if (token != originalToken && depth > 1) break;
+        join();
+        Node a = stack.removeLast();
+        token = parseContent(token.next!, depth: depth + 1);
+        Node b = stack.removeLast();
+        OrNode orNode = new OrNode();
+        if (a is OrNode) {
+          orNode.nodes.addAll(a.nodes);
+        } else {
+          orNode.nodes.add(a);
+        }
+        if (b is OrNode) {
+          orNode.nodes.addAll(b.nodes);
+        } else {
+          orNode.nodes.add(b);
+        }
+        stack.add(orNode);
+      } else if (token.value == "(") {
+        List<Node> savedStack = stack;
+        stack = [];
+        token = parseContent(token.next!, depth: 1);
+        if (token.value != ")") throw "Expected ')'";
+        if (stack.isEmpty) {
+          // ()
+          stack.add(new Empty());
+        }
+        if (stack.length != 1) throw "Expected stack to have length 1.";
+        savedStack.addAll(stack);
+        stack = savedStack;
+        token = token.next!;
+      } else if (token.value == "{") {
+        token = parseBrackets(token.next!);
+      } else if (token.value == "'") {
+        token = parseLiteral(token.next!);
+      } else if (token.value == "?") {
+        Node a = stack.removeLast();
+        if (a is DummyNode) {
+          stack.add(a);
+        } else {
+          stack.add(new Optional(a));
+        }
+        token = token.next!;
+      } else if (token.value == "+") {
+        Node a = stack.removeLast();
+        stack.add(new OneOrMore(a));
+        token = token.next!;
+      } else if (token.value == "*") {
+        Node a = stack.removeLast();
+        stack.add(new ZeroOrMore(a));
+        token = token.next!;
+      } else if (token.value == "~") {
+        token = parseContent(token.next!, depth: depth + 1);
+        Node a = stack.removeLast();
+        stack.add(new TildeNode(a));
+      } else if (token.value == ".") {
+        stack.add(new AnyCharNode(token));
+        token = token.next!;
+      } else if (token.value == "EOF") {
+        stack.add(new EndOfFileNode(token));
+        token = token.next!;
+      } else {
+        stack.add(new Leaf(token));
+        token = token.next!;
+      }
+    }
+    join();
+  } catch (e, st) {
+    print("Got error at around ${token.offset}: $st");
+    rethrow;
+  }
+  return token;
+}
+
+Token parseBrackets(Token token) {
+  while (token.value != "}") {
+    token = token.next!;
+  }
+  stack.add(new DummyNode());
+  return token.next!;
+}
+
+Token parseLiteral(Token token) {
+  List<String> content = [];
+  while (token.value != "'") {
+    if (token.value == r"\" && token.next != null) {
+      // Escape stuff...
+      Token next = token.next!;
+      if (next.value == '\'') {
+        content.add(next.value);
+        token = next.next!;
+      } else if (next.value == '\\') {
+        content.add("\\");
+        token = next.next!;
+      } else if (next.value == 'r') {
+        content.add("\r");
+        token = next.next!;
+      } else if (next.value == 'n') {
+        content.add("\n");
+        token = next.next!;
+      } else if (next.value == 'uFEFF') {
+        content.add("\uFEFF");
+        token = next.next!;
+      } else if (next.value == 't') {
+        content.add("\t");
+        token = next.next!;
+      } else {
+        print(token.next!.value);
+      }
+    } else {
+      content.add(token.value);
+      token = token.next!;
+    }
+  }
+  stack.add(new Literal(content.join()));
+  return token.next!;
+}
+
+List<Token> tokenize(List<int> bytes) {
+  List<int> tmp = [];
+  int index = 0;
+  bool inComment = false;
+  bool inQuote = false;
+  bool inEscaped = false;
+  List<Token> tokens = [];
+
+  void pushToken() {
+    if (inComment) {
+      tmp.clear();
+    } else if (tmp.isNotEmpty) {
+      String s = utf8.decode(tmp);
+      if (s == "." && tokens.isNotEmpty && tokens.last.value == ".") {
+        // Hack: Join them.
+        Token last = tokens.removeLast();
+        Token nextToken = new Token("..", last.offset);
+        if (tokens.isNotEmpty) tokens.last.next = nextToken;
+        tokens.add(nextToken);
+      } else {
+        Token nextToken = new Token(s, index);
+        if (tokens.isNotEmpty) tokens.last.next = nextToken;
+        tokens.add(nextToken);
+      }
+      tmp.clear();
+    }
+  }
+
+  while (index < bytes.length) {
+    int b = bytes[index++];
+    if (b == $LF || b == $CR) {
+      pushToken();
+      inComment = false;
+      inEscaped = false;
+    } else if (inComment) {
+      // ignore comments.
+    } else if (!inQuote &&
+        b == $SLASH &&
+        index < bytes.length &&
+        bytes[index] == $SLASH) {
+      inComment = true;
+    } else if (!inEscaped &&
+        b == $BACKSLASH &&
+        index < bytes.length &&
+        ((bytes[index] == $SQ) || (bytes[index] == $BACKSLASH))) {
+      // Escaped ' or escaped \.
+      inEscaped = true;
+      pushToken();
+      tmp.add(b);
+      pushToken();
+    } else if (b == $SQ) {
+      if (!inEscaped) {
+        inQuote = !inQuote;
+      }
+      inEscaped = false;
+      pushToken();
+      tmp.add(b);
+      pushToken();
+    } else if (b == $SPACE && !inQuote) {
+      pushToken();
+      inEscaped = false;
+    } else if ((b >= $0 && b <= $9) ||
+        (b >= $a && b <= $z) ||
+        (b >= $A && b <= $Z) ||
+        b == $_) {
+      tmp.add(b);
+      inEscaped = false;
+    } else {
+      pushToken();
+      tmp.add(b);
+      pushToken();
+      inEscaped = false;
+    }
+  }
+  pushToken();
+
+  return tokens;
+}
+
+class Token {
+  final String value;
+  final int offset;
+  Token? next;
+
+  Token(this.value, this.offset);
+
+  @override
+  String toString() => "$value";
+}
+
+abstract class Node {
+  /// Not really depth,
+  /// but minimum number of steps to the possibility of ending.
+  int? depth;
+
+  void accept(Visitor visitor);
+  void visitChildren(Visitor visitor);
+}
+
+class DummyNode extends Node {
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitDummyNode(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {}
+}
+
+class Empty extends Node {
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitEmpty(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {}
+}
+
+class AnyCharNode extends Node {
+  final Token token;
+
+  AnyCharNode(this.token);
+
+  @override
+  String toString() => "$token";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitAnyCharNode(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {}
+}
+
+class EndOfFileNode extends Node {
+  final Token token;
+
+  EndOfFileNode(this.token);
+
+  @override
+  String toString() => "$token";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitEndOfFileNode(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {}
+}
+
+class Leaf extends Node {
+  final Token token;
+
+  Leaf(this.token);
+
+  @override
+  String toString() => "$token";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitLeaf(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {}
+}
+
+class Sequence extends Node {
+  final List<Node> nodes;
+
+  Sequence(this.nodes);
+
+  @override
+  String toString() => "$nodes";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitSequence(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {
+    for (Node node in nodes) {
+      node.accept(visitor);
+    }
+  }
+}
+
+class OrNode extends Node {
+  List<Node> nodes = [];
+  List<Node>? _nodesFiltered;
+
+  List<Node> get nodesFiltered {
+    return _nodesFiltered ??= _filterNodes();
+  }
+
+  List<Node> _filterNodes() {
+    bool skipLeaf(Leaf leaf) {
+      const Set<String> skip = {
+        "functionType",
+        "multiLineString",
+        "SUPER",
+        "awaitExpression",
+        "functionExpression",
+      };
+      return skip.contains(leaf.token.value);
+    }
+
+    List<Node> result = [];
+    for (Node node in nodes) {
+      if (node is Sequence &&
+          node.nodes
+              .where((child) => child is Leaf && skipLeaf(child))
+              .isNotEmpty) {
+        // Skip
+      } else if (node is Leaf && skipLeaf(node)) {
+        // Skip
+      } else {
+        result.add(node);
+      }
+    }
+    if (result.isEmpty) return nodes;
+    return result;
+  }
+
+  @override
+  String toString() => "${nodes.join(" | ")}";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitOrNode(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {
+    for (Node node in nodes) {
+      node.accept(visitor);
+    }
+  }
+}
+
+class RangeNode extends Node {
+  final Literal a;
+  final Literal b;
+
+  RangeNode(this.a, this.b);
+
+  @override
+  String toString() => "$a .. $b";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitRangeNode(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {
+    a.accept(visitor);
+    b.accept(visitor);
+  }
+}
+
+class Optional extends Node {
+  final Node a;
+
+  Optional(this.a);
+
+  @override
+  String toString() => "($a)?";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitOptional(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {
+    a.accept(visitor);
+  }
+}
+
+class OneOrMore extends Node {
+  final Node a;
+
+  OneOrMore(this.a);
+
+  @override
+  String toString() => "($a)+";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitOneOrMore(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {
+    a.accept(visitor);
+  }
+}
+
+class ZeroOrMore extends Node {
+  final Node a;
+
+  ZeroOrMore(this.a);
+
+  @override
+  String toString() => "($a)*";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitZeroOrMore(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {
+    a.accept(visitor);
+  }
+}
+
+class TildeNode extends Node {
+  final Node a;
+
+  TildeNode(this.a);
+
+  @override
+  String toString() => "~($a)";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitTildeNode(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {
+    a.accept(visitor);
+  }
+}
+
+class Literal extends Node {
+  String content;
+
+  Literal(this.content);
+
+  @override
+  String toString() => "'${content}'";
+
+  @override
+  void accept(Visitor visitor) {
+    visitor.visitLiteral(this);
+  }
+
+  @override
+  void visitChildren(Visitor visitor) {}
+}
+
+abstract class Visitor {
+  void defaultNode(Node node) {
+    node.visitChildren(this);
+  }
+
+  void visitDummyNode(DummyNode node) => defaultNode(node);
+  void visitEmpty(Empty node) => defaultNode(node);
+  void visitLeaf(Leaf node) => defaultNode(node);
+  void visitAnyCharNode(AnyCharNode node) => defaultNode(node);
+  void visitEndOfFileNode(EndOfFileNode node) => defaultNode(node);
+  void visitSequence(Sequence node) => defaultNode(node);
+  void visitOrNode(OrNode node) => defaultNode(node);
+  void visitRangeNode(RangeNode node) => defaultNode(node);
+  void visitOptional(Optional node) => defaultNode(node);
+  void visitOneOrMore(OneOrMore node) => defaultNode(node);
+  void visitZeroOrMore(ZeroOrMore node) => defaultNode(node);
+  void visitTildeNode(TildeNode node) => defaultNode(node);
+  void visitLiteral(Literal node) => defaultNode(node);
+}
diff --git a/pkg/front_end/tool/fuzz/minimizer.dart b/pkg/front_end/tool/fuzz/minimizer.dart
new file mode 100644
index 0000000..42f33de
--- /dev/null
+++ b/pkg/front_end/tool/fuzz/minimizer.dart
@@ -0,0 +1,244 @@
+// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:convert';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:_fe_analyzer_shared/src/scanner/token.dart';
+
+import 'package:front_end/src/util/parser_ast.dart';
+import 'package:front_end/src/util/parser_ast_helper.dart';
+
+import '../interval_list.dart';
+import 'compile_helper.dart';
+import 'stacktrace_utils.dart';
+
+const bool debug = false;
+
+Future<void> main(List<String> args) async {
+  if (args.length != 1) throw "Needs exactly 1 input.";
+
+  Helper helper = new Helper();
+  await helper.setup();
+
+  Uint8List rawBytes = new File(args.single).readAsBytesSync();
+  String sourceAsString = utf8.decode(rawBytes);
+
+  (Object, StackTrace)? compile = await helper.compile(sourceAsString);
+  if (compile == null) throw "Input doesn't crash.";
+  String categorized = categorize(compile.$2);
+
+  CompilationUnitEnd ast = getAST(
+    rawBytes,
+    includeComments: true,
+    enableExtensionMethods: true,
+    enableNonNullable: true,
+    enableTripleShift: true,
+    allowPatterns: true,
+  );
+  ast.debugPrint();
+
+  Visitor v = new Visitor(helper, categorized, sourceAsString);
+  Token firstToken = (ast.children!.first as CompilationUnitBegin).token;
+  await v.walkTokens(firstToken);
+  await ast.accept(v);
+
+  String? minimized = v.getMinimizedSource();
+  if (minimized == null) {
+    print("Couldn't minimize.");
+  } else {
+    print("Minimized to: ");
+    print(minimized);
+  }
+}
+
+class Visitor extends RecursiveParserAstVisitorWithDefaultNodeAsync {
+  final Helper helper;
+  final String wantedCategorizedCrash;
+  final String initialSource;
+  List<(int, int)> removeable = [];
+
+  Visitor(this.helper, this.wantedCategorizedCrash, this.initialSource) {
+    prevBraceInfo = initialSource.getBraceCountString();
+    print("Initial source brace info: $prevBraceInfo");
+  }
+
+  Future<void> walkTokens(Token token) async {
+    Token t = token;
+    while (!t.isEof) {
+      if (t.endGroup != null) {
+        if (await _canRemove("Token range", t.runtimeType, t, t.endGroup!)) {
+          t = t.endGroup!.next!;
+          continue;
+        }
+      }
+      t = t.next!;
+    }
+  }
+
+  @override
+  Future<void> defaultNode(ParserAstNode node) async {
+    if (node is BeginAndEndTokenParserAstNode) {
+      if (await _canRemove(
+          node.what, node.runtimeType, node.beginToken, node.endToken)) {
+        return;
+      }
+    } else if (node is BinaryExpressionEnd) {
+      if (await _canRemove(
+          node.what, node.runtimeType, node.token, node.endToken)) {
+        return;
+      }
+    } else if (node is ThrowExpressionHandle) {
+      if (await _canRemove(
+          node.what, node.runtimeType, node.throwToken, node.endToken)) {
+        return;
+      }
+    } else if (node is LabelHandle) {
+      if (await _canRemove(
+          node.what, node.runtimeType, node.token.previous!, node.token)) {
+        return;
+      }
+    } else if (node is CaseExpressionEnd) {
+      if (await _canRemove(
+          node.what, node.runtimeType, node.caseKeyword, node.colon)) {
+        return;
+      }
+    } else if (node is UnaryPrefixAssignmentExpressionHandle) {
+      if (await _canRemove(
+          node.what, node.runtimeType, node.token, node.token)) {
+        return;
+      }
+    } else if (node is UnaryPrefixExpressionHandle) {
+      if (await _canRemove(
+          node.what, node.runtimeType, node.token, node.token)) {
+        return;
+      }
+    } else if (node is EmptyStatementHandle) {
+      if (await _canRemove(
+          node.what, node.runtimeType, node.token, node.token)) {
+        return;
+      }
+    } else if (node is AssignmentExpressionHandle) {
+      if (await _canRemove(
+          node.what, node.runtimeType, node.token, node.endToken)) {
+        return;
+      }
+    } else if (node is ConditionalExpressionEnd) {
+      if (await _canRemove(node.what, node.runtimeType, node.question.next!,
+          node.colon.previous!)) {
+        // Fine, but we still have to recurse.
+      }
+      if (await _canRemove(
+          node.what, node.runtimeType, node.colon.next!, node.endToken)) {
+        // Fine, but we still have to recurse.
+      }
+    } else if (node is BinaryPatternEnd) {
+      // This seems a little dicy.
+      if (await _canRemove(
+          node.what, node.runtimeType, node.token, node.token)) {
+        return;
+      }
+    } else if (node is PartEnd) {
+      if (await _canRemove(
+          node.what, node.runtimeType, node.partKeyword, node.semicolon)) {
+        return;
+      }
+    } else if (node is TypedefEnd) {
+      if (await _canRemove(node.what, node.runtimeType,
+          node.augmentToken ?? node.typedefKeyword, node.endToken)) {
+        return;
+      }
+    }
+    // We can't remove the node (or it isn't a Begin/End one) - recurse.
+    await super.defaultNode(node);
+  }
+
+  Future<bool> _canRemove(
+      String what, Type type, Token beginToken, Token endToken) async {
+    int length = endToken.charEnd - beginToken.offset;
+    if (length > 0) {
+      String newSource =
+          _constructSourceFromRemovableAnd((type, beginToken, endToken));
+      print("Compile (${newSource.length} vs ${initialSource.length})");
+      (Object, StackTrace)? compile = await helper.compile(newSource);
+      if (compile != null) {
+        String categorized = categorize(compile.$2);
+        if (categorized == wantedCategorizedCrash) {
+          print(" => Can remove $what with length $length");
+          // We can actually do this.
+          removeable.add((beginToken.charOffset, endToken.charEnd));
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  String _constructSourceFromRemovableAnd(
+      (Type type, Token beginToken, Token endToken)? nodeInfo) {
+    IntervalListBuilder intervalListBuilder = new IntervalListBuilder();
+    if (nodeInfo != null) {
+      intervalListBuilder.addIntervalExcludingEnd(
+          nodeInfo.$2.charOffset, nodeInfo.$3.charEnd);
+    }
+    for ((int, int) removeThis in removeable) {
+      intervalListBuilder.addIntervalExcludingEnd(removeThis.$1, removeThis.$2);
+    }
+    IntervalList intervalList = intervalListBuilder.buildIntervalList();
+    int from = 0;
+    StringBuffer sb = new StringBuffer();
+    intervalList.forEach((int open, int close) {
+      sb.write(initialSource.substring(from, open));
+      from = close;
+    });
+    sb.write(initialSource.substring(from, initialSource.length));
+    String output = sb.toString();
+    if (debug) {
+      String braceInfo = output.getBraceCountString();
+      if (braceInfo != prevBraceInfo) {
+        print("After cutting out ${nodeInfo?.$1} at "
+            "${nodeInfo?.$2.charOffset} brace info: "
+            "$braceInfo");
+        prevBraceInfo = braceInfo;
+      }
+    }
+    return output;
+  }
+
+  String? prevBraceInfo;
+
+  String? getMinimizedSource() {
+    if (removeable.isEmpty) return null;
+    return _constructSourceFromRemovableAnd(null);
+  }
+}
+
+extension on String {
+  List<int> count() {
+    List<int> result = List.filled(257, 0);
+    List<int> codeUnits = this.codeUnits;
+    for (int i = 0; i < codeUnits.length; i++) {
+      int unit = codeUnits[i];
+      if (unit < 0 || unit > 255) {
+        result[256]++;
+      } else {
+        result[unit]++;
+      }
+    }
+    return result;
+  }
+
+  String getBraceCountString() {
+    List<int> counts = count();
+    StringBuffer sb = new StringBuffer();
+    for (int codeUnit in [40, 41, 91, 93, 123, 125]) {
+      if (sb.isNotEmpty) sb.write(", ");
+      sb.writeCharCode(codeUnit);
+      sb.write(": ");
+      sb.write(counts[codeUnit]);
+    }
+    return sb.toString();
+  }
+}
diff --git a/pkg/front_end/tool/fuzz/post_fix_checker.dart b/pkg/front_end/tool/fuzz/post_fix_checker.dart
new file mode 100644
index 0000000..2a7399d
--- /dev/null
+++ b/pkg/front_end/tool/fuzz/post_fix_checker.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io' show Directory, File, FileSystemEntity;
+
+import '../../test/utils/io_utils.dart' show computeRepoDirUri;
+
+import 'compile_helper.dart';
+import 'stacktrace_utils.dart';
+
+final Uri repoDir = computeRepoDirUri();
+
+Future<void> main(List<String> args) async {
+  Directory d = new Directory(args.single);
+  Helper helper = new Helper();
+  await helper.setup();
+  int good = 0;
+  int bad = 0;
+  for (FileSystemEntity file in d.listSync(recursive: false)) {
+    if (file is! File) continue;
+    (Object, StackTrace)? result =
+        await helper.compile(file.readAsStringSync());
+    String filename = file.uri.pathSegments.last;
+    if (result == null) {
+      print("${filename}: OK");
+      good++;
+    } else {
+      print("${filename}: Still crashes: ${categorize(result.$2)}");
+      bad++;
+    }
+  }
+  print("Done. $good good, $bad bad.");
+}
diff --git a/pkg/front_end/tool/fuzz/reorganizer.dart b/pkg/front_end/tool/fuzz/reorganizer.dart
new file mode 100644
index 0000000..944826a
--- /dev/null
+++ b/pkg/front_end/tool/fuzz/reorganizer.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io' show Directory, File, FileSystemEntity;
+
+import '../../test/utils/io_utils.dart' show computeRepoDirUri;
+
+import 'compile_helper.dart';
+import 'stacktrace_utils.dart';
+
+final Uri repoDir = computeRepoDirUri();
+
+Future<void> main(List<String> args) async {
+  List<Directory> directories = [];
+  Directory? outputDirectory;
+  for (int i = 0; i < args.length; i++) {
+    String arg = args[i];
+    if (arg.startsWith("--output=")) {
+      outputDirectory =
+          new Directory(arg.substring("--output=".length)).absolute;
+    } else {
+      Directory d = new Directory(arg);
+      if (d.existsSync()) {
+        directories.add(d);
+      } else {
+        print("Error: $arg isn't a directory.");
+      }
+    }
+  }
+  if (outputDirectory == null) throw "No --output= given";
+
+  Helper helper = new Helper();
+  await helper.setup();
+  int good = 0;
+  int bad = 0;
+
+  for (int i = 0; i < directories.length; i++) {
+    Directory d = directories[i];
+    for (FileSystemEntity file in d.listSync(recursive: true)) {
+      if (file is! File) continue;
+      String content = file.readAsStringSync();
+      (Object, StackTrace)? result = await helper.compile(content);
+      String filename = file.uri.pathSegments.last;
+      if (result == null) {
+        print("${filename}: OK");
+        good++;
+      } else {
+        String category = categorize(result.$2);
+        print("${filename}: Still crashes: $category");
+        bad++;
+        Directory d =
+            new Directory.fromUri(outputDirectory.uri.resolve("$category/"));
+        d.createSync(recursive: true);
+        File f = new File.fromUri(
+            outputDirectory.uri.resolve("$category/$bad.input"));
+        f.writeAsStringSync(content);
+      }
+    }
+  }
+  print("Done. $good good, $bad bad.");
+}
diff --git a/pkg/front_end/tool/fuzz/stacktrace_utils.dart b/pkg/front_end/tool/fuzz/stacktrace_utils.dart
new file mode 100644
index 0000000..a3da0fc
--- /dev/null
+++ b/pkg/front_end/tool/fuzz/stacktrace_utils.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/scanner/characters.dart';
+
+String categorize(StackTrace st) {
+  List<StackTraceLine> lines = parseStackTrace(st);
+  List<StackTraceLine> notSdk =
+      lines.where((l) => l.uri.scheme != "dart").toList();
+  return "${notSdk.first.uri.pathSegments.last}/${notSdk.first.line}";
+}
+
+List<StackTraceLine> parseStackTrace(StackTrace st) {
+  List<StackTraceLine> result = [];
+  List<String> lines = st.toString().split("\n");
+  for (int i = 0; i < lines.length; i++) {
+    String s = lines[i];
+    if (s == "") continue;
+    if (s == "...") continue;
+    if (s == "<asynchronous suspension>") continue;
+    if (s.startsWith("#")) {
+      int j = 1;
+      for (; j < s.length; j++) {
+        int c = s.codeUnitAt(j);
+        if (c >= $0 && c <= $9) {
+          // #numGoesHere
+        } else {
+          break;
+        }
+      }
+      s = s.substring(j).trim();
+      int indexOfParen = s.indexOf("(");
+      String method = s.substring(0, indexOfParen).trim();
+      if (s[s.length - 1] != ")") throw "s";
+      String uriEtc = s.substring(indexOfParen + 1, s.length - 1);
+      List<String> colonSplit = uriEtc.split(":");
+      int column = -1;
+      int line = -1;
+      // If last two are numbers it's line and column --- the rest is uri;
+      // if last is number it's line --- the rest is uri
+      // else its all uri.
+      if (uriEtc.length >= 2) {
+        column = int.tryParse(colonSplit[colonSplit.length - 1]) ?? -1;
+        line = int.tryParse(colonSplit[colonSplit.length - 2]) ?? -1;
+      }
+      String uriPart;
+      if (line > -1 && column > -1) {
+        uriPart = colonSplit.take(colonSplit.length - 2).join(":");
+      } else if (column > -1) {
+        line = column;
+        column = -1;
+        uriPart = colonSplit.take(colonSplit.length - 1).join(":");
+      } else {
+        uriPart = uriEtc;
+      }
+      StackTraceLine stLine = new StackTraceLine(
+          method, Uri.parse(uriPart), line, column, lines[i]);
+      result.add(stLine);
+    } else {
+      throw "Unexpected line: '$s' in stacktrace: $st";
+    }
+  }
+
+  return result;
+}
+
+class StackTraceLine {
+  final String method;
+  final Uri uri;
+  final int line;
+  final int column;
+  final String orgLine;
+
+  StackTraceLine(this.method, this.uri, this.line, this.column, this.orgLine);
+}
diff --git a/pkg/front_end/tool/interval_list.dart b/pkg/front_end/tool/interval_list.dart
index 65c659a..476b97b 100644
--- a/pkg/front_end/tool/interval_list.dart
+++ b/pkg/front_end/tool/interval_list.dart
@@ -102,4 +102,10 @@
     }
     return low == high && (low & 1) == 0;
   }
+
+  void forEach(void action(int open, int close)) {
+    for (int i = 0; i < _intervalList.length; i += 2) {
+      action(_intervalList[i], _intervalList[i + 1]);
+    }
+  }
 }
diff --git a/pkg/front_end/tool/parser_ast_helper_creator.dart b/pkg/front_end/tool/parser_ast_helper_creator.dart
index f34871e..a58ccdc 100644
--- a/pkg/front_end/tool/parser_ast_helper_creator.dart
+++ b/pkg/front_end/tool/parser_ast_helper_creator.dart
@@ -77,6 +77,40 @@
     }
   }
 
+
+
+  void debugPrint() {
+    StringBuffer sb = new StringBuffer();
+    _debugPrintImpl(0, sb);
+    print(sb.toString());
+  }
+
+  void _debugPrintImpl(int indentation, StringBuffer sb) {
+    sb.write(" " * indentation);
+    sb.write(what);
+    sb.write(type.name);
+    Token? tokenWithSmallestOffset;
+    for (Object? value in deprecatedArguments.values) {
+      if (value is Token) {
+        if (tokenWithSmallestOffset == null ||
+            value.charOffset < tokenWithSmallestOffset.charOffset) {
+          tokenWithSmallestOffset = value;
+        }
+      }
+    }
+    if (tokenWithSmallestOffset != null) {
+      sb.write(
+          " (${tokenWithSmallestOffset.lexeme} @ "
+          "${tokenWithSmallestOffset.charOffset})");
+    }
+    sb.writeln();
+    List<ParserAstNode>? children = this.children;
+    if (children == null) return;
+    for (ParserAstNode child in children) {
+      child._debugPrintImpl(indentation + 2, sb);
+    }
+  }
+
   // TODO(jensj): Compare two ASTs.
 }
 
@@ -116,6 +150,22 @@
   }
   out.write(r"}");
 
+  out.write("class RecursiveParserAstVisitorWithDefaultNodeAsync "
+      "implements ParserAstVisitor<Future<void>> {");
+  out.write("Future<void> defaultNode(ParserAstNode node) async {");
+  out.write("  List<ParserAstNode>? children = node.children;");
+  out.write("  if (children == null) return;");
+  out.write("  for (ParserAstNode child in children) {");
+  out.write("    await child.accept(this);");
+  out.write("  }");
+  out.write("}");
+
+  for (String name in listener.visitNames) {
+    out.write("  @override\n");
+    out.write("  Future<void> $name => defaultNode(node);\n\n");
+  }
+  out.write(r"}");
+
   return new DartFormatter(
           languageVersion: DartFormatter.latestShortStyleLanguageVersion)
       .format("$out");