[dart2js_info] Add 'main-only' option to info diff.

New option will only include diffs in the "main" output unit for the provided builds. Only sub-library entities have an output unit. Libraries do not have an output unit because there is no code associated with the library itself and a single library's contents can be spread across multiple output units.

Mutually exclusive with the 'package-only' flag because that one only prints libraries and 'main-only' includes no libraries.

Change-Id: I4d5abe54daa8e09d892dd2e2fc2b840f0705ad86
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/332120
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
diff --git a/pkg/dart2js_info/bin/src/common_command.dart b/pkg/dart2js_info/bin/src/common_command.dart
index 8e2aa48..a96826f 100644
--- a/pkg/dart2js_info/bin/src/common_command.dart
+++ b/pkg/dart2js_info/bin/src/common_command.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:args/command_runner.dart';
-
 import 'package:dart2js_info/info.dart';
 import 'package:dart2js_info/src/common_element.dart';
 import 'package:dart2js_info/src/io.dart';
@@ -21,12 +20,19 @@
 
   CommonCommand() {
     argParser.addFlag('packages-only',
-        defaultsTo: false, help: "Show only packages in common");
+        defaultsTo: false,
+        help: "Show only packages in common. "
+            "Cannot be used with `main-only`.");
     argParser.addFlag('order-by-size',
         defaultsTo: false,
         help: "Show output ordered by size in bytes (decreasing). "
             "If there are size discrepancies, orders by the first "
             "dump-info file's reported size.");
+    argParser.addFlag('main-only',
+        defaultsTo: false,
+        help: "Only shows output comparison for main output unit. Provides "
+            "results by class and member rather than by library. "
+            "Cannot be used with `packages-only`.");
   }
 
   @override
@@ -40,10 +46,16 @@
 
     var oldInfo = await infoFromFile(args[0]);
     var newInfo = await infoFromFile(args[1]);
-    var packagesOnly = argRes['packages-only'];
-    var orderBySize = argRes['order-by-size'];
+    bool packagesOnly = argRes['packages-only'];
+    bool orderBySize = argRes['order-by-size'];
+    bool mainOnly = argRes['main-only'];
+    if (packagesOnly && mainOnly) {
+      throw ArgumentError(
+          'Only one of `main-only` and `packages-only` can be provided.');
+    }
 
-    var commonElements = findCommonalities(oldInfo, newInfo);
+    var commonElements =
+        findCommonalities(oldInfo, newInfo, mainOnly: mainOnly);
 
     if (packagesOnly) {
       reportPackages(commonElements, orderBySize: orderBySize);
diff --git a/pkg/dart2js_info/bin/src/deferred_library_check.dart b/pkg/dart2js_info/bin/src/deferred_library_check.dart
index ce3da3c..9bbb3ab 100644
--- a/pkg/dart2js_info/bin/src/deferred_library_check.dart
+++ b/pkg/dart2js_info/bin/src/deferred_library_check.dart
@@ -45,7 +45,7 @@
 
 import 'usage_exception.dart';
 
-/// A command that computes the diff between two info files.
+/// A command that verifies that deferred libraries conform to a given spec.
 class DeferredLibraryCheck extends Command<void> with PrintUsageException {
   @override
   final String name = "deferred_check";
diff --git a/pkg/dart2js_info/lib/src/common_element.dart b/pkg/dart2js_info/lib/src/common_element.dart
index 6e7f635..1d6eac0 100644
--- a/pkg/dart2js_info/lib/src/common_element.dart
+++ b/pkg/dart2js_info/lib/src/common_element.dart
@@ -10,8 +10,9 @@
   String get name => longName(oldInfo, useLibraryUri: true);
 }
 
-List<CommonElement> findCommonalities(AllInfo oldInfo, AllInfo newInfo) {
-  var finder = _InfoCommonElementFinder(oldInfo, newInfo);
+List<CommonElement> findCommonalities(AllInfo oldInfo, AllInfo newInfo,
+    {bool mainOnly = false}) {
+  var finder = _InfoCommonElementFinder(oldInfo, newInfo, mainOnly: mainOnly);
   finder.run();
   return finder.commonElements;
 }
@@ -19,12 +20,13 @@
 class _InfoCommonElementFinder extends InfoVisitor<void> {
   final AllInfo _old;
   final AllInfo _new;
+  final bool mainOnly;
 
   late BasicInfo _other;
 
   List<CommonElement> commonElements = <CommonElement>[];
 
-  _InfoCommonElementFinder(this._old, this._new);
+  _InfoCommonElementFinder(this._old, this._new, {required this.mainOnly});
 
   void run() {
     _commonList(_old.libraries, _new.libraries);
@@ -53,7 +55,7 @@
   @override
   void visitLibrary(LibraryInfo info) {
     var other = _other as LibraryInfo;
-    commonElements.add(CommonElement(info, other));
+    _addElement(info, other);
     _commonList(info.topLevelVariables, other.topLevelVariables);
     _commonList(info.topLevelFunctions, other.topLevelFunctions);
     _commonList(info.classes, other.classes);
@@ -62,7 +64,7 @@
   @override
   void visitClass(ClassInfo info) {
     var other = _other as ClassInfo;
-    commonElements.add(CommonElement(info, other));
+    _addElement(info, other);
     _commonList(info.fields, other.fields);
     _commonList(info.functions, other.functions);
   }
@@ -70,34 +72,40 @@
   @override
   void visitClassType(ClassTypeInfo info) {
     var other = _other as ClassInfo;
-    commonElements.add(CommonElement(info, other));
+    _addElement(info, other);
   }
 
   @override
   void visitClosure(ClosureInfo info) {
     var other = _other as ClosureInfo;
-    commonElements.add(CommonElement(info, other));
+    _addElement(info, other);
     _commonList([info.function], [other.function]);
   }
 
   @override
   void visitField(FieldInfo info) {
     var other = _other as FieldInfo;
-    commonElements.add(CommonElement(info, other));
+    _addElement(info, other);
     _commonList(info.closures, other.closures);
   }
 
   @override
   void visitFunction(FunctionInfo info) {
     var other = _other as FunctionInfo;
-    commonElements.add(CommonElement(info, other));
+    _addElement(info, other);
     _commonList(info.closures, other.closures);
   }
 
   @override
   void visitTypedef(TypedefInfo info) {
     var other = _other as ClassInfo;
-    commonElements.add(CommonElement(info, other));
+    _addElement(info, other);
+  }
+
+  void _addElement(BasicInfo info, BasicInfo other) {
+    if (!mainOnly || (info.outputUnit?.name) == 'main') {
+      commonElements.add(CommonElement(info, other));
+    }
   }
 
   void _commonList(List<BasicInfo> oldInfos, List<BasicInfo> newInfos) {