Compare names of class members, and refactor analyzer visitor.

The analyzer visitor now follows the suggestion of
https://dart-review.googlesource.com/c/sdk/+/72541/1/pkg/analyzer_fe_comparison/lib/src/analyzer.dart#51

Change-Id: I093f994b36d835ae6db0dc6a24ed3357dc53c8b7
Reviewed-on: https://dart-review.googlesource.com/72550
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer_fe_comparison/lib/src/analyzer.dart b/pkg/analyzer_fe_comparison/lib/src/analyzer.dart
index e7161ca..b0aff01 100644
--- a/pkg/analyzer_fe_comparison/lib/src/analyzer.dart
+++ b/pkg/analyzer_fe_comparison/lib/src/analyzer.dart
@@ -13,7 +13,6 @@
 /// Analyzes the project located at [libPath] using the analyzer, and returns a
 /// [ComparisonNode] representing it.
 Future<ComparisonNode> driveAnalyzer(String libPath) async {
-  var visitor = _AnalyzerVisitor();
   var contextCollection = AnalysisContextCollection(includedPaths: [libPath]);
   var contexts = contextCollection.contexts;
   if (contexts.length != 1) {
@@ -36,9 +35,7 @@
       for (var compilationUnit in libraryElement.units) {
         var unitResult =
             await session.getResolvedAst(compilationUnit.source.fullName);
-        for (var astNode in unitResult.unit.declarations) {
-          childNodes.addAll(astNode.accept(visitor));
-        }
+        _AnalyzerVisitor(childNodes)._visitList(unitResult.unit.declarations);
       }
       libraryNodes.add(ComparisonNode.sorted(importUri.toString(), childNodes));
     }
@@ -48,19 +45,43 @@
 
 /// Visitor for serializing the contents of an analyzer AST into
 /// ComparisonNodes.
-class _AnalyzerVisitor extends UnifyingAstVisitor<Iterable<ComparisonNode>> {
+///
+/// Results are accumulated into [_resultNodes].
+class _AnalyzerVisitor extends UnifyingAstVisitor<void> {
+  final List<ComparisonNode> _resultNodes;
+
+  _AnalyzerVisitor(this._resultNodes);
+
   @override
-  List<ComparisonNode> visitClassDeclaration(ClassDeclaration node) {
-    return [ComparisonNode('Class ${node.name.name}')];
+  void visitClassDeclaration(ClassDeclaration node) {
+    var children = <ComparisonNode>[];
+    _AnalyzerVisitor(children)._visitList(node.members);
+    _resultNodes
+        .add(ComparisonNode.sorted('Class ${node.name.name}', children));
   }
 
   @override
-  List<ComparisonNode> visitEnumDeclaration(EnumDeclaration node) {
-    return [ComparisonNode('Enum ${node.name.name}')];
+  void visitConstructorDeclaration(ConstructorDeclaration node) {
+    _resultNodes
+        .add(ComparisonNode('Constructor ${node.name?.name ?? '(unnamed)'}'));
   }
 
   @override
-  List<ComparisonNode> visitFunctionDeclaration(FunctionDeclaration node) {
+  void visitEnumDeclaration(EnumDeclaration node) {
+    var children = <ComparisonNode>[];
+    for (var enumValue in node.constants) {
+      children.add(ComparisonNode('EnumValue ${enumValue.name.name}'));
+    }
+    _resultNodes.add(ComparisonNode.sorted('Enum ${node.name.name}', children));
+  }
+
+  @override
+  void visitFieldDeclaration(FieldDeclaration node) {
+    node.fields.accept(this);
+  }
+
+  @override
+  void visitFunctionDeclaration(FunctionDeclaration node) {
     String kind;
     if (node.isGetter) {
       kind = 'Getter';
@@ -70,12 +91,27 @@
       // Kernel calls top level functions "methods".
       kind = 'Method';
     }
-    return [ComparisonNode('$kind ${node.name.name}')];
+    _resultNodes.add(ComparisonNode('$kind ${node.name.name}'));
   }
 
   @override
-  List<ComparisonNode> visitFunctionTypeAlias(FunctionTypeAlias node) {
-    return [ComparisonNode('Typedef ${node.name.name}')];
+  void visitFunctionTypeAlias(FunctionTypeAlias node) {
+    _resultNodes.add(ComparisonNode('Typedef ${node.name.name}'));
+  }
+
+  @override
+  void visitMethodDeclaration(MethodDeclaration node) {
+    String kind;
+    if (node.isGetter) {
+      kind = 'Getter';
+    } else if (node.isSetter) {
+      kind = 'Setter';
+    } else if (node.isOperator) {
+      kind = 'Operator';
+    } else {
+      kind = 'Method';
+    }
+    _resultNodes.add(ComparisonNode('$kind ${node.name.name}'));
   }
 
   @override
@@ -84,11 +120,23 @@
   }
 
   @override
-  Iterable<ComparisonNode> visitTopLevelVariableDeclaration(
-      TopLevelVariableDeclaration node) sync* {
-    for (var variableDeclaration in node.variables.variables) {
-      // Kernel calls top level variable declarations "fields".
-      yield ComparisonNode('Field ${variableDeclaration.name.name}');
+  void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    node.variables.accept(this);
+  }
+
+  @override
+  void visitVariableDeclarationList(VariableDeclarationList node) {
+    for (var variableDeclaration in node.variables) {
+      // Kernel calls both fields and top level variable declarations "fields".
+      _resultNodes
+          .add(ComparisonNode('Field ${variableDeclaration.name.name}'));
+    }
+  }
+
+  /// Visits all the nodes in [nodes].
+  void _visitList(List<AstNode> nodes) {
+    for (var astNode in nodes) {
+      astNode.accept(this);
     }
   }
 }
diff --git a/pkg/analyzer_fe_comparison/lib/src/kernel.dart b/pkg/analyzer_fe_comparison/lib/src/kernel.dart
index 138d05b..638447c 100644
--- a/pkg/analyzer_fe_comparison/lib/src/kernel.dart
+++ b/pkg/analyzer_fe_comparison/lib/src/kernel.dart
@@ -48,8 +48,21 @@
   ComparisonNode visitClass(Class class_) {
     if (class_.isAnonymousMixin) return null;
     var kind = class_.isEnum ? 'Enum' : 'Class';
-    // TODO(paulberry): handle fields from Class
-    return ComparisonNode('$kind ${class_.name}');
+    var children = <ComparisonNode>[];
+    if (class_.isEnum) {
+      for (var field in class_.fields) {
+        if (!field.isStatic) continue;
+        if (field.name.name == 'values') continue;
+        // TODO(paulberry): handle index
+        children.add(ComparisonNode('EnumValue ${field.name.name}'));
+      }
+    } else {
+      _visitList(class_.fields, children);
+      _visitList(class_.constructors, children);
+      _visitList(class_.procedures, children);
+    }
+    // TODO(paulberry): handle more fields from Class
+    return ComparisonNode.sorted('$kind ${class_.name}', children);
   }
 
   @override
@@ -60,7 +73,19 @@
   }
 
   @override
+  ComparisonNode visitConstructor(Constructor constructor) {
+    if (constructor.isSynthetic) return null;
+    var name = constructor.name.name;
+    if (name.isEmpty) {
+      name = '(unnamed)';
+    }
+    // TODO(paulberry): handle fields from Constructor
+    return ComparisonNode('Constructor $name');
+  }
+
+  @override
   ComparisonNode visitField(Field field) {
+    if (field.name.name == '_redirecting#') return null;
     // TODO(paulberry): handle fields from Field
     return ComparisonNode('Field ${field.name.name}');
   }
@@ -82,9 +107,18 @@
 
   @override
   ComparisonNode visitProcedure(Procedure procedure) {
-    var kind = procedure.kind.toString().replaceAll('ProcedureKind.', '');
+    if (procedure.isForwardingStub) return null;
+    // TODO(paulberry): add an annotation to the ComparisonNode when the
+    // procedure is a factory.
+    var kind = procedure.isFactory
+        ? 'Constructor'
+        : procedure.kind.toString().replaceAll('ProcedureKind.', '');
+    var name = procedure.name.name;
+    if (name.isEmpty) {
+      name = '(unnamed)';
+    }
     // TODO(paulberry): handle fields from Procedure
-    return ComparisonNode('$kind ${procedure.name.name}');
+    return ComparisonNode('$kind $name');
   }
 
   @override