[cfe] Replace ClassHierarchyNode.directInterfaceBuilders with directInterfaceNodes

This refactors ClassHierarchyNode to contain the [ClassHierarchNode]s of
its direct interfaces instead of the [TypeBuilder]s. This makes the
[ClassHierarchyNode] interface more self-contained and prepares for use
in macro introspection.

Change-Id: I4014ea180321060aceaf5cc88d5d2182a3c34d54
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/236881
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/hierarchy/hierarchy_node.dart b/pkg/front_end/lib/src/fasta/kernel/hierarchy/hierarchy_node.dart
index 30bd373..2bd09ac 100644
--- a/pkg/front_end/lib/src/fasta/kernel/hierarchy/hierarchy_node.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/hierarchy/hierarchy_node.dart
@@ -51,7 +51,7 @@
     List<Supertype> interfacesList;
     int maxInheritancePath;
 
-    List<TypeBuilder>? directInterfaceBuilders;
+    List<ClassHierarchyNode>? interfaceNodes;
 
     if (classBuilder.isMixinApplication) {
       inferMixinApplication();
@@ -85,7 +85,8 @@
         }
       }
 
-      directInterfaceBuilders = ignoreFunction(classBuilder.interfaceBuilders);
+      List<TypeBuilder>? directInterfaceBuilders =
+          ignoreFunction(classBuilder.interfaceBuilders);
       if (classBuilder.isMixinApplication) {
         if (directInterfaceBuilders == null) {
           directInterfaceBuilders = <TypeBuilder>[
@@ -99,15 +100,13 @@
       }
 
       List<Supertype> superclassInterfaces = supernode.interfaces;
-      // ignore: unnecessary_null_comparison
-      if (superclassInterfaces != null) {
+      if (superclassInterfaces.isNotEmpty) {
         superclassInterfaces = substSupertypes(supertype, superclassInterfaces);
       }
 
       if (directInterfaceBuilders != null) {
         Map<Class, Supertype> interfaces = {};
-        // ignore: unnecessary_null_comparison
-        if (superclassInterfaces != null) {
+        if (superclassInterfaces.isNotEmpty) {
           for (int i = 0; i < superclassInterfaces.length; i++) {
             addInterface(interfaces, superclasses, superclassInterfaces[i]);
           }
@@ -121,31 +120,28 @@
             addInterface(interfaces, superclasses, directInterface);
             ClassHierarchyNode interfaceNode =
                 hierarchy.getNodeFromClass(directInterface.classNode);
-            // ignore: unnecessary_null_comparison
-            if (interfaceNode != null) {
-              if (maxInheritancePath < interfaceNode.maxInheritancePath + 1) {
-                maxInheritancePath = interfaceNode.maxInheritancePath + 1;
-              }
+            (interfaceNodes ??= []).add(interfaceNode);
 
+            if (maxInheritancePath < interfaceNode.maxInheritancePath + 1) {
+              maxInheritancePath = interfaceNode.maxInheritancePath + 1;
+            }
+
+            List<Supertype> types =
+                substSupertypes(directInterface, interfaceNode.superclasses);
+            for (int i = 0; i < types.length; i++) {
+              addInterface(interfaces, superclasses, types[i]);
+            }
+            if (interfaceNode.interfaces.isNotEmpty) {
               List<Supertype> types =
-                  substSupertypes(directInterface, interfaceNode.superclasses);
+                  substSupertypes(directInterface, interfaceNode.interfaces);
               for (int i = 0; i < types.length; i++) {
                 addInterface(interfaces, superclasses, types[i]);
               }
-              // ignore: unnecessary_null_comparison
-              if (interfaceNode.interfaces != null) {
-                List<Supertype> types =
-                    substSupertypes(directInterface, interfaceNode.interfaces);
-                for (int i = 0; i < types.length; i++) {
-                  addInterface(interfaces, superclasses, types[i]);
-                }
-              }
             }
           }
         }
         interfacesList = interfaces.values.toList();
-        // ignore: unnecessary_null_comparison
-      } else if (superclassInterfaces != null &&
+      } else if (superclassInterfaces.isNotEmpty &&
           !classBuilder.library.isNonNullableByDefault &&
           supernode.classBuilder.library.isNonNullableByDefault) {
         Map<Class, Supertype> interfaces = {};
@@ -168,13 +164,8 @@
       }
     }
 
-    return new ClassHierarchyNode(
-        classBuilder,
-        supernode,
-        directInterfaceBuilders,
-        superclasses,
-        interfacesList,
-        maxInheritancePath);
+    return new ClassHierarchyNode(classBuilder, supernode, interfaceNodes,
+        superclasses, interfacesList, maxInheritancePath);
   }
 
   Supertype recordSupertype(Supertype supertype) {
@@ -329,9 +320,13 @@
   /// The class corresponding to this hierarchy node.
   final ClassBuilder classBuilder;
 
-  final ClassHierarchyNode? supernode;
+  /// The [ClassHierarchyNode] for the direct super class of [classBuilder], or
+  /// `null` if this is `Object`.
+  final ClassHierarchyNode? directSuperClassNode;
 
-  final List<TypeBuilder>? directInterfaceBuilders;
+  /// The [ClassHierarchyNode]s for the direct super interfaces of
+  /// [classBuilder].
+  final List<ClassHierarchyNode>? directInterfaceNodes;
 
   /// All superclasses of [classBuilder] excluding itself. The classes are
   /// sorted by depth from the root (Object) in ascending order.
@@ -348,8 +343,8 @@
 
   ClassHierarchyNode(
       this.classBuilder,
-      this.supernode,
-      this.directInterfaceBuilders,
+      this.directSuperClassNode,
+      this.directInterfaceNodes,
       this.superclasses,
       this.interfaces,
       this.maxInheritancePath);
@@ -390,8 +385,7 @@
       sb.writeln();
       depth++;
     }
-    // ignore: unnecessary_null_comparison
-    if (interfaces != null) {
+    if (interfaces.isNotEmpty) {
       sb.write("  interfaces:");
       bool first = true;
       for (Supertype i in interfaces) {
diff --git a/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart b/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart
index e4a2839..e4faf56 100644
--- a/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart
@@ -566,12 +566,12 @@
   }
 
   ClassMembersNode build() {
-    ClassMembersNode? supernode = _hierarchyNode.supernode != null
-        ? _membersBuilder
-            .getNodeFromClassBuilder(_hierarchyNode.supernode!.classBuilder)
+    ClassMembersNode? supernode = _hierarchyNode.directSuperClassNode != null
+        ? _membersBuilder.getNodeFromClassBuilder(
+            _hierarchyNode.directSuperClassNode!.classBuilder)
         : null;
-    List<TypeBuilder>? directInterfaceBuilders =
-        _hierarchyNode.directInterfaceBuilders;
+    List<ClassHierarchyNode>? interfaceNodes =
+        _hierarchyNode.directInterfaceNodes;
 
     /// Set to `true` if the class needs interfaces, that is, if it has any
     /// members where the interface member is different from its corresponding
@@ -770,18 +770,16 @@
         implement(supernode.interfaceSetterMap ?? supernode.classSetterMap);
       }
 
-      if (directInterfaceBuilders != null) {
-        for (int i = 0; i < directInterfaceBuilders.length; i++) {
+      if (interfaceNodes != null) {
+        for (int i = 0; i < interfaceNodes.length; i++) {
           ClassMembersNode? interfaceNode = _membersBuilder
-              .getNodeFromTypeBuilder(directInterfaceBuilders[i]);
-          if (interfaceNode != null) {
-            hasInterfaces = true;
+              .getNodeFromClassBuilder(interfaceNodes[i].classBuilder);
+          hasInterfaces = true;
 
-            implement(interfaceNode.interfaceMemberMap ??
-                interfaceNode.classMemberMap);
-            implement(interfaceNode.interfaceSetterMap ??
-                interfaceNode.classSetterMap);
-          }
+          implement(
+              interfaceNode.interfaceMemberMap ?? interfaceNode.classMemberMap);
+          implement(
+              interfaceNode.interfaceSetterMap ?? interfaceNode.classSetterMap);
         }
       }
     }
diff --git a/pkg/front_end/lib/src/fasta/kernel/macro/macro.dart b/pkg/front_end/lib/src/fasta/kernel/macro/macro.dart
index 2b027d9..8779439 100644
--- a/pkg/front_end/lib/src/fasta/kernel/macro/macro.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/macro/macro.dart
@@ -920,10 +920,10 @@
     ClassBuilder classBuilder = macroApplications._getClassBuilder(clazz);
     ClassHierarchyNode node =
         classHierarchy.getNodeFromClassBuilder(classBuilder);
-    ClassHierarchyNode? superNode = node.supernode;
+    ClassHierarchyNode? superNode = node.directSuperClassNode;
     while (superNode != null &&
         superNode.classBuilder.isAnonymousMixinApplication) {
-      superNode = superNode.supernode;
+      superNode = superNode.directSuperClassNode;
     }
     if (superNode != null) {
       return new Future.value(
diff --git a/pkg/front_end/test/fasta/types/dill_hierarchy_test.dart b/pkg/front_end/test/fasta/types/dill_hierarchy_test.dart
index 55076d6..a1c2094 100644
--- a/pkg/front_end/test/fasta/types/dill_hierarchy_test.dart
+++ b/pkg/front_end/test/fasta/types/dill_hierarchy_test.dart
@@ -37,12 +37,10 @@
 const String expectedHierarchy = """
 Object:
   superclasses:
-  interfaces:
 
 A:
   superclasses:
     Object
-  interfaces:
 
 B:
   Longest path to Object: 2