Macro. Extract most of the printer into shared.

Also a few more renames.
And element tests for class fields.

Change-Id: I3905f5b04c619371ff860efd6ae0e7d457cd4b58
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/334381
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/test/src/summary/macro/introspect_declarations_phase.dart b/pkg/analyzer/test/src/summary/macro/introspect_declarations_phase.dart
index 8583d0f..3df98e9 100644
--- a/pkg/analyzer/test/src/summary/macro/introspect_declarations_phase.dart
+++ b/pkg/analyzer/test/src/summary/macro/introspect_declarations_phase.dart
@@ -21,29 +21,9 @@
     IntrospectableClassDeclaration declaration,
     MemberDeclarationBuilder builder,
   ) async {
-    final buffer = StringBuffer();
-    final sink = TreeStringSink(
-      sink: buffer,
-      indent: '',
-    );
-
-    final printer = _Printer(
-      sink: sink,
-      withDetailsFor: {
-        declaration.identifier.name,
-        ...withDetailsFor.cast(),
-      },
-      declarationPhaseIntrospector: builder,
-    );
-    await printer.writeClassDeclaration(declaration);
-    final text = buffer.toString();
-
-    final resultName = 'introspect_${declaration.identifier.name}';
-    builder.declareInLibrary(
-      DeclarationCode.fromString(
-        'const $resultName = r"""$text""";',
-      ),
-    );
+    await _write(builder, declaration, (printer) async {
+      await printer.writeClassDeclaration(declaration);
+    });
   }
 
   @override
@@ -51,6 +31,18 @@
     IntrospectableMixinDeclaration declaration,
     MemberDeclarationBuilder builder,
   ) async {
+    await _write(builder, declaration, (printer) async {
+      await printer.writeMixinDeclaration(declaration);
+    });
+  }
+
+  Future<void> _write(
+    DeclarationBuilder builder,
+    Declaration declaration,
+    Future<void> Function(_Printer printer) f,
+  ) async {
+    final declarationName = declaration.identifier.name;
+
     final buffer = StringBuffer();
     final sink = TreeStringSink(
       sink: buffer,
@@ -59,16 +51,16 @@
 
     final printer = _Printer(
       sink: sink,
+      introspector: builder,
       withDetailsFor: {
-        declaration.identifier.name,
+        declarationName,
         ...withDetailsFor.cast(),
       },
-      declarationPhaseIntrospector: builder,
     );
-    await printer.writeMixinDeclaration(declaration);
+    await f(printer);
     final text = buffer.toString();
 
-    final resultName = 'introspect_${declaration.identifier.name}';
+    final resultName = 'introspect_$declarationName';
     builder.declareInLibrary(
       DeclarationCode.fromString(
         'const $resultName = r"""$text""";',
@@ -81,174 +73,19 @@
   @override
   final TreeStringSink sink;
 
-  final Set<String> withDetailsFor;
-  final DeclarationPhaseIntrospector declarationPhaseIntrospector;
+  @override
+  final DeclarationPhaseIntrospector introspector;
 
-  Identifier? _enclosingDeclarationIdentifier;
+  final Set<String> withDetailsFor;
 
   _Printer({
     required this.sink,
+    required this.introspector,
     required this.withDetailsFor,
-    required this.declarationPhaseIntrospector,
   });
 
-  Future<void> writeClassDeclaration(IntrospectableClassDeclaration e) async {
-    if (!_shouldWriteDetailsFor(e)) {
-      return;
-    }
-
-    sink.writelnWithIndent('class ${e.identifier.name}');
-
-    await sink.withIndent(() async {
-      await sink.writeFlags({
-        'hasAbstract': e.hasAbstract,
-        'hasBase': e.hasBase,
-        'hasExternal': e.hasExternal,
-        'hasFinal': e.hasFinal,
-        'hasInterface': e.hasInterface,
-        'hasMixin': e.hasMixin,
-        'hasSealed': e.hasSealed,
-      });
-      await writeMetadata(e);
-      if (e.superclass case final superclass?) {
-        await _writeNamedTypeAnnotation('superclass', superclass);
-      }
-      await _writeTypeParameters(e.typeParameters);
-      await _writeTypeAnnotations('mixins', e.mixins);
-      await _writeTypeAnnotations('interfaces', e.interfaces);
-
-      _enclosingDeclarationIdentifier = e.identifier;
-      await sink.writeElements<FieldDeclaration>(
-        'fields',
-        await declarationPhaseIntrospector.fieldsOf(e),
-        _writeField,
-      );
-    });
-  }
-
-  Future<void> writeMixinDeclaration(IntrospectableMixinDeclaration e) async {
-    if (!_shouldWriteDetailsFor(e)) {
-      return;
-    }
-
-    sink.writelnWithIndent('mixin ${e.identifier.name}');
-
-    await sink.withIndent(() async {
-      await sink.writeFlags({
-        'hasBase': e.hasBase,
-      });
-
-      await writeMetadata(e);
-
-      await _writeTypeParameters(e.typeParameters);
-      await _writeTypeAnnotations(
-        'superclassConstraints',
-        e.superclassConstraints,
-      );
-      await _writeTypeAnnotations('interfaces', e.interfaces);
-
-      _enclosingDeclarationIdentifier = e.identifier;
-      await sink.writeElements<FieldDeclaration>(
-        'fields',
-        await declarationPhaseIntrospector.fieldsOf(e),
-        _writeField,
-      );
-    });
-  }
-
-  void _assertEnclosingClass(MemberDeclaration e) {
-    if (e.definingType != _enclosingDeclarationIdentifier) {
-      throw StateError('Mismatch: definingClass');
-    }
-  }
-
-  bool _shouldWriteDetailsFor(Declaration declaration) {
+  @override
+  bool shouldWriteDetailsFor(Declaration declaration) {
     return withDetailsFor.contains(declaration.identifier.name);
   }
-
-  Future<void> _writeField(FieldDeclaration e) async {
-    _assertEnclosingClass(e);
-
-    sink.writelnWithIndent(e.identifier.name);
-
-    await sink.withIndent(() async {
-      await sink.writeFlags({
-        'hasExternal': e.hasExternal,
-        'hasFinal': e.hasFinal,
-        'hasLate': e.hasLate,
-        'isStatic': e.isStatic,
-      });
-      await writeMetadata(e);
-      await _writeNamedTypeAnnotation('type', e.type);
-    });
-  }
-
-  Future<void> _writeNamedTypeAnnotation(
-    String name,
-    TypeAnnotation? type,
-  ) async {
-    sink.writeWithIndent('$name: ');
-    await _writeTypeAnnotation(type);
-  }
-
-  Future<void> _writeTypeAnnotation(TypeAnnotation? type) async {
-    if (type != null) {
-      sink.writeln(type.asString);
-      await _writeTypeAnnotationDeclaration(type);
-    } else {
-      sink.writeln('null');
-    }
-  }
-
-  Future<void> _writeTypeAnnotationDeclaration(TypeAnnotation type) async {
-    await sink.withIndent(() async {
-      switch (type) {
-        case NamedTypeAnnotation():
-          final identifier = type.identifier;
-          try {
-            final declaration = await declarationPhaseIntrospector
-                .typeDeclarationOf(identifier);
-            switch (declaration) {
-              case IntrospectableClassDeclaration():
-                await writeClassDeclaration(declaration);
-              case IntrospectableMixinDeclaration():
-                await writeMixinDeclaration(declaration);
-              default:
-                throw UnimplementedError('${declaration.runtimeType}');
-            }
-          } on ArgumentError {
-            sink.writelnWithIndent('noDeclaration');
-          }
-        default:
-          throw UnimplementedError('(${type.runtimeType}) $type');
-      }
-    });
-  }
-
-  Future<void> _writeTypeAnnotations(
-    String name,
-    Iterable<TypeAnnotation> types,
-  ) async {
-    await sink.writeElements(name, types, (type) async {
-      sink.writeIndent();
-      await _writeTypeAnnotation(type);
-    });
-  }
-
-  Future<void> _writeTypeParameter(TypeParameterDeclaration e) async {
-    sink.writelnWithIndent(e.identifier.name);
-
-    await sink.withIndent(() async {
-      final bound = e.bound;
-      if (bound != null) {
-        await _writeNamedTypeAnnotation('bound', bound);
-      }
-    });
-  }
-
-  Future<void> _writeTypeParameters(
-    Iterable<TypeParameterDeclaration> elements,
-  ) async {
-    await sink.writeElements('typeParameters', elements, _writeTypeParameter);
-  }
 }
diff --git a/pkg/analyzer/test/src/summary/macro/introspect_shared.dart b/pkg/analyzer/test/src/summary/macro/introspect_shared.dart
index c327c34..5c50fb1 100644
--- a/pkg/analyzer/test/src/summary/macro/introspect_shared.dart
+++ b/pkg/analyzer/test/src/summary/macro/introspect_shared.dart
@@ -5,9 +5,126 @@
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
 
 mixin SharedPrinter {
+  Identifier? _enclosingDeclarationIdentifier;
+
+  TypePhaseIntrospector get introspector;
+
   TreeStringSink get sink;
 
-  Future<void> writeMetadata(Annotatable e) async {
+  bool shouldWriteDetailsFor(Declaration declaration) {
+    return true;
+  }
+
+  Future<void> writeClassDeclaration(ClassDeclaration e) async {
+    if (!shouldWriteDetailsFor(e)) {
+      return;
+    }
+
+    sink.writelnWithIndent('class ${e.identifier.name}');
+
+    await sink.withIndent(() async {
+      await sink.writeFlags({
+        'hasAbstract': e.hasAbstract,
+        'hasBase': e.hasBase,
+        'hasExternal': e.hasExternal,
+        'hasFinal': e.hasFinal,
+        'hasInterface': e.hasInterface,
+        'hasMixin': e.hasMixin,
+        'hasSealed': e.hasSealed,
+      });
+      await _writeMetadata(e);
+      if (e.superclass case final superclass?) {
+        await _writeNamedTypeAnnotation('superclass', superclass);
+      }
+      await _writeTypeParameters(e.typeParameters);
+      await _writeTypeAnnotations('mixins', e.mixins);
+      await _writeTypeAnnotations('interfaces', e.interfaces);
+      await _writeTypeDeclarationMembers(e);
+    });
+  }
+
+  Future<void> writeField(FieldDeclaration e) async {
+    _assertEnclosingClass(e);
+
+    sink.writelnWithIndent(e.identifier.name);
+
+    await sink.withIndent(() async {
+      await sink.writeFlags({
+        'hasExternal': e.hasExternal,
+        'hasFinal': e.hasFinal,
+        'hasLate': e.hasLate,
+        'isStatic': e.isStatic,
+      });
+      await _writeMetadata(e);
+      await _writeNamedTypeAnnotation('type', e.type);
+    });
+  }
+
+  Future<void> writeMethodDeclaration(MethodDeclaration e) async {
+    sink.writelnWithIndent(e.identifier.name);
+
+    await sink.withIndent(() async {
+      await sink.writeFlags({
+        'hasAbstract': e.hasAbstract,
+        'hasBody': e.hasBody,
+        'hasExternal': e.hasExternal,
+        'isGetter': e.isGetter,
+        'isOperator': e.isOperator,
+        'isSetter': e.isSetter,
+        'isStatic': e.isStatic,
+      });
+
+      await _writeMetadata(e);
+      await _writeNamedFormalParameters(e.namedParameters);
+      await _writePositionalFormalParameters(e.positionalParameters);
+      await _writeNamedTypeAnnotation('returnType', e.returnType);
+      await _writeTypeParameters(e.typeParameters);
+    });
+  }
+
+  Future<void> writeMixinDeclaration(MixinDeclaration e) async {
+    if (!shouldWriteDetailsFor(e)) {
+      return;
+    }
+
+    sink.writelnWithIndent('mixin ${e.identifier.name}');
+
+    await sink.withIndent(() async {
+      await sink.writeFlags({
+        'hasBase': e.hasBase,
+      });
+
+      await _writeMetadata(e);
+
+      await _writeTypeParameters(e.typeParameters);
+      await _writeTypeAnnotations(
+        'superclassConstraints',
+        e.superclassConstraints,
+      );
+      await _writeTypeAnnotations('interfaces', e.interfaces);
+      await _writeTypeDeclarationMembers(e);
+    });
+  }
+
+  void _assertEnclosingClass(MemberDeclaration e) {
+    if (e.definingType != _enclosingDeclarationIdentifier) {
+      throw StateError('Mismatch: definingClass');
+    }
+  }
+
+  Future<void> _writeFormalParameter(ParameterDeclaration e) async {
+    sink.writelnWithIndent(e.identifier.name);
+    await sink.withIndent(() async {
+      await sink.writeFlags({
+        'isNamed': e.isNamed,
+        'isRequired': e.isRequired,
+      });
+      await _writeMetadata(e);
+      await _writeNamedTypeAnnotation('type', e.type);
+    });
+  }
+
+  Future<void> _writeMetadata(Annotatable e) async {
     await sink.writeElements(
       'metadata',
       e.metadata,
@@ -34,6 +151,119 @@
       default:
     }
   }
+
+  Future<void> _writeNamedFormalParameters(
+    Iterable<ParameterDeclaration> elements,
+  ) async {
+    await sink.writeElements(
+      'namedParameters',
+      elements,
+      _writeFormalParameter,
+    );
+  }
+
+  Future<void> _writeNamedTypeAnnotation(
+    String name,
+    TypeAnnotation? type,
+  ) async {
+    sink.writeWithIndent('$name: ');
+    await _writeTypeAnnotation(type);
+  }
+
+  Future<void> _writePositionalFormalParameters(
+    Iterable<ParameterDeclaration> elements,
+  ) async {
+    await sink.writeElements(
+      'positionalParameters',
+      elements,
+      _writeFormalParameter,
+    );
+  }
+
+  Future<void> _writeTypeAnnotation(TypeAnnotation? type) async {
+    if (type != null) {
+      sink.writeln(type.asString);
+      await _writeTypeAnnotationDeclaration(type);
+    } else {
+      sink.writeln('null');
+    }
+  }
+
+  Future<void> _writeTypeAnnotationDeclaration(TypeAnnotation type) async {
+    final introspector = this.introspector;
+    if (introspector is! DeclarationPhaseIntrospector) {
+      return;
+    }
+
+    await sink.withIndent(() async {
+      switch (type) {
+        case NamedTypeAnnotation():
+          TypeDeclaration declaration;
+          try {
+            final identifier = type.identifier;
+            declaration = await introspector.typeDeclarationOf(identifier);
+          } on ArgumentError {
+            sink.writelnWithIndent('noDeclaration');
+            return;
+          }
+
+          switch (declaration) {
+            case ClassDeclaration():
+              await writeClassDeclaration(declaration);
+            case MixinDeclaration():
+              await writeMixinDeclaration(declaration);
+            default:
+              throw UnimplementedError('${declaration.runtimeType}');
+          }
+        default:
+          throw UnimplementedError('(${type.runtimeType}) $type');
+      }
+    });
+  }
+
+  Future<void> _writeTypeAnnotations(
+    String name,
+    Iterable<TypeAnnotation> types,
+  ) async {
+    await sink.writeElements(name, types, (type) async {
+      sink.writeIndent();
+      await _writeTypeAnnotation(type);
+    });
+  }
+
+  Future<void> _writeTypeDeclarationMembers(TypeDeclaration e) async {
+    _enclosingDeclarationIdentifier = e.identifier;
+
+    final introspector = this.introspector;
+    if (introspector is DeclarationPhaseIntrospector) {
+      if (e is IntrospectableType) {
+        await sink.writeElements(
+          'fields',
+          await introspector.fieldsOf(e),
+          writeField,
+        );
+      }
+    }
+
+    _enclosingDeclarationIdentifier = null;
+  }
+
+  Future<void> _writeTypeParameter(TypeParameterDeclaration e) async {
+    sink.writelnWithIndent(e.identifier.name);
+
+    await sink.withIndent(() async {
+      await _writeMetadata(e);
+      if (e.bound case final bound?) {
+        await _writeNamedTypeAnnotation('bound', bound);
+      }
+    });
+  }
+
+  Future<void> _writeTypeParameters(
+    Iterable<TypeParameterDeclaration> elements,
+  ) async {
+    await sink.writeElements('typeParameters', elements, _writeTypeParameter);
+  }
 }
 
 /// Wrapper around a [StringSink] for writing tree structures.
diff --git a/pkg/analyzer/test/src/summary/macro/introspect_types_phase.dart b/pkg/analyzer/test/src/summary/macro/introspect_types_phase.dart
index 500f7d6..b90afd3 100644
--- a/pkg/analyzer/test/src/summary/macro/introspect_types_phase.dart
+++ b/pkg/analyzer/test/src/summary/macro/introspect_types_phase.dart
@@ -14,50 +14,29 @@
 
   @override
   Future<void> buildTypesForClass(declaration, builder) async {
-    final buffer = StringBuffer();
-    final sink = TreeStringSink(
-      sink: buffer,
-      indent: '',
-    );
-
-    final printer = _Printer(
-      sink: sink,
-    );
-    await printer.writeClassDeclaration(declaration);
-    final text = buffer.toString();
-
-    builder.declareType(
-      'x',
-      DeclarationCode.fromString(
-        'const x = r"""$text""";',
-      ),
-    );
+    await _write(builder, (printer) async {
+      await printer.writeClassDeclaration(declaration);
+    });
   }
 
   @override
-  Future<void> buildTypesForMethod(method, builder) async {
-    final buffer = StringBuffer();
-    final sink = TreeStringSink(
-      sink: buffer,
-      indent: '',
-    );
-
-    final printer = _Printer(
-      sink: sink,
-    );
-    await printer.writeMethodDeclaration(method);
-    final text = buffer.toString();
-
-    builder.declareType(
-      'x',
-      DeclarationCode.fromString(
-        'const x = r"""$text""";',
-      ),
-    );
+  Future<void> buildTypesForMethod(declaration, builder) async {
+    await _write(builder, (printer) async {
+      await printer.writeMethodDeclaration(declaration);
+    });
   }
 
   @override
   Future<void> buildTypesForMixin(declaration, builder) async {
+    await _write(builder, (printer) async {
+      await printer.writeMixinDeclaration(declaration);
+    });
+  }
+
+  Future<void> _write(
+    TypeBuilder builder,
+    Future<void> Function(_Printer printer) f,
+  ) async {
     final buffer = StringBuffer();
     final sink = TreeStringSink(
       sink: buffer,
@@ -66,8 +45,9 @@
 
     final printer = _Printer(
       sink: sink,
+      introspector: builder,
     );
-    await printer.writeMixinDeclaration(declaration);
+    await f(printer);
     final text = buffer.toString();
 
     builder.declareType(
@@ -83,144 +63,11 @@
   @override
   final TreeStringSink sink;
 
+  @override
+  final TypePhaseIntrospector introspector;
+
   _Printer({
     required this.sink,
+    required this.introspector,
   });
-
-  Future<void> writeClassDeclaration(ClassDeclaration e) async {
-    sink.writelnWithIndent('class ${e.identifier.name}');
-
-    await sink.withIndent(() async {
-      await sink.writeFlags({
-        'hasAbstract': e.hasAbstract,
-        'hasExternal': e.hasExternal,
-      });
-
-      await writeMetadata(e);
-      await _writeTypeParameters(e.typeParameters);
-      if (e.superclass case final superclass?) {
-        await _writeTypeAnnotation('superclass', superclass);
-      }
-      await _writeTypeAnnotations('mixins', e.mixins);
-      await _writeTypeAnnotations('interfaces', e.interfaces);
-    });
-  }
-
-  Future<void> writeMethodDeclaration(MethodDeclaration e) async {
-    sink.writelnWithIndent(e.identifier.name);
-
-    await sink.withIndent(() async {
-      await sink.writeFlags({
-        'hasAbstract': e.hasAbstract,
-        'hasBody': e.hasBody,
-        'hasExternal': e.hasExternal,
-        'isGetter': e.isGetter,
-        'isOperator': e.isOperator,
-        'isSetter': e.isSetter,
-        'isStatic': e.isStatic,
-      });
-
-      await writeMetadata(e);
-      await _writeNamedFormalParameters(e.namedParameters);
-      await _writePositionalFormalParameters(e.positionalParameters);
-      await _writeTypeAnnotation('returnType', e.returnType);
-      await _writeTypeParameters(e.typeParameters);
-    });
-  }
-
-  Future<void> writeMixinDeclaration(MixinDeclaration e) async {
-    sink.writelnWithIndent('mixin ${e.identifier.name}');
-
-    await sink.withIndent(() async {
-      await sink.writeFlags({
-        'hasBase': e.hasBase,
-      });
-
-      await writeMetadata(e);
-      await _writeTypeParameters(e.typeParameters);
-      await _writeTypeAnnotations(
-        'superclassConstraints',
-        e.superclassConstraints,
-      );
-      await _writeTypeAnnotations('interfaces', e.interfaces);
-    });
-  }
-
-  Future<void> _writeFormalParameter(ParameterDeclaration e) async {
-    sink.writelnWithIndent(e.identifier.name);
-    await sink.withIndent(() async {
-      await sink.writeFlags({
-        'isNamed': e.isNamed,
-        'isRequired': e.isRequired,
-      });
-      await writeMetadata(e);
-      await _writeTypeAnnotation('type', e.type);
-    });
-  }
-
-  Future<void> _writeNamedFormalParameters(
-    Iterable<ParameterDeclaration> elements,
-  ) async {
-    await sink.writeElements(
-      'namedParameters',
-      elements,
-      _writeFormalParameter,
-    );
-  }
-
-  Future<void> _writePositionalFormalParameters(
-    Iterable<ParameterDeclaration> elements,
-  ) async {
-    await sink.writeElements(
-      'positionalParameters',
-      elements,
-      _writeFormalParameter,
-    );
-  }
-
-  Future<void> _writeTypeAnnotation(String name, TypeAnnotation? type) async {
-    sink.writeWithIndent('$name: ');
-
-    if (type != null) {
-      sink.writeln(type.asString);
-    } else {
-      sink.writeln('null');
-    }
-  }
-
-  Future<void> _writeTypeAnnotationLine(TypeAnnotation type) async {
-    sink.writelnWithIndent(type.asString);
-  }
-
-  Future<void> _writeTypeAnnotations(
-    String name,
-    Iterable<TypeAnnotation> elements,
-  ) async {
-    await sink.writeElements(
-      name,
-      elements,
-      _writeTypeAnnotationLine,
-    );
-  }
-
-  Future<void> _writeTypeParameter(TypeParameterDeclaration e) async {
-    sink.writelnWithIndent(e.identifier.name);
-
-    await sink.withIndent(() async {
-      await writeMetadata(e);
-      if (e.bound case final bound?) {
-        await _writeTypeAnnotation('bound', bound);
-      }
-    });
-  }
-
-  Future<void> _writeTypeParameters(
-    Iterable<TypeParameterDeclaration> elements,
-  ) async {
-    await sink.writeElements(
-      'typeParameters',
-      elements,
-      _writeTypeParameter,
-    );
-  }
 }
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart
index 1be8b43..782f5a7 100644
--- a/pkg/analyzer/test/src/summary/macro_test.dart
+++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -1464,6 +1464,110 @@
     return code.replaceAll('/*macro*/', 'macro');
   }
 
+  test_element_class_field_flag_hasExternal() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+class A {
+  external int foo;
+}
+''');
+
+    await _assertIntrospectText(r'''
+import 'a.dart';
+
+@IntrospectDeclarationsPhaseMacro(
+  withDetailsFor: {'A'},
+)
+class X extends A {}
+''', r'''
+class X
+  superclass: A
+    class A
+      superclass: Object
+      fields
+        foo
+          flags: hasExternal
+          type: int
+''');
+  }
+
+  test_element_class_field_flag_hasFinal() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+class A {
+  final int foo = 0;
+}
+''');
+
+    await _assertIntrospectText(r'''
+import 'a.dart';
+
+@IntrospectDeclarationsPhaseMacro(
+  withDetailsFor: {'A'},
+)
+class X extends A {}
+''', r'''
+class X
+  superclass: A
+    class A
+      superclass: Object
+      fields
+        foo
+          flags: hasFinal
+          type: int
+''');
+  }
+
+  test_element_class_field_flag_hasLate() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+class A {
+  late int foo;
+}
+''');
+
+    await _assertIntrospectText(r'''
+import 'a.dart';
+
+@IntrospectDeclarationsPhaseMacro(
+  withDetailsFor: {'A'},
+)
+class X extends A {}
+''', r'''
+class X
+  superclass: A
+    class A
+      superclass: Object
+      fields
+        foo
+          flags: hasLate
+          type: int
+''');
+  }
+
+  test_element_class_field_flag_isStatic() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+class A {
+  static int foo = 0;
+}
+''');
+
+    await _assertIntrospectText(r'''
+import 'a.dart';
+
+@IntrospectDeclarationsPhaseMacro(
+  withDetailsFor: {'A'},
+)
+class X extends A {}
+''', r'''
+class X
+  superclass: A
+    class A
+      superclass: Object
+      fields
+        foo
+          flags: isStatic
+          type: int
+''');
+  }
+
   test_element_class_field_metadata_identifier() async {
     newFile('$testPackageLibPath/a.dart', r'''
 const a = 0;
@@ -1530,7 +1634,7 @@
 ''');
   }
 
-  test_element_class_flags_isAbstract() async {
+  test_element_class_flags_hasAbstract() async {
     newFile('$testPackageLibPath/a.dart', r'''
 abstract class A {}
 ''');
@@ -2138,7 +2242,7 @@
 ''');
   }
 
-  test_node_class_field_flags_isExternal() async {
+  test_node_class_field_flags_hasExternal() async {
     await _assertIntrospectText(r'''
 @IntrospectDeclarationsPhaseMacro(
   withDetailsFor: {'X'},
@@ -2158,7 +2262,7 @@
 ''');
   }
 
-  test_node_class_field_flags_isFinal() async {
+  test_node_class_field_flags_hasFinal() async {
     await _assertIntrospectText(r'''
 @IntrospectDeclarationsPhaseMacro(
   withDetailsFor: {'X'},
@@ -2178,7 +2282,7 @@
 ''');
   }
 
-  test_node_class_field_flags_isLate() async {
+  test_node_class_field_flags_hasLate() async {
     await _assertIntrospectText(r'''
 @IntrospectDeclarationsPhaseMacro(
   withDetailsFor: {'X'},
@@ -2290,7 +2394,7 @@
 ''');
   }
 
-  test_node_mixin_field_flags_isFinal() async {
+  test_node_mixin_field_flags_hasFinal() async {
     await _assertIntrospectText(r'''
 @IntrospectDeclarationsPhaseMacro(
   withDetailsFor: {'X'},
@@ -3151,7 +3255,7 @@
     return code.replaceAll('/*macro*/', 'macro');
   }
 
-  test_class_flags_isAbstract() async {
+  test_class_flags_hasAbstract() async {
     await _assertIntrospectText(r'''
 @IntrospectTypesPhaseMacro()
 abstract class A {}