[cfe] Compute macro declaration data early in outline building

This CL moves the computation of macro declaration data and the
required compilation sequence for macro compilation early in the
outline building phase by performing computation fully in terms
of builders.

Change-Id: I61a566f0aed661767a47579bca4d5931e4a4df9f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/220771
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
diff --git a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
index 0bfffd5..930f1b0 100644
--- a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
@@ -400,7 +400,7 @@
         // from ClassHierarchyBuilder.
         TypeDeclarationBuilder? unaliasedDeclaration = this.declaration;
         // The following code assumes that the declaration is a TypeAliasBuilder
-        // that through a chain of other TypeAliasBuilders (possibly, the chian
+        // that through a chain of other TypeAliasBuilders (possibly, the chain
         // length is 0) references a ClassBuilder of the Null class.  Otherwise,
         // it won't produce the NullType on the output.
         while (unaliasedDeclaration is TypeAliasBuilder) {
@@ -421,7 +421,7 @@
         // class from ClassHierarchyBuilder.
         TypeDeclarationBuilder? unaliasedDeclaration = this.declaration;
         // The following code assumes that the declaration is a TypeAliasBuilder
-        // that through a chain of other TypeAliasBuilders (possibly, the chian
+        // that through a chain of other TypeAliasBuilders (possibly, the chain
         // length is 0) references a ClassBuilder of the FutureOr class.
         // Otherwise, it won't produce the FutureOrType on the output.
         while (unaliasedDeclaration is TypeAliasBuilder) {
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index 40a1882..2950121 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -402,7 +402,6 @@
     if (loader.first == null) return null;
     return withCrashReporting<Component?>(() async {
       await loader.buildOutlines();
-      loader.createTypeInferenceEngine();
       loader.coreLibrary.becomeCoreLibrary();
       loader.resolveParts();
       loader.computeLibraryScopes();
@@ -411,32 +410,33 @@
       loader.computeVariances();
       loader.computeDefaultTypes(
           dynamicType, nullType, bottomType, objectClassBuilder);
-      List<SourceClassBuilder> myClasses =
+      List<SourceClassBuilder> sourceClassBuilders =
           loader.checkSemantics(objectClassBuilder);
+      loader.computeMacroDeclarations(sourceClassBuilders);
       loader.finishTypeVariables(objectClassBuilder, dynamicType);
+      loader.createTypeInferenceEngine();
       loader.buildComponent();
       installDefaultSupertypes();
-      installSyntheticConstructors(myClasses);
+      installSyntheticConstructors(sourceClassBuilders);
       loader.resolveConstructors();
       component =
           link(new List<Library>.from(loader.libraries), nameRoot: nameRoot);
       computeCoreTypes();
-      loader.buildClassHierarchy(myClasses, objectClassBuilder);
+      loader.buildClassHierarchy(sourceClassBuilders, objectClassBuilder);
       loader.computeHierarchy();
-      loader.computeMacroDeclarations(myClasses);
       loader.computeShowHideElements();
       loader.installTypedefTearOffs();
-      loader.performTopLevelInference(myClasses);
-      loader.checkSupertypes(myClasses);
-      loader.checkOverrides(myClasses);
-      loader.checkAbstractMembers(myClasses);
-      loader.addNoSuchMethodForwarders(myClasses);
-      loader.checkMixins(myClasses);
+      loader.performTopLevelInference(sourceClassBuilders);
+      loader.checkSupertypes(sourceClassBuilders);
+      loader.checkOverrides(sourceClassBuilders);
+      loader.checkAbstractMembers(sourceClassBuilders);
+      loader.addNoSuchMethodForwarders(sourceClassBuilders);
+      loader.checkMixins(sourceClassBuilders);
       loader.buildOutlineExpressions(
           loader.coreTypes, synthesizedFunctionNodes);
       loader.computeMacroApplications();
       loader.checkTypes();
-      loader.checkRedirectingFactories(myClasses);
+      loader.checkRedirectingFactories(sourceClassBuilders);
       loader.checkMainMethods();
       installAllComponentProblems(loader.allComponentProblems);
       loader.allComponentProblems.clear();
diff --git a/pkg/front_end/lib/src/fasta/kernel/macro.dart b/pkg/front_end/lib/src/fasta/kernel/macro.dart
index 33d4691..afa9dca 100644
--- a/pkg/front_end/lib/src/fasta/kernel/macro.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/macro.dart
@@ -10,9 +10,8 @@
 const String macroClassName = 'Macro';
 
 class MacroDeclarationData {
-  Class? macroClass;
-  Map<Library, List<Class>> macroDeclarations = {};
-  Set<Class> macroClasses = {};
+  bool macrosAreAvailable = false;
+  Map<Uri, List<String>> macroDeclarations = {};
   List<List<Uri>>? compilationSequence;
 }
 
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 00108f9..aa989c5 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -35,6 +35,7 @@
 import '../../base/common.dart';
 import '../../base/instrumentation.dart' show Instrumentation;
 import '../../base/nnbd_mode.dart';
+import '../dill/dill_class_builder.dart';
 import '../dill/dill_library_builder.dart';
 import '../builder_graph.dart';
 import '../builder/builder.dart';
@@ -202,7 +203,7 @@
 
   Uri? currentUriForCrashReporting;
 
-  Class? _macroClass;
+  ClassBuilder? _macroClassBuilder;
 
   SourceLoader(this.fileSystem, this.includeComments, this.target)
       : dataForTesting =
@@ -1173,13 +1174,12 @@
     ticker.logMs("Resolved $typeCount types");
   }
 
-  void computeMacroDeclarations(List<SourceClassBuilder> classes) {
+  void computeMacroDeclarations(List<SourceClassBuilder> sourceClassBuilders) {
     if (!enableMacros) return;
 
-    bool isDillLibrary(Uri uri) => _builders[uri]?.loader != this;
-
     LibraryBuilder? macroLibraryBuilder = lookupLibraryBuilder(macroLibraryUri);
     if (macroLibraryBuilder == null) return;
+
     Builder? macroClassBuilder =
         macroLibraryBuilder.lookupLocalMember(macroClassName);
     if (macroClassBuilder is! ClassBuilder) {
@@ -1188,23 +1188,56 @@
       return;
     }
 
-    Class macroClass = _macroClass = macroClassBuilder.cls;
+    _macroClassBuilder = macroClassBuilder;
     if (retainDataForTesting) {
-      dataForTesting!.macroDeclarationData.macroClass = macroClass;
+      dataForTesting!.macroDeclarationData.macrosAreAvailable = true;
     }
-    Set<Uri> macroLibraries = {};
-    for (SourceClassBuilder classBuilder in classes) {
-      Class cls = classBuilder.cls;
-      if (hierarchy.isSubtypeOf(cls, macroClass)) {
-        macroLibraries.add(cls.enclosingLibrary.importUri);
+
+    Set<ClassBuilder> macroClasses = {macroClassBuilder};
+    Set<Uri> macroLibraries = {macroLibraryBuilder.importUri};
+
+    bool isMacroClass(TypeDeclarationBuilder? typeDeclarationBuilder) {
+      if (typeDeclarationBuilder == null) return false;
+      while (typeDeclarationBuilder is TypeAliasBuilder) {
+        typeDeclarationBuilder =
+            typeDeclarationBuilder.unaliasDeclaration(null);
+      }
+      if (typeDeclarationBuilder is ClassBuilder) {
+        if (macroClasses.contains(typeDeclarationBuilder)) return true;
+        if (typeDeclarationBuilder is DillClassBuilder) {
+          // TODO(johnniwinther): Recognize macro classes from dill.
+        }
+      }
+      return false;
+    }
+
+    for (SourceClassBuilder sourceClassBuilder in sourceClassBuilders) {
+      bool isMacro =
+          isMacroClass(sourceClassBuilder.supertypeBuilder?.declaration);
+      if (!isMacro && sourceClassBuilder.interfaceBuilders != null) {
+        for (TypeBuilder interfaceBuilder
+            in sourceClassBuilder.interfaceBuilders!) {
+          if (isMacroClass(interfaceBuilder.declaration)) {
+            isMacro = true;
+            break;
+          }
+        }
+      }
+      isMacro = isMacro ||
+          isMacroClass(sourceClassBuilder.mixedInTypeBuilder?.declaration);
+      if (isMacro) {
+        macroClasses.add(sourceClassBuilder);
+        macroLibraries.add(sourceClassBuilder.library.importUri);
         if (retainDataForTesting) {
-          (dataForTesting!.macroDeclarationData
-                  .macroDeclarations[cls.enclosingLibrary] ??= [])
-              .add(cls);
+          (dataForTesting!.macroDeclarationData.macroDeclarations[
+                  sourceClassBuilder.library.importUri] ??= [])
+              .add(sourceClassBuilder.name);
         }
       }
     }
 
+    bool isDillLibrary(Uri uri) => _builders[uri]?.loader != this;
+
     List<List<Uri>> computeCompilationSequence(Graph<Uri> libraryGraph,
         {required bool Function(Uri) filter}) {
       List<List<Uri>> stronglyConnectedComponents =
@@ -1248,28 +1281,13 @@
   }
 
   void computeMacroApplications() {
-    if (!enableMacros || _macroClass == null) return;
-    Class macroClass = _macroClass!;
-
-    Set<Class> macroClasses = {};
-    for (Library library in hierarchy.knownLibraries) {
-      for (Class cls in library.classes) {
-        // TODO(johnniwinther): Avoid calling `isSubtypeOf` for all classes. We
-        // should only check classes used in annotations to avoid marking too
-        // many classes as used.
-        if (hierarchy.isSubtypeOf(cls, macroClass)) {
-          macroClasses.add(cls);
-          if (retainDataForTesting) {
-            dataForTesting!.macroDeclarationData.macroClasses.add(cls);
-          }
-        }
-      }
-    }
+    if (!enableMacros || _macroClassBuilder == null) return;
+    Class macroClass = _macroClassBuilder!.cls;
 
     Class? computeApplication(Expression expression) {
       if (expression is ConstructorInvocation) {
         Class cls = expression.target.enclosingClass;
-        if (macroClasses.contains(cls)) {
+        if (hierarchy.isSubtypeOf(cls, macroClass)) {
           return cls;
         }
       }
diff --git a/pkg/front_end/test/macros/data/tests/macro_declarations.dart b/pkg/front_end/test/macros/data/tests/macro_declarations.dart
new file mode 100644
index 0000000..872dad84
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/macro_declarations.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2021, 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.
+
+/*library: 
+ compilationSequence=[
+  package:macro_builder/src/macro.dart,
+  main.dart|package:macro_builder/macro_builder.dart],
+ declaredMacros=[
+  Extends,
+  ExtendsAlias,
+  Implements,
+  ImplementsAlias,
+  Mixin,
+  MixinAlias,
+  _Mixin&Object&Macro,
+  _MixinAlias&Object&Alias],
+ macrosAreAvailable
+*/
+
+import 'package:macro_builder/macro_builder.dart';
+
+class Extends extends Macro {}
+
+class Implements implements Macro {}
+
+class Mixin with Macro {}
+
+typedef Alias = Macro;
+
+class ExtendsAlias extends Alias {}
+
+class ImplementsAlias implements Alias {}
+
+class MixinAlias with Alias {}
+
+void main() {}
diff --git a/pkg/front_end/test/macros/macro_test.dart b/pkg/front_end/test/macros/macro_test.dart
index d0bc75f..82fe62d 100644
--- a/pkg/front_end/test/macros/macro_test.dart
+++ b/pkg/front_end/test/macros/macro_test.dart
@@ -160,7 +160,7 @@
   @override
   Features computeLibraryValue(Id id, Library node) {
     Features features = new Features();
-    if (macroDeclarationData.macroClass != null) {
+    if (macroDeclarationData.macrosAreAvailable) {
       features.add(Tags.macrosAreAvailable);
     }
     if (node == compilerResult.component!.mainMethod!.enclosingLibrary) {
@@ -172,10 +172,11 @@
         }
       }
     }
-    List<Class>? macroClasses = macroDeclarationData.macroDeclarations[node];
+    List<String>? macroClasses =
+        macroDeclarationData.macroDeclarations[node.importUri];
     if (macroClasses != null) {
-      for (Class cls in macroClasses) {
-        features.addElement(Tags.declaredMacros, cls.name);
+      for (String clsName in macroClasses) {
+        features.addElement(Tags.declaredMacros, clsName);
       }
     }
     if (getLibraryMacroApplicationData(node) != null) {
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index cad9efd..4a5faff 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -436,7 +436,6 @@
 checking
 checkpoints
 checks
-chian
 child
 children
 choice