Version 2.12.0-41.0.dev

Merge commit '8abc99d07a7b7fa965bd6a441d718fd924e886e3' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 24ce857..7e58d34 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -74,12 +74,6 @@
       "languageVersion": "2.8"
     },
     {
-      "name": "analysis_tool",
-      "rootUri": "../pkg/analysis_tool",
-      "packageUri": "lib/",
-      "languageVersion": "2.1"
-    },
-    {
       "name": "analyzer",
       "rootUri": "../pkg/analyzer",
       "packageUri": "lib/",
@@ -98,6 +92,12 @@
       "languageVersion": "2.3"
     },
     {
+      "name": "analyzer_utilities",
+      "rootUri": "../pkg/analyzer_utilities",
+      "packageUri": "lib/",
+      "languageVersion": "2.1"
+    },
+    {
       "name": "args",
       "rootUri": "../third_party/pkg/args",
       "packageUri": "lib/",
diff --git a/.packages b/.packages
index 22dab41..f264745 100644
--- a/.packages
+++ b/.packages
@@ -10,10 +10,10 @@
 _js_interop_checks:pkg/_js_interop_checks/lib
 analysis_server:pkg/analysis_server/lib
 analysis_server_client:pkg/analysis_server_client/lib
-analysis_tool:pkg/analysis_tool/lib
 analyzer:pkg/analyzer/lib
 analyzer_cli:pkg/analyzer_cli/lib
 analyzer_plugin:pkg/analyzer_plugin/lib
+analyzer_utilities:pkg/analyzer_utilities/lib
 args:third_party/pkg/args/lib
 async:third_party/pkg/async/lib
 async_helper:pkg/async_helper/lib
diff --git a/pkg/analysis_server/pubspec.yaml b/pkg/analysis_server/pubspec.yaml
index da3e6a6..2b64203 100644
--- a/pkg/analysis_server/pubspec.yaml
+++ b/pkg/analysis_server/pubspec.yaml
@@ -30,7 +30,7 @@
   yaml: any
 
 dev_dependencies:
-  analysis_tool: any
+  analyzer_utilities: any
   http: any
   logging: any
   matcher: any
diff --git a/pkg/analysis_server/test/src/utilities/mock_packages.dart b/pkg/analysis_server/test/src/utilities/mock_packages.dart
index 1a34e4a..c60002d 100644
--- a/pkg/analysis_server/test/src/utilities/mock_packages.dart
+++ b/pkg/analysis_server/test/src/utilities/mock_packages.dart
@@ -2,10 +2,10 @@
 // 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.
 
-import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/memory_file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
 
 /// Helper for copying files from "tests/mock_packages" to memory file system.
 class MockPackages {
diff --git a/pkg/analysis_server/test/verify_sorted_test.dart b/pkg/analysis_server/test/verify_sorted_test.dart
index b59ef2b..60fda8d 100644
--- a/pkg/analysis_server/test/verify_sorted_test.dart
+++ b/pkg/analysis_server/test/verify_sorted_test.dart
@@ -5,12 +5,12 @@
 import 'dart:io';
 
 import 'package:analysis_server/src/services/correction/sort_members.dart';
-import 'package:analysis_tool/package_root.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer_utilities/package_root.dart';
 import 'package:meta/meta.dart';
 import 'package:test/test.dart';
 
diff --git a/pkg/analysis_server/test/verify_tests_test.dart b/pkg/analysis_server/test/verify_tests_test.dart
index 73aa656..4b07a76 100644
--- a/pkg/analysis_server/test/verify_tests_test.dart
+++ b/pkg/analysis_server/test/verify_tests_test.dart
@@ -2,10 +2,10 @@
 // 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.
 
-import 'package:analysis_tool/package_root.dart' as package_root;
-import 'package:analysis_tool/verify_tests.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
+import 'package:analyzer_utilities/verify_tests.dart';
 
 void main() {
   var provider = PhysicalResourceProvider.INSTANCE;
diff --git a/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart b/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart
index 74ef351..21230de 100644
--- a/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart
+++ b/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart
@@ -8,8 +8,6 @@
 import 'package:analysis_server/src/protocol_server.dart' show ElementKind;
 import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
 import 'package:analysis_server/src/utilities/flutter.dart';
-import 'package:analysis_tool/package_root.dart' as package_root;
-import 'package:analysis_tool/tools.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/context_root.dart';
 import 'package:analyzer/dart/analysis/results.dart';
@@ -24,6 +22,8 @@
 import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
 import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
+import 'package:analyzer_utilities/tools.dart';
 import 'package:args/args.dart';
 import 'package:meta/meta.dart';
 
diff --git a/pkg/analysis_server/tool/spec/check_all_test.dart b/pkg/analysis_server/tool/spec/check_all_test.dart
index de4be12..3be8ed7 100644
--- a/pkg/analysis_server/tool/spec/check_all_test.dart
+++ b/pkg/analysis_server/tool/spec/check_all_test.dart
@@ -4,7 +4,7 @@
 
 import 'dart:io';
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart';
 
 import 'generate_all.dart';
diff --git a/pkg/analysis_server/tool/spec/codegen_analysis_server.dart b/pkg/analysis_server/tool/spec/codegen_analysis_server.dart
index e72548d..9e19fb6 100644
--- a/pkg/analysis_server/tool/spec/codegen_analysis_server.dart
+++ b/pkg/analysis_server/tool/spec/codegen_analysis_server.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Code generation for the file "AnalysisServer.java".
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 
 import 'api.dart';
 import 'codegen_java.dart';
diff --git a/pkg/analysis_server/tool/spec/codegen_dart_notification_handler.dart b/pkg/analysis_server/tool/spec/codegen_dart_notification_handler.dart
index 7fddd04..65515ef 100644
--- a/pkg/analysis_server/tool/spec/codegen_dart_notification_handler.dart
+++ b/pkg/analysis_server/tool/spec/codegen_dart_notification_handler.dart
@@ -2,7 +2,7 @@
 // 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.
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:html/dom.dart';
 
 import 'api.dart';
diff --git a/pkg/analysis_server/tool/spec/codegen_dart_protocol.dart b/pkg/analysis_server/tool/spec/codegen_dart_protocol.dart
index a9da290..2acef57 100644
--- a/pkg/analysis_server/tool/spec/codegen_dart_protocol.dart
+++ b/pkg/analysis_server/tool/spec/codegen_dart_protocol.dart
@@ -4,7 +4,7 @@
 
 import 'dart:convert';
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:html/dom.dart' as dom;
 import 'package:path/path.dart' as path;
 
diff --git a/pkg/analysis_server/tool/spec/codegen_inttest_methods.dart b/pkg/analysis_server/tool/spec/codegen_inttest_methods.dart
index 09a412b..208e0a0 100644
--- a/pkg/analysis_server/tool/spec/codegen_inttest_methods.dart
+++ b/pkg/analysis_server/tool/spec/codegen_inttest_methods.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Code generation for the file "integration_test_methods.dart".
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart' as path;
 
 import 'api.dart';
diff --git a/pkg/analysis_server/tool/spec/codegen_java.dart b/pkg/analysis_server/tool/spec/codegen_java.dart
index f898732..341e4e1 100644
--- a/pkg/analysis_server/tool/spec/codegen_java.dart
+++ b/pkg/analysis_server/tool/spec/codegen_java.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Tools for Java code generation.
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:html/dom.dart' as dom;
 
 import 'api.dart';
diff --git a/pkg/analysis_server/tool/spec/codegen_java_types.dart b/pkg/analysis_server/tool/spec/codegen_java_types.dart
index 4ecbc1d..a59a788 100644
--- a/pkg/analysis_server/tool/spec/codegen_java_types.dart
+++ b/pkg/analysis_server/tool/spec/codegen_java_types.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Code generation for the file "AnalysisServer.java".
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:html/dom.dart' as dom;
 
 import 'api.dart';
diff --git a/pkg/analysis_server/tool/spec/codegen_matchers.dart b/pkg/analysis_server/tool/spec/codegen_matchers.dart
index afe7545..2e53975 100644
--- a/pkg/analysis_server/tool/spec/codegen_matchers.dart
+++ b/pkg/analysis_server/tool/spec/codegen_matchers.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Code generation for the file "matchers.dart".
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 
 import 'api.dart';
 import 'from_html.dart';
diff --git a/pkg/analysis_server/tool/spec/codegen_protocol_constants.dart b/pkg/analysis_server/tool/spec/codegen_protocol_constants.dart
index 12e2dc2..511f260 100644
--- a/pkg/analysis_server/tool/spec/codegen_protocol_constants.dart
+++ b/pkg/analysis_server/tool/spec/codegen_protocol_constants.dart
@@ -2,7 +2,7 @@
 // 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.
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 
 import 'api.dart';
 import 'codegen_dart.dart';
diff --git a/pkg/analysis_server/tool/spec/from_html.dart b/pkg/analysis_server/tool/spec/from_html.dart
index 5496a9c..237a6a7 100644
--- a/pkg/analysis_server/tool/spec/from_html.dart
+++ b/pkg/analysis_server/tool/spec/from_html.dart
@@ -5,7 +5,7 @@
 /// Code for reading an HTML API description.
 import 'dart:io';
 
-import 'package:analysis_tool/html.dart';
+import 'package:analyzer_utilities/html.dart';
 import 'package:html/dom.dart' as dom;
 import 'package:html/parser.dart' as parser;
 import 'package:path/path.dart';
diff --git a/pkg/analysis_server/tool/spec/generate_all.dart b/pkg/analysis_server/tool/spec/generate_all.dart
index ac9eb91..20840ce 100644
--- a/pkg/analysis_server/tool/spec/generate_all.dart
+++ b/pkg/analysis_server/tool/spec/generate_all.dart
@@ -4,7 +4,7 @@
 
 import 'dart:io';
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart';
 
 import 'codegen_analysis_server.dart' as codegen_analysis_server;
diff --git a/pkg/analysis_server/tool/spec/implied_types.dart b/pkg/analysis_server/tool/spec/implied_types.dart
index 44d89c2..fb67058 100644
--- a/pkg/analysis_server/tool/spec/implied_types.dart
+++ b/pkg/analysis_server/tool/spec/implied_types.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Code for enumerating the set of types implied by the API.
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 
 import 'api.dart';
 
diff --git a/pkg/analysis_server/tool/spec/to_html.dart b/pkg/analysis_server/tool/spec/to_html.dart
index 9ad321a..db50d44 100644
--- a/pkg/analysis_server/tool/spec/to_html.dart
+++ b/pkg/analysis_server/tool/spec/to_html.dart
@@ -7,8 +7,8 @@
 /// in generated code.
 import 'dart:convert';
 
-import 'package:analysis_tool/html.dart';
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/html.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:html/dom.dart' as dom;
 
 import 'api.dart';
diff --git a/pkg/analysis_server_client/pubspec.yaml b/pkg/analysis_server_client/pubspec.yaml
index 465884d..0589bc3 100644
--- a/pkg/analysis_server_client/pubspec.yaml
+++ b/pkg/analysis_server_client/pubspec.yaml
@@ -15,6 +15,6 @@
     path: ../analyzer
   analysis_server:
     path: ../analysis_server
-  analysis_tool:
-    path: ../analysis_tool
+  analyzer_utilities:
+    path: ../analyzer_utilities
   test: ^1.14.2
diff --git a/pkg/analysis_server_client/test/verify_sorted_test.dart b/pkg/analysis_server_client/test/verify_sorted_test.dart
index 66a848a..79d7299 100644
--- a/pkg/analysis_server_client/test/verify_sorted_test.dart
+++ b/pkg/analysis_server_client/test/verify_sorted_test.dart
@@ -5,12 +5,12 @@
 import 'dart:io';
 
 import 'package:analysis_server/src/services/correction/sort_members.dart';
-import 'package:analysis_tool/package_root.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer_utilities/package_root.dart';
 import 'package:test/test.dart';
 
 void main() {
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index 0ad2ec6..f097d37 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -22,8 +22,8 @@
   watcher: ^0.9.6
   yaml: ^2.1.2
 dev_dependencies:
-  analysis_tool:
-    path: ../analysis_tool
+  analyzer_utilities:
+    path: ../analyzer_utilities
   linter: any
   matcher: ^0.12.3
   pedantic: ^1.9.0
diff --git a/pkg/analyzer/test/src/fasta/message_coverage_test.dart b/pkg/analyzer/test/src/fasta/message_coverage_test.dart
index 8b309c8..73a7fac 100644
--- a/pkg/analyzer/test/src/fasta/message_coverage_test.dart
+++ b/pkg/analyzer/test/src/fasta/message_coverage_test.dart
@@ -4,9 +4,9 @@
 
 import 'dart:io' as io;
 
-import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
 import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
diff --git a/pkg/analyzer/test/src/summary2/ast_text_printer_integration_test.dart b/pkg/analyzer/test/src/summary2/ast_text_printer_integration_test.dart
index 867debe..64623f8 100644
--- a/pkg/analyzer/test/src/summary2/ast_text_printer_integration_test.dart
+++ b/pkg/analyzer/test/src/summary2/ast_text_printer_integration_test.dart
@@ -4,8 +4,8 @@
 
 import 'dart:io';
 
-import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
 import 'package:test/test.dart';
 
 import '../dart/ast/parse_base.dart';
diff --git a/pkg/analyzer/test/verify_docs_test.dart b/pkg/analyzer/test/verify_docs_test.dart
index 2254409..32e6bc1 100644
--- a/pkg/analyzer/test/verify_docs_test.dart
+++ b/pkg/analyzer/test/verify_docs_test.dart
@@ -4,7 +4,6 @@
 
 import 'dart:convert';
 
-import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analyzer/dart/analysis/analysis_context.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
@@ -13,6 +12,7 @@
 import 'package:analyzer/file_system/overlay_file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
 import 'package:test/test.dart';
 
 main() async {
diff --git a/pkg/analyzer/test/verify_tests_test.dart b/pkg/analyzer/test/verify_tests_test.dart
index d1e1e17..3db4bb7 100644
--- a/pkg/analyzer/test/verify_tests_test.dart
+++ b/pkg/analyzer/test/verify_tests_test.dart
@@ -2,10 +2,10 @@
 // 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.
 
-import 'package:analysis_tool/package_root.dart' as package_root;
-import 'package:analysis_tool/verify_tests.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
+import 'package:analyzer_utilities/verify_tests.dart';
 
 main() {
   var provider = PhysicalResourceProvider.INSTANCE;
diff --git a/pkg/analyzer/tool/diagnostics/generate.dart b/pkg/analyzer/tool/diagnostics/generate.dart
index 18dcbd2..7571b56 100644
--- a/pkg/analyzer/tool/diagnostics/generate.dart
+++ b/pkg/analyzer/tool/diagnostics/generate.dart
@@ -4,13 +4,13 @@
 
 import 'dart:io';
 
-import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
 import 'package:path/src/context.dart';
 
 /// Generate the file `diagnostics.md` based on the documentation associated
diff --git a/pkg/analyzer/tool/experiments/experiments_test.dart b/pkg/analyzer/tool/experiments/experiments_test.dart
index 4f77ee3..575c084 100644
--- a/pkg/analyzer/tool/experiments/experiments_test.dart
+++ b/pkg/analyzer/tool/experiments/experiments_test.dart
@@ -4,7 +4,7 @@
 
 import "dart:io" show Platform;
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart';
 
 import 'generate.dart';
diff --git a/pkg/analyzer/tool/experiments/generate.dart b/pkg/analyzer/tool/experiments/generate.dart
index ac315ad..6e28018 100644
--- a/pkg/analyzer/tool/experiments/generate.dart
+++ b/pkg/analyzer/tool/experiments/generate.dart
@@ -4,8 +4,8 @@
 
 import 'package:_fe_analyzer_shared/src/scanner/characters.dart'
     show $MINUS, $_;
-import 'package:analysis_tool/package_root.dart' as pkg_root;
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/package_root.dart' as pkg_root;
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart';
 import 'package:yaml/yaml.dart' show YamlMap, loadYaml;
 
diff --git a/pkg/analyzer/tool/messages/generate.dart b/pkg/analyzer/tool/messages/generate.dart
index 23e066e..e845177 100644
--- a/pkg/analyzer/tool/messages/generate.dart
+++ b/pkg/analyzer/tool/messages/generate.dart
@@ -13,10 +13,10 @@
 import 'dart:io';
 
 import 'package:_fe_analyzer_shared/src/scanner/scanner.dart';
-import 'package:analysis_tool/package_root.dart' as pkg_root;
-import 'package:analysis_tool/tools.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/src/dart/error/syntactic_errors.dart';
+import 'package:analyzer_utilities/package_root.dart' as pkg_root;
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart';
 import 'package:yaml/yaml.dart' show loadYaml;
 
diff --git a/pkg/analyzer/tool/summary/check_test.dart b/pkg/analyzer/tool/summary/check_test.dart
index 34edda7..a5d0473 100644
--- a/pkg/analyzer/tool/summary/check_test.dart
+++ b/pkg/analyzer/tool/summary/check_test.dart
@@ -2,8 +2,8 @@
 // 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.
 
-import 'package:analysis_tool/package_root.dart' as package_root;
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart';
 
 import 'generate.dart';
diff --git a/pkg/analyzer/tool/summary/generate.dart b/pkg/analyzer/tool/summary/generate.dart
index 945b34c..3bba6a2 100644
--- a/pkg/analyzer/tool/summary/generate.dart
+++ b/pkg/analyzer/tool/summary/generate.dart
@@ -20,7 +20,7 @@
 
 import 'package:_fe_analyzer_shared/src/scanner/scanner.dart';
 import 'package:_fe_analyzer_shared/src/scanner/token.dart' show Token;
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 
 import 'idl_model.dart' as idl_model;
 import 'mini_ast.dart';
diff --git a/pkg/analyzer_plugin/pubspec.yaml b/pkg/analyzer_plugin/pubspec.yaml
index 50a1e05..b3ba237 100644
--- a/pkg/analyzer_plugin/pubspec.yaml
+++ b/pkg/analyzer_plugin/pubspec.yaml
@@ -14,8 +14,8 @@
   pub_semver: '^1.3.2'
 
 dev_dependencies:
-  analysis_tool:
-    path: ../analysis_tool
+  analyzer_utilities:
+    path: ../analyzer_utilities
   html: '>=0.13.1 <0.15.0'
   path: '^1.4.1'
   test_reflective_loader: ^0.1.8
diff --git a/pkg/analyzer_plugin/test/verify_tests_test.dart b/pkg/analyzer_plugin/test/verify_tests_test.dart
index 3ea9290..48657ce 100644
--- a/pkg/analyzer_plugin/test/verify_tests_test.dart
+++ b/pkg/analyzer_plugin/test/verify_tests_test.dart
@@ -2,10 +2,10 @@
 // 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.
 
-import 'package:analysis_tool/package_root.dart' as package_root;
-import 'package:analysis_tool/verify_tests.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
+import 'package:analyzer_utilities/verify_tests.dart';
 
 void main() {
   var provider = PhysicalResourceProvider.INSTANCE;
diff --git a/pkg/analyzer_plugin/tool/spec/check_all_test.dart b/pkg/analyzer_plugin/tool/spec/check_all_test.dart
index 2d4f184..24b06fa 100644
--- a/pkg/analyzer_plugin/tool/spec/check_all_test.dart
+++ b/pkg/analyzer_plugin/tool/spec/check_all_test.dart
@@ -4,7 +4,7 @@
 
 import 'dart:io';
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart';
 
 import 'generate_all.dart';
diff --git a/pkg/analyzer_plugin/tool/spec/codegen_dart_protocol.dart b/pkg/analyzer_plugin/tool/spec/codegen_dart_protocol.dart
index 93c58c0..6497fe8 100644
--- a/pkg/analyzer_plugin/tool/spec/codegen_dart_protocol.dart
+++ b/pkg/analyzer_plugin/tool/spec/codegen_dart_protocol.dart
@@ -4,7 +4,7 @@
 
 import 'dart:convert';
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:html/dom.dart' as dom;
 import 'package:path/path.dart' as path;
 
diff --git a/pkg/analyzer_plugin/tool/spec/codegen_inttest_methods.dart b/pkg/analyzer_plugin/tool/spec/codegen_inttest_methods.dart
index add730b..fe65490 100644
--- a/pkg/analyzer_plugin/tool/spec/codegen_inttest_methods.dart
+++ b/pkg/analyzer_plugin/tool/spec/codegen_inttest_methods.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Code generation for the file "integration_test_methods.dart".
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart' as path;
 
 import 'api.dart';
diff --git a/pkg/analyzer_plugin/tool/spec/codegen_matchers.dart b/pkg/analyzer_plugin/tool/spec/codegen_matchers.dart
index 00110bc..9e3e2fd 100644
--- a/pkg/analyzer_plugin/tool/spec/codegen_matchers.dart
+++ b/pkg/analyzer_plugin/tool/spec/codegen_matchers.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Code generation for the file "matchers.dart".
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 
 import 'api.dart';
 import 'from_html.dart';
diff --git a/pkg/analyzer_plugin/tool/spec/codegen_protocol_common.dart b/pkg/analyzer_plugin/tool/spec/codegen_protocol_common.dart
index ae017a6..099eb1d 100644
--- a/pkg/analyzer_plugin/tool/spec/codegen_protocol_common.dart
+++ b/pkg/analyzer_plugin/tool/spec/codegen_protocol_common.dart
@@ -2,7 +2,7 @@
 // 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.
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart' as path;
 
 import 'api.dart';
diff --git a/pkg/analyzer_plugin/tool/spec/codegen_protocol_constants.dart b/pkg/analyzer_plugin/tool/spec/codegen_protocol_constants.dart
index 2c6718f..52618ac 100644
--- a/pkg/analyzer_plugin/tool/spec/codegen_protocol_constants.dart
+++ b/pkg/analyzer_plugin/tool/spec/codegen_protocol_constants.dart
@@ -2,7 +2,7 @@
 // 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.
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 
 import 'api.dart';
 import 'codegen_dart.dart';
diff --git a/pkg/analyzer_plugin/tool/spec/from_html.dart b/pkg/analyzer_plugin/tool/spec/from_html.dart
index 86d0ebb..977737b 100644
--- a/pkg/analyzer_plugin/tool/spec/from_html.dart
+++ b/pkg/analyzer_plugin/tool/spec/from_html.dart
@@ -5,7 +5,7 @@
 /// Code for reading an HTML API description.
 import 'dart:io';
 
-import 'package:analysis_tool/html.dart';
+import 'package:analyzer_utilities/html.dart';
 import 'package:html/dom.dart' as dom;
 import 'package:html/parser.dart' as parser;
 import 'package:path/path.dart';
diff --git a/pkg/analyzer_plugin/tool/spec/generate_all.dart b/pkg/analyzer_plugin/tool/spec/generate_all.dart
index 4083370..5375f42 100644
--- a/pkg/analyzer_plugin/tool/spec/generate_all.dart
+++ b/pkg/analyzer_plugin/tool/spec/generate_all.dart
@@ -4,7 +4,7 @@
 
 import 'dart:io';
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:path/path.dart';
 
 import 'codegen_dart_protocol.dart' as codegen_dart_protocol;
diff --git a/pkg/analyzer_plugin/tool/spec/implied_types.dart b/pkg/analyzer_plugin/tool/spec/implied_types.dart
index 0eb3e4b..04f217c 100644
--- a/pkg/analyzer_plugin/tool/spec/implied_types.dart
+++ b/pkg/analyzer_plugin/tool/spec/implied_types.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Code for enumerating the set of types implied by the API.
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 
 import 'api.dart';
 
diff --git a/pkg/analyzer_plugin/tool/spec/to_html.dart b/pkg/analyzer_plugin/tool/spec/to_html.dart
index 115b3ec..923e34b 100644
--- a/pkg/analyzer_plugin/tool/spec/to_html.dart
+++ b/pkg/analyzer_plugin/tool/spec/to_html.dart
@@ -7,8 +7,8 @@
 /// in generated code.
 import 'dart:convert';
 
-import 'package:analysis_tool/html.dart';
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/html.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:html/dom.dart' as dom;
 
 import 'api.dart';
diff --git a/pkg/analysis_tool/README.md b/pkg/analyzer_utilities/README.md
similarity index 100%
rename from pkg/analysis_tool/README.md
rename to pkg/analyzer_utilities/README.md
diff --git a/pkg/analysis_tool/analysis_options.yaml b/pkg/analyzer_utilities/analysis_options.yaml
similarity index 100%
rename from pkg/analysis_tool/analysis_options.yaml
rename to pkg/analyzer_utilities/analysis_options.yaml
diff --git a/pkg/analysis_tool/lib/html.dart b/pkg/analyzer_utilities/lib/html.dart
similarity index 100%
rename from pkg/analysis_tool/lib/html.dart
rename to pkg/analyzer_utilities/lib/html.dart
diff --git a/pkg/analysis_tool/lib/package_root.dart b/pkg/analyzer_utilities/lib/package_root.dart
similarity index 100%
rename from pkg/analysis_tool/lib/package_root.dart
rename to pkg/analyzer_utilities/lib/package_root.dart
diff --git a/pkg/analysis_tool/lib/text_formatter.dart b/pkg/analyzer_utilities/lib/text_formatter.dart
similarity index 98%
rename from pkg/analysis_tool/lib/text_formatter.dart
rename to pkg/analyzer_utilities/lib/text_formatter.dart
index 2305910..a35920b 100644
--- a/pkg/analysis_tool/lib/text_formatter.dart
+++ b/pkg/analyzer_utilities/lib/text_formatter.dart
@@ -7,7 +7,7 @@
  * analyzer and analysis server.
  */
 
-import 'package:analysis_tool/tools.dart';
+import 'package:analyzer_utilities/tools.dart';
 import 'package:html/dom.dart' as dom;
 
 final RegExp whitespace = new RegExp(r'\s');
diff --git a/pkg/analysis_tool/lib/tools.dart b/pkg/analyzer_utilities/lib/tools.dart
similarity index 99%
rename from pkg/analysis_tool/lib/tools.dart
rename to pkg/analyzer_utilities/lib/tools.dart
index 831014e..c26e5a5 100644
--- a/pkg/analysis_tool/lib/tools.dart
+++ b/pkg/analyzer_utilities/lib/tools.dart
@@ -8,8 +8,8 @@
 import 'dart:async';
 import 'dart:io';
 
-import 'package:analysis_tool/html.dart';
-import 'package:analysis_tool/text_formatter.dart';
+import 'package:analyzer_utilities/html.dart';
+import 'package:analyzer_utilities/text_formatter.dart';
 import 'package:html/dom.dart' as dom;
 import 'package:path/path.dart';
 
diff --git a/pkg/analysis_tool/lib/verify_tests.dart b/pkg/analyzer_utilities/lib/verify_tests.dart
similarity index 100%
rename from pkg/analysis_tool/lib/verify_tests.dart
rename to pkg/analyzer_utilities/lib/verify_tests.dart
diff --git a/pkg/analysis_tool/pubspec.yaml b/pkg/analyzer_utilities/pubspec.yaml
similarity index 88%
rename from pkg/analysis_tool/pubspec.yaml
rename to pkg/analyzer_utilities/pubspec.yaml
index f4763fd..ed67a42 100644
--- a/pkg/analysis_tool/pubspec.yaml
+++ b/pkg/analyzer_utilities/pubspec.yaml
@@ -1,4 +1,4 @@
-name: analysis_tool
+name: analyzer_utilities
 # This package is not intended for consumption on pub.dev. DO NOT publish.
 publish_to: none
 
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index 703abc6..e2229b4 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -33,34 +33,128 @@
 Future<void> runDartdev(List<String> args, SendPort port) async {
   VmInteropHandler.initialize(port);
 
-  if (args.contains('run')) {
-    // These flags have a format that can't be handled by package:args, so while
-    // they are valid flags we'll assume the VM has verified them by this point.
-    args = args
-        .where(
-          (element) => !(element.contains('--observe') ||
-              element.contains('--enable-vm-service')),
-        )
-        .toList();
+  int result;
+
+  // The exit code for the dartdev process; null indicates that it has not been
+  // set yet. The value is set in the catch and finally blocks below.
+  int exitCode;
+
+  // Any caught non-UsageExceptions when running the sub command.
+  Object exception;
+  StackTrace stackTrace;
+
+  // The Analytics instance used to report information back to Google Analytics;
+  // see lib/src/analytics.dart.
+  final analytics = createAnalyticsInstance(
+    args.contains('--disable-dartdev-analytics'),
+  );
+
+  // If we have not printed the analyticsNoticeOnFirstRunMessage to stdout,
+  // the user is on a terminal, and the machine is not a bot, then print the
+  // disclosure and set analytics.disclosureShownOnTerminal to true.
+  if (analytics is DartdevAnalytics &&
+      !analytics.disclosureShownOnTerminal &&
+      io.stdout.hasTerminal &&
+      !isBot()) {
+    print(analyticsNoticeOnFirstRunMessage);
+    analytics.disclosureShownOnTerminal = true;
   }
 
-  // Finally, call the runner to execute the command; see DartdevRunner.
+  // When `--disable-analytics` or `--enable-analytics` are called we perform
+  // the respective intention and print any notices to standard out and exit.
+  if (args.contains('--disable-analytics')) {
+    // This block also potentially catches the case of (disableAnalytics &&
+    // enableAnalytics), in which we favor the disabling of analytics.
+    analytics.enabled = false;
 
-  final runner = DartdevRunner(args);
-  var exitCode = 1;
+    // Alert the user that analytics has been disabled.
+    print(analyticsDisabledNoticeMessage);
+    VmInteropHandler.exit(0);
+    return;
+  } else if (args.contains('--enable-analytics')) {
+    analytics.enabled = true;
+
+    // Alert the user again that anonymous data will be collected.
+    print(analyticsNoticeOnFirstRunMessage);
+    VmInteropHandler.exit(0);
+    return;
+  }
+
   try {
-    exitCode = await runner.run(args);
-  } on UsageException catch (e) {
-    // TODO(sigurdm): It is unclear when a UsageException gets to here, and
-    // when it is in DartdevRunner.runCommand.
-    io.stderr.writeln('$e');
-    exitCode = 64;
+    final runner = DartdevRunner(args, analytics);
+
+    // Run can't be called with the '--disable-dartdev-analytics' flag; remove
+    // it if it is contained in args.
+    if (args.contains('--disable-dartdev-analytics')) {
+      args = List.from(args)..remove('--disable-dartdev-analytics');
+    }
+
+    if (args.contains('run')) {
+      // These flags have a format that can't be handled by package:args, so while
+      // they are valid flags we'll assume the VM has verified them by this point.
+      args = args
+          .where(
+            (element) => !(element.contains('--observe') ||
+                element.contains('--enable-vm-service')),
+          )
+          .toList();
+    }
+
+    // If ... help pub ... is in the args list, remove 'help', and add '--help'
+    // to the end of the list. This will make it possible to use the help
+    // command to access subcommands of pub such as `dart help pub publish`; see
+    // https://github.com/dart-lang/sdk/issues/42965.
+    if (PubUtils.shouldModifyArgs(args, runner.commands.keys.toList())) {
+      args = PubUtils.modifyArgs(args);
+    }
+
+    // Finally, call the runner to execute the command; see DartdevRunner.
+    result = await runner.run(args);
+  } catch (e, st) {
+    if (e is UsageException) {
+      io.stderr.writeln('$e');
+      exitCode = 64;
+    } else {
+      // Set the exception and stack trace only for non-UsageException cases:
+      exception = e;
+      stackTrace = st;
+      io.stderr.writeln('$e');
+      io.stderr.writeln('$st');
+      exitCode = 1;
+    }
   } finally {
+    // Set the exitCode, if it wasn't set in the catch block above.
+    exitCode ??= result ?? 0;
+
+    // Send analytics before exiting
+    if (analytics.enabled) {
+      // And now send the exceptions and events to Google Analytics:
+      if (exception != null) {
+        unawaited(
+          analytics.sendException(
+              '${exception.runtimeType}\n${sanitizeStacktrace(stackTrace)}',
+              fatal: true),
+        );
+      }
+
+      await analytics.waitForLastPing(
+          timeout: const Duration(milliseconds: 200));
+    }
+
+    // Set the enabled flag in the analytics object to true. Note: this will not
+    // enable the analytics unless the disclosure was shown (terminal detected),
+    // and the machine is not detected to be a bot.
+    if (analytics.firstRun) {
+      analytics.enabled = true;
+    }
+    analytics.close();
     VmInteropHandler.exit(exitCode);
   }
 }
 
 class DartdevRunner extends CommandRunner<int> {
+  final Analytics analytics;
+
   @override
   final ArgParser argParser =
       ArgParser(usageLineLength: dartdevUsageLineLength);
@@ -68,7 +162,8 @@
   static const String dartdevDescription =
       'A command-line utility for Dart development';
 
-  DartdevRunner(List<String> args) : super('dart', '$dartdevDescription.') {
+  DartdevRunner(List<String> args, this.analytics)
+      : super('dart', '$dartdevDescription.') {
     final bool verbose = args.contains('-v') || args.contains('--verbose');
 
     argParser.addFlag('verbose',
@@ -83,9 +178,12 @@
     argParser.addFlag('diagnostics',
         negatable: false, help: 'Show tool diagnostic output.', hide: !verbose);
 
+    // A hidden flag to disable analytics on this run, this constructor can be
+    // called with this flag, but should be removed before run() is called as
+    // the flag has not been added to all sub-commands.
     argParser.addFlag(
-      'analytics',
-      negatable: true,
+      'disable-dartdev-analytics',
+      negatable: false,
       help: 'Disable anonymous analytics for this `dart *` run',
       hide: true,
     );
@@ -112,38 +210,7 @@
   @override
   Future<int> runCommand(ArgResults topLevelResults) async {
     final stopwatch = Stopwatch()..start();
-    // The Analytics instance used to report information back to Google Analytics;
-    // see lib/src/analytics.dart.
-    final analytics = createAnalyticsInstance(!topLevelResults['analytics']);
-
-    // If we have not printed the analyticsNoticeOnFirstRunMessage to stdout,
-    // the user is on a terminal, and the machine is not a bot, then print the
-    // disclosure and set analytics.disclosureShownOnTerminal to true.
-    if (analytics is DartdevAnalytics &&
-        !analytics.disclosureShownOnTerminal &&
-        io.stdout.hasTerminal &&
-        !isBot()) {
-      print(analyticsNoticeOnFirstRunMessage);
-      analytics.disclosureShownOnTerminal = true;
-    }
-
-    // When `--disable-analytics` or `--enable-analytics` are called we perform
-    // the respective intention and print any notices to standard out and exit.
-    if (topLevelResults['disable-analytics']) {
-      // This block also potentially catches the case of (disableAnalytics &&
-      // enableAnalytics), in which we favor the disabling of analytics.
-      analytics.enabled = false;
-
-      // Alert the user that analytics has been disabled.
-      print(analyticsDisabledNoticeMessage);
-      return 0;
-    } else if (topLevelResults['enable-analytics']) {
-      analytics.enabled = true;
-
-      // Alert the user again that anonymous data will be collected.
-      print(analyticsNoticeOnFirstRunMessage);
-      return 0;
-    }
+    assert(!topLevelResults.arguments.contains('--disable-dartdev-analytics'));
 
     if (topLevelResults.command == null &&
         topLevelResults.arguments.isNotEmpty) {
@@ -153,12 +220,14 @@
         io.stderr.writeln(
             "Error when reading '$firstArg': No such file or directory.");
         // This is the exit code used by the frontend.
-        return 254;
+        VmInteropHandler.exit(254);
       }
     }
 
+    isDiagnostics = topLevelResults['diagnostics'];
+
     final Ansi ansi = Ansi(Ansi.terminalSupportsAnsi);
-    log = topLevelResults['diagnostics']
+    log = isDiagnostics
         ? Logger.verbose(ansi: ansi)
         : Logger.standard(ansi: ansi);
 
@@ -176,15 +245,8 @@
       analytics.sendScreenView(path),
     );
 
-    // The exit code for the dartdev process; null indicates that it has not been
-    // set yet. The value is set in the catch and finally blocks below.
-    int exitCode;
-
-    // Any caught non-UsageExceptions when running the sub command.
-    Object exception;
-    StackTrace stackTrace;
     try {
-      exitCode = await super.runCommand(topLevelResults);
+      final exitCode = await super.runCommand(topLevelResults);
 
       if (path != null && analytics.enabled) {
         // Send the event to analytics
@@ -204,16 +266,8 @@
           ),
         );
       }
-    } on UsageException catch (e) {
-      io.stderr.writeln('$e');
-      exitCode = 64;
-    } catch (e, st) {
-      // Set the exception and stack trace only for non-UsageException cases:
-      exception = e;
-      stackTrace = st;
-      io.stderr.writeln('$e');
-      io.stderr.writeln('$st');
-      exitCode = 1;
+
+      return exitCode;
     } finally {
       stopwatch.stop();
       if (analytics.enabled) {
@@ -225,32 +279,6 @@
           ),
         );
       }
-      // Set the exitCode, if it wasn't set in the catch block above.
-      exitCode ??= 0;
-
-      // Send analytics before exiting
-      if (analytics.enabled) {
-        // And now send the exceptions and events to Google Analytics:
-        if (exception != null) {
-          unawaited(
-            analytics.sendException(
-                '${exception.runtimeType}\n${sanitizeStacktrace(stackTrace)}',
-                fatal: true),
-          );
-        }
-
-        await analytics.waitForLastPing(
-            timeout: const Duration(milliseconds: 200));
-      }
-
-      // Set the enabled flag in the analytics object to true. Note: this will not
-      // enable the analytics unless the disclosure was shown (terminal detected),
-      // and the machine is not detected to be a bot.
-      if (analytics.firstRun) {
-        analytics.enabled = true;
-      }
-      analytics.close();
-      return exitCode;
     }
   }
 }
diff --git a/pkg/dartdev/lib/src/analytics.dart b/pkg/dartdev/lib/src/analytics.dart
index 0462363..f8e8ffc 100644
--- a/pkg/dartdev/lib/src/analytics.dart
+++ b/pkg/dartdev/lib/src/analytics.dart
@@ -53,7 +53,7 @@
   }
 
   if (disableAnalytics) {
-    // Dartdev tests pass a hidden 'no-analytics' flag which is
+    // Dartdev tests pass a hidden 'disable-dartdev-analytics' flag which is
     // handled here.
     // Also, stdout.hasTerminal is checked, if there is no terminal we infer that
     // a machine is running dartdev so we return analytics shouldn't be set.
diff --git a/pkg/dartdev/lib/src/utils.dart b/pkg/dartdev/lib/src/utils.dart
index 32a9c8e..88b80ed 100644
--- a/pkg/dartdev/lib/src/utils.dart
+++ b/pkg/dartdev/lib/src/utils.dart
@@ -38,6 +38,31 @@
   return s;
 }
 
+/// Static util methods used in dartdev to potentially modify the order of the
+/// arguments passed into dartdev.
+class PubUtils {
+  /// If [doModifyArgs] returns true, then this method returns a modified copy
+  /// of the argument list, 'help' is removed from the interior of the list, and
+  /// '--help' is added to the end of the list of arguments. This method returns
+  /// a modified copy of the list, the list itself is not modified.
+  static List<String> modifyArgs(List<String> args) => List.from(args)
+    ..remove('help')
+    ..add('--help');
+
+  /// If ... help pub ..., and no other verb (such as 'analyze') appears before
+  /// the ... help pub ... in the argument list, then return true.
+  static bool shouldModifyArgs(List<String> args, List<String> allCmds) =>
+      args != null &&
+      allCmds != null &&
+      args.isNotEmpty &&
+      allCmds.isNotEmpty &&
+      args.firstWhere((arg) => allCmds.contains(arg), orElse: () => '') ==
+          'help' &&
+      args.contains('help') &&
+      args.contains('pub') &&
+      args.indexOf('help') + 1 == args.indexOf('pub');
+}
+
 extension FileSystemEntityExtension on FileSystemEntity {
   String get name => p.basename(path);
 
diff --git a/pkg/dartdev/test/analytics_test.dart b/pkg/dartdev/test/analytics_test.dart
index 8c74972..26d9822 100644
--- a/pkg/dartdev/test/analytics_test.dart
+++ b/pkg/dartdev/test/analytics_test.dart
@@ -23,7 +23,7 @@
   group('Sending analytics', () {
     test('help', () {
       final p = project(logAnalytics: true);
-      final result = p.runSync(['help']);
+      final result = p.runSync('help', []);
       expect(extractAnalytics(result), [
         {
           'hitType': 'screenView',
@@ -51,7 +51,7 @@
     });
     test('create', () {
       final p = project(logAnalytics: true);
-      final result = p.runSync(['create', '-tpackage-simple', 'name']);
+      final result = p.runSync('create', ['-tpackage-simple', 'name']);
       expect(extractAnalytics(result), [
         {
           'hitType': 'screenView',
@@ -82,7 +82,7 @@
 
     test('pub get', () {
       final p = project(logAnalytics: true);
-      final result = p.runSync(['pub', 'get', '--dry-run']);
+      final result = p.runSync('pub', ['get', '--dry-run']);
       expect(extractAnalytics(result), [
         {
           'hitType': 'screenView',
@@ -113,7 +113,7 @@
 
     test('format', () {
       final p = project(logAnalytics: true);
-      final result = p.runSync(['format', '-l80']);
+      final result = p.runSync('format', ['-l80']);
       expect(extractAnalytics(result), [
         {
           'hitType': 'screenView',
@@ -146,8 +146,7 @@
       final p = project(
           mainSrc: 'void main(List<String> args) => print(args)',
           logAnalytics: true);
-      final result = p.runSync([
-        'run',
+      final result = p.runSync('run', [
         '--no-pause-isolates-on-exit',
         '--enable-asserts',
         'lib/main.dart',
@@ -185,8 +184,7 @@
       final p = project(
           mainSrc: 'void main(List<String> args) => print(args);',
           logAnalytics: true);
-      final result = p.runSync([
-        'run',
+      final result = p.runSync('run', [
         '--enable-experiment=non-nullable',
         'lib/main.dart',
       ]);
@@ -223,7 +221,7 @@
           mainSrc: 'void main(List<String> args) => print(args);',
           logAnalytics: true);
       final result = p
-          .runSync(['compile', 'kernel', 'lib/main.dart', '-o', 'main.kernel']);
+          .runSync('compile', ['kernel', 'lib/main.dart', '-o', 'main.kernel']);
       expect(extractAnalytics(result), [
         {
           'hitType': 'screenView',
diff --git a/pkg/dartdev/test/commands/analyze_test.dart b/pkg/dartdev/test/commands/analyze_test.dart
index 0c07435..2d88b65 100644
--- a/pkg/dartdev/test/commands/analyze_test.dart
+++ b/pkg/dartdev/test/commands/analyze_test.dart
@@ -62,7 +62,7 @@
 
   test('--help', () {
     p = project();
-    var result = p.runSync(['analyze', '--help']);
+    var result = p.runSync('analyze', ['--help']);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -72,7 +72,7 @@
 
   test('multiple directories', () {
     p = project();
-    var result = p.runSync(['analyze', '/no/such/dir1/', '/no/such/dir2/']);
+    var result = p.runSync('analyze', ['/no/such/dir1/', '/no/such/dir2/']);
 
     expect(result.exitCode, 64);
     expect(result.stdout, isEmpty);
@@ -82,7 +82,7 @@
 
   test('no such directory', () {
     p = project();
-    var result = p.runSync(['analyze', '/no/such/dir1/']);
+    var result = p.runSync('analyze', ['/no/such/dir1/']);
 
     expect(result.exitCode, 64);
     expect(result.stdout, isEmpty);
@@ -93,7 +93,7 @@
   test('current working directory', () {
     p = project(mainSrc: 'int get foo => 1;\n');
 
-    var result = p.runSync(['analyze'], workingDir: p.dirPath);
+    var result = p.runSync('analyze', [], workingDir: p.dirPath);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -102,7 +102,7 @@
 
   test('no errors', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    var result = p.runSync(['analyze', p.dirPath]);
+    var result = p.runSync('analyze', [p.dirPath]);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -111,7 +111,7 @@
 
   test('one error', () {
     p = project(mainSrc: "int get foo => 'str';\n");
-    var result = p.runSync(['analyze', p.dirPath]);
+    var result = p.runSync('analyze', [p.dirPath]);
 
     expect(result.exitCode, 3);
     expect(result.stderr, isEmpty);
@@ -123,7 +123,7 @@
 
   test('two errors', () {
     p = project(mainSrc: "int get foo => 'str';\nint get bar => 'str';\n");
-    var result = p.runSync(['analyze', p.dirPath]);
+    var result = p.runSync('analyze', [p.dirPath]);
 
     expect(result.exitCode, 3);
     expect(result.stderr, isEmpty);
@@ -134,7 +134,7 @@
     p = project(
         mainSrc: _unusedImportCodeSnippet,
         analysisOptions: _unusedImportAnalysisOptions);
-    var result = p.runSync(['analyze', '--fatal-warnings', p.dirPath]);
+    var result = p.runSync('analyze', ['--fatal-warnings', p.dirPath]);
 
     expect(result.exitCode, equals(2));
     expect(result.stderr, isEmpty);
@@ -145,7 +145,7 @@
     p = project(
         mainSrc: _unusedImportCodeSnippet,
         analysisOptions: _unusedImportAnalysisOptions);
-    var result = p.runSync(['analyze', p.dirPath]);
+    var result = p.runSync('analyze', [p.dirPath]);
 
     expect(result.exitCode, equals(2));
     expect(result.stderr, isEmpty);
@@ -156,7 +156,7 @@
     p = project(
         mainSrc: _unusedImportCodeSnippet,
         analysisOptions: _unusedImportAnalysisOptions);
-    var result = p.runSync(['analyze', '--no-fatal-warnings', p.dirPath]);
+    var result = p.runSync('analyze', ['--no-fatal-warnings', p.dirPath]);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -165,7 +165,7 @@
 
   test('info implicit no --fatal-infos', () {
     p = project(mainSrc: dartVersionFilePrefix2_9 + 'String foo() {}');
-    var result = p.runSync(['analyze', p.dirPath]);
+    var result = p.runSync('analyze', [p.dirPath]);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -174,7 +174,7 @@
 
   test('info --fatal-infos', () {
     p = project(mainSrc: dartVersionFilePrefix2_9 + 'String foo() {}');
-    var result = p.runSync(['analyze', '--fatal-infos', p.dirPath]);
+    var result = p.runSync('analyze', ['--fatal-infos', p.dirPath]);
 
     expect(result.exitCode, 1);
     expect(result.stderr, isEmpty);
@@ -188,7 +188,7 @@
   var one = 1;
   return result;
 }''');
-    var result = p.runSync(['analyze', '--verbose', p.dirPath]);
+    var result = p.runSync('analyze', ['--verbose', p.dirPath]);
 
     expect(result.exitCode, 3);
     expect(result.stderr, isEmpty);
diff --git a/pkg/dartdev/test/commands/compile_test.dart b/pkg/dartdev/test/commands/compile_test.dart
index f2f5f31..57e669a 100644
--- a/pkg/dartdev/test/commands/compile_test.dart
+++ b/pkg/dartdev/test/commands/compile_test.dart
@@ -27,9 +27,8 @@
   test('Implicit --help', () {
     final p = project();
     var result = p.runSync(
-      [
-        'compile',
-      ],
+      'compile',
+      [],
     );
     expect(result.stderr, contains('Compile Dart'));
     expect(result.exitCode, compileErrorExitCode);
@@ -38,7 +37,8 @@
   test('--help', () {
     final p = project();
     final result = p.runSync(
-      ['compile', '--help'],
+      'compile',
+      ['--help'],
     );
     expect(result.stdout, contains('Compile Dart'));
     expect(result.exitCode, 0);
@@ -48,8 +48,8 @@
     final p = project(mainSrc: 'void main() { print("I love jit"); }');
     final outFile = path.join(p.dirPath, 'main.jit');
     var result = p.runSync(
+      'compile',
       [
-        'compile',
         'jit-snapshot',
         '-o',
         outFile,
@@ -61,7 +61,7 @@
     expect(File(outFile).existsSync(), true,
         reason: 'File not found: $outFile');
 
-    result = p.runSync(['run', 'main.jit']);
+    result = p.runSync('run', ['main.jit']);
     expect(result.stdout, contains('I love jit'));
     expect(result.stderr, isEmpty);
     expect(result.exitCode, 0);
@@ -73,8 +73,8 @@
     final outFile = path.canonicalize(path.join(p.dirPath, 'lib', 'main.exe'));
 
     var result = p.runSync(
+      'compile',
       [
-        'compile',
         'exe',
         inFile,
       ],
@@ -102,8 +102,8 @@
     final outFile = path.canonicalize(path.join(p.dirPath, 'myexe'));
 
     var result = p.runSync(
+      'compile',
       [
-        'compile',
         'exe',
         '--define',
         'life=42',
@@ -134,8 +134,8 @@
     final outFile = path.canonicalize(path.join(p.dirPath, 'main.aot'));
 
     var result = p.runSync(
+      'compile',
       [
-        'compile',
         'aot-snapshot',
         '-o',
         'main.aot',
@@ -163,8 +163,8 @@
     final p = project(mainSrc: 'void main() { print("I love kernel"); }');
     final outFile = path.join(p.dirPath, 'main.dill');
     var result = p.runSync(
+      'compile',
       [
-        'compile',
         'kernel',
         '-o',
         outFile,
@@ -176,7 +176,7 @@
     expect(result.stderr, isEmpty);
     expect(result.exitCode, 0);
 
-    result = p.runSync(['run', 'main.dill']);
+    result = p.runSync('run', ['main.dill']);
     expect(result.stdout, contains('I love kernel'));
     expect(result.stderr, isEmpty);
     expect(result.exitCode, 0);
@@ -187,8 +187,7 @@
     final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
     final outFile = path.canonicalize(path.join(p.dirPath, 'main.js'));
 
-    final result = p.runSync([
-      'compile',
+    final result = p.runSync('compile', [
       'js',
       '-m',
       '-o',
diff --git a/pkg/dartdev/test/commands/create_test.dart b/pkg/dartdev/test/commands/create_test.dart
index 7f5c846..38a8ebd 100644
--- a/pkg/dartdev/test/commands/create_test.dart
+++ b/pkg/dartdev/test/commands/create_test.dart
@@ -36,7 +36,7 @@
   test('list templates', () {
     p = project();
 
-    ProcessResult result = p.runSync(['create', '--list-templates']);
+    ProcessResult result = p.runSync('create', ['--list-templates']);
     expect(result.exitCode, 0);
 
     String output = result.stdout.toString();
@@ -50,9 +50,7 @@
   test('no directory given', () {
     p = project();
 
-    ProcessResult result = p.runSync([
-      'create',
-    ]);
+    ProcessResult result = p.runSync('create', []);
     expect(result.exitCode, 1);
   });
 
@@ -60,7 +58,7 @@
     p = project();
 
     ProcessResult result = p.runSync(
-        ['create', '--template', CreateCommand.defaultTemplateId, p.dir.path]);
+        'create', ['--template', CreateCommand.defaultTemplateId, p.dir.path]);
     expect(result.exitCode, 73);
   });
 
@@ -68,7 +66,7 @@
     p = project();
 
     ProcessResult result =
-        p.runSync(['create', '--no-pub', '--template', 'foo-bar', p.dir.path]);
+        p.runSync('create', ['--no-pub', '--template', 'foo-bar', p.dir.path]);
     expect(result.exitCode, isNot(0));
   });
 
@@ -78,7 +76,7 @@
       p = project();
 
       ProcessResult result = p
-          .runSync(['create', '--force', '--template', templateId, p.dir.path]);
+          .runSync('create', ['--force', '--template', templateId, p.dir.path]);
       expect(result.exitCode, 0);
 
       String projectName = path.basename(p.dir.path);
diff --git a/pkg/dartdev/test/commands/fix_test.dart b/pkg/dartdev/test/commands/fix_test.dart
index ad4999a..5450742 100644
--- a/pkg/dartdev/test/commands/fix_test.dart
+++ b/pkg/dartdev/test/commands/fix_test.dart
@@ -21,7 +21,7 @@
 
   test('none', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    var result = p.runSync(['fix', p.dirPath]);
+    var result = p.runSync('fix', [p.dirPath]);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Nothing to fix!'));
@@ -38,7 +38,7 @@
     - prefer_single_quotes
 ''',
     );
-    var result = p.runSync(['fix'], workingDir: p.dirPath);
+    var result = p.runSync('fix', [], workingDir: p.dirPath);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Fixed 1 file.'));
@@ -55,7 +55,7 @@
     - prefer_single_quotes
 ''',
     );
-    var result = p.runSync(['fix', '--dry-run', '.'], workingDir: p.dirPath);
+    var result = p.runSync('fix', ['--dry-run', '.'], workingDir: p.dirPath);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(
@@ -76,7 +76,7 @@
     - prefer_single_quotes
 ''',
     );
-    var result = p.runSync(['fix', '.'], workingDir: p.dirPath);
+    var result = p.runSync('fix', ['.'], workingDir: p.dirPath);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Fixed 1 file.'));
@@ -96,7 +96,7 @@
     - prefer_single_quotes
 ''',
     );
-    var result = p.runSync(['fix', '.'], workingDir: p.dirPath);
+    var result = p.runSync('fix', ['.'], workingDir: p.dirPath);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Nothing to fix!'));
@@ -114,7 +114,7 @@
     - prefer_single_quotes
 ''',
     );
-    var result = p.runSync(['fix', '.'], workingDir: p.dirPath);
+    var result = p.runSync('fix', ['.'], workingDir: p.dirPath);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Nothing to fix!'));
diff --git a/pkg/dartdev/test/commands/flag_test.dart b/pkg/dartdev/test/commands/flag_test.dart
index 0475ee3..6416ffc 100644
--- a/pkg/dartdev/test/commands/flag_test.dart
+++ b/pkg/dartdev/test/commands/flag_test.dart
@@ -6,6 +6,7 @@
 
 import 'package:args/command_runner.dart';
 import 'package:dartdev/dartdev.dart';
+import 'package:dartdev/src/analytics.dart' show disabledAnalytics;
 import 'package:test/test.dart';
 
 import '../utils.dart';
@@ -19,7 +20,7 @@
   // For each command description, assert that the values are not empty, don't
   // have trailing white space and end with a period.
   test('description formatting', () {
-    DartdevRunner(['--no-analytics'])
+    DartdevRunner(['--disable-dartdev-analytics'], disabledAnalytics)
         .commands
         .forEach((String commandKey, Command command) {
       expect(commandKey, isNotEmpty);
@@ -31,7 +32,7 @@
 
   // Assert that all found usageLineLengths are the same and null
   test('argParser usageLineLength', () {
-    DartdevRunner(['--no-analytics'])
+    DartdevRunner(['--disable-dartdev-analytics'], disabledAnalytics)
         .commands
         .forEach((String commandKey, Command command) {
       if (command.argParser != null) {
@@ -61,7 +62,7 @@
 
   test('--help', () {
     p = project();
-    var result = p.runSync(['--help']);
+    var result = p.runSync('--help', []);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -79,7 +80,7 @@
 
   test('--help --verbose', () {
     p = project();
-    var result = p.runSync(['--help', '--verbose']);
+    var result = p.runSync('--help', ['--verbose']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, isEmpty);
@@ -89,7 +90,7 @@
 
   test('--help -v', () {
     p = project();
-    var result = p.runSync(['--help', '-v']);
+    var result = p.runSync('--help', ['-v']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, isEmpty);
@@ -99,7 +100,7 @@
 
   test('help', () {
     p = project();
-    var result = p.runSync(['help']);
+    var result = p.runSync('help', []);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -117,7 +118,7 @@
 
   test('help --verbose', () {
     p = project();
-    var result = p.runSync(['help', '--verbose']);
+    var result = p.runSync('help', ['--verbose']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, contains('migrate '));
@@ -125,7 +126,7 @@
 
   test('help -v', () {
     p = project();
-    var result = p.runSync(['help', '-v']);
+    var result = p.runSync('help', ['-v']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, contains('migrate '));
diff --git a/pkg/dartdev/test/commands/format_test.dart b/pkg/dartdev/test/commands/format_test.dart
index 6f36795..cded95b 100644
--- a/pkg/dartdev/test/commands/format_test.dart
+++ b/pkg/dartdev/test/commands/format_test.dart
@@ -19,7 +19,7 @@
 
   test('--help', () {
     p = project();
-    var result = p.runSync(['format', '--help']);
+    var result = p.runSync('format', ['--help']);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Idiomatically format Dart source code.'));
@@ -32,7 +32,7 @@
 
   test('--help --verbose', () {
     p = project();
-    var result = p.runSync(['format', '--help', '--verbose']);
+    var result = p.runSync('format', ['--help', '--verbose']);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Idiomatically format Dart source code.'));
@@ -45,7 +45,7 @@
 
   test('unchanged', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    ProcessResult result = p.runSync(['format', p.relativeFilePath]);
+    ProcessResult result = p.runSync('format', [p.relativeFilePath]);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, startsWith('Formatted 1 file (0 changed) in '));
@@ -53,7 +53,7 @@
 
   test('formatted', () {
     p = project(mainSrc: 'int get foo =>       1;\n');
-    ProcessResult result = p.runSync(['format', p.relativeFilePath]);
+    ProcessResult result = p.runSync('format', [p.relativeFilePath]);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(
@@ -65,7 +65,7 @@
   test('unknown file', () {
     p = project(mainSrc: 'int get foo => 1;\n');
     var unknownFilePath = '${p.relativeFilePath}-unknown-file.dart';
-    ProcessResult result = p.runSync(['format', unknownFilePath]);
+    ProcessResult result = p.runSync('format', [unknownFilePath]);
     expect(result.exitCode, 0);
     expect(result.stderr,
         startsWith('No file or directory found at "$unknownFilePath".'));
diff --git a/pkg/dartdev/test/commands/help_test.dart b/pkg/dartdev/test/commands/help_test.dart
index afbaf09..c272e43 100644
--- a/pkg/dartdev/test/commands/help_test.dart
+++ b/pkg/dartdev/test/commands/help_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:args/command_runner.dart';
 import 'package:dartdev/dartdev.dart';
+import 'package:dartdev/src/analytics.dart' show disabledAnalytics;
 import 'package:test/test.dart';
 
 import '../utils.dart';
@@ -21,14 +22,14 @@
   List<String> _commandsNotTested = <String>[
     'help', // `dart help help` is redundant
   ];
-  DartdevRunner(['--no-analytics'])
+  DartdevRunner(['--disable-dartdev-analytics'], disabledAnalytics)
       .commands
       .forEach((String commandKey, Command command) {
     if (!_commandsNotTested.contains(commandKey)) {
       test('(help $commandKey == $commandKey --help)', () {
         p = project();
-        var result = p.runSync(['help', commandKey]);
-        var verbHelpResult = p.runSync([commandKey, '--help']);
+        var result = p.runSync('help', [commandKey]);
+        var verbHelpResult = p.runSync(commandKey, ['--help']);
 
         expect(result.stdout, contains(verbHelpResult.stdout));
         expect(result.stderr, contains(verbHelpResult.stderr));
@@ -38,15 +39,15 @@
 
   test('(help pub == pub --help)', () {
     p = project();
-    var result = p.runSync(['help', 'pub']);
-    var pubHelpResult = p.runSync(['pub', '--help']);
+    var result = p.runSync('help', ['pub']);
+    var pubHelpResult = p.runSync('pub', ['--help']);
 
     expect(result.stdout, contains(pubHelpResult.stdout));
     expect(result.stderr, contains(pubHelpResult.stderr));
   });
 
   test('(--help flags also have -h abbr)', () {
-    DartdevRunner(['--no-analytics'])
+    DartdevRunner(['--disable-dartdev-analytics'], disabledAnalytics)
         .commands
         .forEach((String commandKey, Command command) {
       var helpOption = command.argParser.options['help'];
diff --git a/pkg/dartdev/test/commands/migrate_test.dart b/pkg/dartdev/test/commands/migrate_test.dart
index 681d550..8101640 100644
--- a/pkg/dartdev/test/commands/migrate_test.dart
+++ b/pkg/dartdev/test/commands/migrate_test.dart
@@ -21,7 +21,7 @@
 
   test('--help', () {
     p = project();
-    var result = p.runSync(['migrate', '--help']);
+    var result = p.runSync('migrate', ['--help']);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -34,7 +34,7 @@
   test('directory implicit', () {
     p = project(mainSrc: dartVersionFilePrefix2_9 + 'int get foo => 1;\n');
     var result =
-        p.runSync(['migrate', '--no-web-preview'], workingDir: p.dirPath);
+        p.runSync('migrate', ['--no-web-preview'], workingDir: p.dirPath);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Generating migration suggestions'));
@@ -42,7 +42,7 @@
 
   test('directory explicit', () {
     p = project(mainSrc: dartVersionFilePrefix2_9 + 'int get foo => 1;\n');
-    var result = p.runSync(['migrate', '--no-web-preview', p.dirPath]);
+    var result = p.runSync('migrate', ['--no-web-preview', p.dirPath]);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Generating migration suggestions'));
@@ -50,7 +50,7 @@
 
   test('bad directory', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    var result = p.runSync(['migrate', 'foo_bar_dir']);
+    var result = p.runSync('migrate', ['foo_bar_dir']);
     expect(result.exitCode, 1);
     expect(result.stderr, contains('foo_bar_dir does not exist'));
     expect(result.stdout, isEmpty);
@@ -58,7 +58,7 @@
 
   test('pub get needs running', () {
     p = project(mainSrc: 'import "package:foo/foo.dart";\n');
-    var result = p.runSync(['migrate', p.dirPath]);
+    var result = p.runSync('migrate', [p.dirPath]);
     expect(result.exitCode, 1);
     expect(result.stderr, isEmpty);
     expect(result.stdout, runPubGet);
@@ -67,7 +67,7 @@
 
   test('non-pub-related error', () {
     p = project(mainSrc: 'var missing = "semicolon"\n');
-    var result = p.runSync(['migrate', p.dirPath]);
+    var result = p.runSync('migrate', [p.dirPath]);
     expect(result.exitCode, 1);
     expect(result.stderr, isEmpty);
     expect(result.stdout, runPubGet);
diff --git a/pkg/dartdev/test/commands/pub_test.dart b/pkg/dartdev/test/commands/pub_test.dart
index e164858..82f00d0 100644
--- a/pkg/dartdev/test/commands/pub_test.dart
+++ b/pkg/dartdev/test/commands/pub_test.dart
@@ -26,7 +26,7 @@
   }
 
   test('implicit --help', () {
-    final result = project().runSync(['pub']);
+    final result = project().runSync('pub', []);
     expect(result, isNotNull);
     expect(result.exitCode, 64);
     expect(result.stderr, contains('Missing subcommand for "dart pub".'));
@@ -35,17 +35,17 @@
   });
 
   test('--help', () {
-    _assertPubHelpInvoked(project().runSync(['pub', '--help']));
+    _assertPubHelpInvoked(project().runSync('pub', ['--help']));
   });
 
   test('-h', () {
-    _assertPubHelpInvoked(project().runSync(['pub', '-h']));
+    _assertPubHelpInvoked(project().runSync('pub', ['-h']));
   });
 
   test('help cache', () {
     p = project();
-    var result = p.runSync(['help', 'pub', 'cache']);
-    var result2 = p.runSync(['pub', 'cache', '--help']);
+    var result = p.runSync('help', ['pub', 'cache']);
+    var result2 = p.runSync('pub', ['cache', '--help']);
 
     expect(result.exitCode, 0);
 
@@ -58,8 +58,8 @@
 
   test('help publish', () {
     p = project();
-    var result = p.runSync(['help', 'pub', 'publish']);
-    var result2 = p.runSync(['pub', 'publish', '--help']);
+    var result = p.runSync('help', ['pub', 'publish']);
+    var result2 = p.runSync('pub', ['publish', '--help']);
 
     expect(result.exitCode, 0);
 
@@ -77,10 +77,10 @@
         "void main() { int? a; a = null; print('a is \$a.'); }");
 
     // run 'pub get'
-    p.runSync(['pub', 'get']);
+    p.runSync('pub', ['get']);
 
     var result = p.runSync(
-        ['pub', 'run', '--enable-experiment=no-non-nullable', 'main.dart']);
+        'pub', ['run', '--enable-experiment=no-non-nullable', 'main.dart']);
 
     expect(result.exitCode, 254);
     expect(result.stdout, isEmpty);
@@ -93,7 +93,7 @@
 
   test('failure', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    var result = p.runSync(['pub', 'deps']);
+    var result = p.runSync('pub', ['deps']);
     expect(result.exitCode, 65);
     expect(result.stdout, isEmpty);
     expect(result.stderr, contains('No pubspec.lock file found'));
@@ -101,7 +101,7 @@
 
   test('failure unknown option', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    var result = p.runSync(['pub', 'deps', '--foo']);
+    var result = p.runSync('pub', ['deps', '--foo']);
     expect(result.exitCode, 64);
     expect(result.stdout, isEmpty);
     expect(result.stderr, startsWith('Could not find an option named "foo".'));
diff --git a/pkg/dartdev/test/commands/run_test.dart b/pkg/dartdev/test/commands/run_test.dart
index 9fd83e5..324e3f5 100644
--- a/pkg/dartdev/test/commands/run_test.dart
+++ b/pkg/dartdev/test/commands/run_test.dart
@@ -20,7 +20,7 @@
 
   test('--help', () {
     p = project();
-    var result = p.runSync(['run', '--help']);
+    var result = p.runSync('run', ['--help']);
 
     expect(result.stdout, contains('Run a Dart program.'));
     expect(result.stdout, contains('Debugging options:'));
@@ -30,7 +30,7 @@
 
   test("'Hello World'", () {
     p = project(mainSrc: "void main() { print('Hello World'); }");
-    ProcessResult result = p.runSync(['run', p.relativeFilePath]);
+    ProcessResult result = p.runSync('run', [p.relativeFilePath]);
 
     expect(result.stdout, contains('Hello World'));
     expect(result.stderr, isEmpty);
@@ -40,7 +40,7 @@
   test('no such file', () {
     p = project(mainSrc: "void main() { print('Hello World'); }");
     ProcessResult result =
-        p.runSync(['run', 'no/such/file/${p.relativeFilePath}']);
+        p.runSync('run', ['no/such/file/${p.relativeFilePath}']);
 
     expect(result.stderr, isNotEmpty);
     expect(result.exitCode, isNot(0));
@@ -51,7 +51,7 @@
     // name (package name) will be the name of the temporary directory on disk
     p = project(mainSrc: "void main() { print('Hello World'); }");
     p.file('bin/main.dart', "void main() { print('Hello main.dart'); }");
-    ProcessResult result = p.runSync(['run']);
+    ProcessResult result = p.runSync('run', []);
 
     expect(result.stdout, contains('Hello main.dart'));
     expect(result.stderr, isEmpty);
@@ -62,7 +62,7 @@
   test('missing implicit packageName.dart', () {
     p = project(mainSrc: "void main() { print('Hello World'); }");
     p.file('bin/foo.dart', "void main() { print('Hello main.dart'); }");
-    ProcessResult result = p.runSync(['run']);
+    ProcessResult result = p.runSync('run', []);
 
     expect(result.stdout, isEmpty);
     expect(
@@ -75,8 +75,7 @@
   test('arguments are properly passed', () {
     p = project();
     p.file('main.dart', 'void main(args) { print(args); }');
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       '--enable-experiment=triple-shift',
       'main.dart',
       'argument1',
@@ -94,8 +93,7 @@
     p.file('main.dart', 'void main(args) { print(args); }');
     // Test with absolute path
     final name = path.join(p.dirPath, 'main.dart');
-    final result = p.runSync([
-      'run',
+    final result = p.runSync('run', [
       '--enable-experiment=triple-shift',
       name,
       '--argument1',
@@ -113,8 +111,7 @@
     p.file('main.dart', 'void main(args) { print(args); }');
     // Test with File uri
     final name = path.join(p.dirPath, 'main.dart');
-    final result = p.runSync([
-      'run',
+    final result = p.runSync('run', [
       Uri.file(name).toString(),
       '--argument1',
       'argument2',
@@ -137,8 +134,7 @@
     //
     // This test ensures that allowed arguments for dart run which are valid VM
     // arguments are properly handled by the VM.
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       '--observe',
       '--pause-isolates-on-start',
       // This should negate the above flag.
@@ -157,8 +153,7 @@
     expect(result.exitCode, 0);
 
     // Again, with --disable-service-auth-codes.
-    result = p.runSync([
-      'run',
+    result = p.runSync('run', [
       '--observe',
       '--pause-isolates-on-start',
       // This should negate the above flag.
@@ -183,8 +178,7 @@
 
     // Any VM flags not listed under 'dart run help --verbose' should be passed
     // before a dartdev command.
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       '--vm-name=foo',
       p.relativeFilePath,
     ]);
@@ -202,8 +196,7 @@
 
     // Any VM flags not listed under 'dart run help --verbose' should be passed
     // before a dartdev command.
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       '--verbose_gc',
       p.relativeFilePath,
     ]);
@@ -221,8 +214,7 @@
 
     // Ensure --enable-asserts doesn't cause the dartdev isolate to fail to
     // load. Regression test for: https://github.com/dart-lang/sdk/issues/42831
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       '--enable-asserts',
       p.relativeFilePath,
     ]);
@@ -236,8 +228,7 @@
     p = project(mainSrc: 'void main() { assert(false); }');
 
     // Any VM flags passed after the script shouldn't be interpreted by the VM.
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       p.relativeFilePath,
       '--enable-asserts',
     ]);
diff --git a/pkg/dartdev/test/commands/test_test.dart b/pkg/dartdev/test/commands/test_test.dart
index 00be2bc..ff85982 100644
--- a/pkg/dartdev/test/commands/test_test.dart
+++ b/pkg/dartdev/test/commands/test_test.dart
@@ -21,7 +21,7 @@
   test('--help', () {
     p = project();
 
-    final result = p.runSync(['test', '--help']);
+    final result = p.runSync('test', ['--help']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, contains(' tests in this package'));
@@ -31,7 +31,7 @@
   test('dart help test', () {
     p = project();
 
-    final result = p.runSync(['help', 'test']);
+    final result = p.runSync('help', ['test']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, contains(' tests in this package'));
@@ -43,7 +43,7 @@
     var pubspec = File(path.join(p.dirPath, 'pubspec.yaml'));
     pubspec.deleteSync();
 
-    var result = p.runSync(['test']);
+    var result = p.runSync('test', []);
 
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('No pubspec.yaml file found'));
@@ -63,7 +63,7 @@
 ''');
 
     // An implicit `pub get` will happen.
-    final result = p.runSync(['test', '--no-color', '--reporter', 'expanded']);
+    final result = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('All tests passed!'));
     expect(result.exitCode, 0);
@@ -86,8 +86,7 @@
 }
 ''');
 
-    final result = p.runSync(['test']);
-    expect(result.exitCode, 65);
+    final result = p.runSync('test', []);
     expect(
       result.stdout,
       contains('You need to add a dependency on package:test'),
@@ -95,10 +94,10 @@
     expect(result.stderr, isEmpty);
     expect(result.exitCode, 65);
 
-    final resultPubAdd = p.runSync(['pub', 'add', 'test']);
+    final resultPubAdd = p.runSync('pub', ['add', 'test']);
 
     expect(resultPubAdd.exitCode, 0);
-    final result2 = p.runSync(['test', '--no-color', '--reporter', 'expanded']);
+    final result2 = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
     expect(result2.stderr, isEmpty);
     expect(result2.stdout, contains('All tests passed!'));
     expect(result2.exitCode, 0);
@@ -118,7 +117,7 @@
 }
 ''');
 
-    final result = p.runSync(['test', '--no-color', '--reporter', 'expanded']);
+    final result = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
     expect(result.exitCode, 0);
     expect(result.stdout, contains('All tests passed!'));
     expect(result.stderr, isEmpty);
@@ -139,13 +138,8 @@
 ''');
 
     final result = p.runSync(
-      [
-        '--enable-experiment=non-nullable',
-        'test',
-        '--no-color',
-        '--reporter',
-        'expanded',
-      ],
+      '--enable-experiment=non-nullable',
+      ['test', '--no-color', '--reporter', 'expanded'],
     );
     expect(result.exitCode, 1);
   });
diff --git a/pkg/dartdev/test/utils.dart b/pkg/dartdev/test/utils.dart
index 08910be..bbc46df 100644
--- a/pkg/dartdev/test/utils.dart
+++ b/pkg/dartdev/test/utils.dart
@@ -71,14 +71,16 @@
   }
 
   ProcessResult runSync(
+    String command,
     List<String> args, {
     String workingDir,
   }) {
     var arguments = [
-      '--no-analytics',
+      command,
       ...?args,
     ];
 
+    arguments.add('--disable-dartdev-analytics');
     return Process.runSync(Platform.resolvedExecutable, arguments,
         workingDirectory: workingDir ?? dir.path,
         environment: {if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true'});
diff --git a/pkg/dartdev/test/utils_test.dart b/pkg/dartdev/test/utils_test.dart
index 199d461..9740dd1 100644
--- a/pkg/dartdev/test/utils_test.dart
+++ b/pkg/dartdev/test/utils_test.dart
@@ -100,6 +100,39 @@
       expect(File('bar.bart').name, 'bar.bart');
     });
   });
+
+  group('PubUtils', () {
+    test('doModifyArgs', () {
+      const allCmds = ['analyze', 'help', 'pub', 'migrate'];
+      expect(PubUtils.shouldModifyArgs(null, null), isFalse);
+      expect(PubUtils.shouldModifyArgs([], null), isFalse);
+      expect(PubUtils.shouldModifyArgs(null, []), isFalse);
+      expect(PubUtils.shouldModifyArgs([], []), isFalse);
+      expect(PubUtils.shouldModifyArgs(['-h'], allCmds), isFalse);
+      expect(PubUtils.shouldModifyArgs(['--help'], allCmds), isFalse);
+      expect(PubUtils.shouldModifyArgs(['help'], allCmds), isFalse);
+      expect(PubUtils.shouldModifyArgs(['pub'], allCmds), isFalse);
+      expect(PubUtils.shouldModifyArgs(['analyze', 'help', 'pub'], allCmds),
+          isFalse);
+
+      expect(PubUtils.shouldModifyArgs(['--some-flag', 'help', 'pub'], allCmds),
+          isTrue);
+      expect(PubUtils.shouldModifyArgs(['help', 'pub'], allCmds), isTrue);
+      expect(PubUtils.shouldModifyArgs(['help', 'pub', 'publish'], allCmds),
+          isTrue);
+      expect(PubUtils.shouldModifyArgs(['help', 'pub', 'analyze'], allCmds),
+          isTrue);
+    });
+
+    test('modifyArgs', () {
+      expect(PubUtils.modifyArgs(['--some-flag', 'help', 'pub']),
+          orderedEquals(['--some-flag', 'pub', '--help']));
+      expect(PubUtils.modifyArgs(['help', 'pub']),
+          orderedEquals(['pub', '--help']));
+      expect(PubUtils.modifyArgs(['help', 'pub', 'publish']),
+          orderedEquals(['pub', 'publish', '--help']));
+    });
+  });
 }
 
 const String _packageData = '''{
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index 9bf97f5..db90835 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -640,7 +640,10 @@
 ''');
       }
       logger.stderr('https://github.com/dart-lang/sdk/issues/new');
+      var sdkVersion = Platform.version.split(' ')[0];
       logger.stderr('''
+Please include the SDK version ($sdkVersion) in your bug report.
+
 To attempt to perform migration anyway, you may re-run with
 --${CommandLineOptions.ignoreExceptionsFlag}.
 
@@ -704,6 +707,9 @@
         if (!options.ignoreErrors) {
           throw MigrationExit(1);
         }
+      } else if (analysisResult.allSourcesAlreadyMigrated) {
+        _logAlreadyMigrated();
+        throw MigrationExit(0);
       } else {
         logger.stdout('No analysis issues found.');
       }
@@ -750,6 +756,9 @@
 Use this interactive web view to review, improve, or apply the results.
 When finished with the preview, hit ctrl-c to terminate this process.
 
+If you make edits outside of the web view (in your IDE), use the 'Rerun from
+sources' action.
+
 ''');
 
       // Block until sigint (ctrl-c).
@@ -854,6 +863,10 @@
     }
   }
 
+  void _logAlreadyMigrated() {
+    logger.stdout(migratedAlready);
+  }
+
   void _logErrors(AnalysisResult analysisResult) {
     logger.stdout('');
 
@@ -919,6 +932,15 @@
           _fixCodeProcessor._task.instrumentationListener,
           {},
           analysisResult);
+    } else if (analysisResult.allSourcesAlreadyMigrated) {
+      _logAlreadyMigrated();
+      return MigrationState(
+          _fixCodeProcessor._task.migration,
+          _fixCodeProcessor._task.includedRoot,
+          _dartFixListener,
+          _fixCodeProcessor._task.instrumentationListener,
+          {},
+          analysisResult);
     } else {
       logger.stdout(ansi.emphasized('Re-generating migration suggestions...'));
       return await _fixCodeProcessor.runLaterPhases();
diff --git a/pkg/nnbd_migration/lib/src/exceptions.dart b/pkg/nnbd_migration/lib/src/exceptions.dart
index 580fcda..54ca2ba 100644
--- a/pkg/nnbd_migration/lib/src/exceptions.dart
+++ b/pkg/nnbd_migration/lib/src/exceptions.dart
@@ -4,9 +4,6 @@
 /// A [StateError] specific to the ways that the NNBD experiment can be
 /// misconfigured which may prevent the tool from working.
 class ExperimentStatusException extends StateError {
-  /// All files included in the migration dir have already been migrated.
-  ExperimentStatusException.migratedAlready() : super(migratedAlready);
-
   /// The SDK was analyzed without NNBD semantics.
   ExperimentStatusException.sdkExperimentDisabled() : super(nnbdExperimentOff);
 
diff --git a/pkg/nnbd_migration/lib/src/messages.dart b/pkg/nnbd_migration/lib/src/messages.dart
index 3f9b048..4202a47 100644
--- a/pkg/nnbd_migration/lib/src/messages.dart
+++ b/pkg/nnbd_migration/lib/src/messages.dart
@@ -5,15 +5,13 @@
 import 'package:nnbd_migration/migration_cli.dart';
 
 const String migratedAlready =
-    "Seem to be migrating code that's already migrated";
+    'All sources appear to be already migrated.  Nothing to do.';
 const String nnbdExperimentOff =
     'Analyzer seems to need the nnbd experiment on in the SDK.';
 const String sdkNnbdOff = 'Analysis seems to have an SDK without NNBD enabled.';
 const String sdkPathEnvironmentVariableSet =
     r'Note: $SDK_PATH environment variable is set and may point to outdated '
     'dart:core sources';
-const String _skipImportCheckFlag =
-    '--${CommandLineOptions.skipImportCheckFlag}';
 const String unmigratedDependenciesWarning = '''
 Warning: package has unmigrated dependencies.
 
@@ -21,6 +19,8 @@
 list of the unmigrated dependencies, re-run without the `$_skipImportCheckFlag`
 flag.
 ''';
+const String _skipImportCheckFlag =
+    '--${CommandLineOptions.skipImportCheckFlag}';
 
 String unmigratedDependenciesError(List<String> uris) => '''
 Error: package has unmigrated dependencies.
diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
index 6c89f59..69bde9d 100644
--- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
@@ -191,12 +191,6 @@
   }
 
   Map<String, Version> finish() {
-    if (!_propagated) {
-      // [finalizeInput] sets this field to `true`, so if it's still false, that
-      // means it was never called; this probably means that all the code fed
-      // to the migration tool was already migrated.
-      throw ExperimentStatusException.migratedAlready();
-    }
     _postmortemFileWriter?.write();
     _instrumentation?.finished();
     return _neededPackages;
diff --git a/pkg/nnbd_migration/pubspec.yaml b/pkg/nnbd_migration/pubspec.yaml
index 59072b4..7917b78 100644
--- a/pkg/nnbd_migration/pubspec.yaml
+++ b/pkg/nnbd_migration/pubspec.yaml
@@ -21,8 +21,8 @@
   yaml: ^2.1.15
 
 dev_dependencies:
-  analysis_tool:
-    path: ../analysis_tool
+  analyzer_utilities:
+    path: ../analyzer_utilities
   http: '>=0.11.3+17 <0.13.0'
   pedantic: ^1.9.0
   test: ^1.6.4
diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart
index 9648403..3dc5556 100644
--- a/pkg/nnbd_migration/test/migration_cli_test.dart
+++ b/pkg/nnbd_migration/test/migration_cli_test.dart
@@ -243,13 +243,13 @@
 
   Future<String> assertErrorExit(
       MigrationCliRunner cliRunner, FutureOr<void> Function() callback,
-      {@required bool withUsage, dynamic expectedExitCode = anything}) async {
+      {@required bool withUsage, dynamic expectedExitCode}) async {
+    expectedExitCode ??= isNot(0);
     try {
       await callback();
       fail('Migration succeeded; expected it to abort with an error');
     } on MigrationExit catch (migrationExit) {
       expect(migrationExit.exitCode, isNotNull);
-      expect(migrationExit.exitCode, isNot(0));
       expect(migrationExit.exitCode, expectedExitCode);
     }
     expect(cliRunner.isPreviewServerRunning, isFalse);
@@ -313,7 +313,8 @@
   Future<String> assertRunFailure(List<String> args,
       {MigrationCli cli,
       bool withUsage = false,
-      dynamic expectedExitCode = anything}) async {
+      dynamic expectedExitCode}) async {
+    expectedExitCode ??= isNot(0);
     cli ??= _createCli();
     MigrationCliRunner cliRunner;
     try {
@@ -321,7 +322,6 @@
           cli.decodeCommandLineArgs(MigrationCli.createParser().parse(args));
     } on MigrationExit catch (e) {
       expect(e.exitCode, isNotNull);
-      expect(e.exitCode, isNot(0));
       expect(e.exitCode, expectedExitCode);
       return assertStderr(withUsage: withUsage);
     }
@@ -684,6 +684,11 @@
     expect(
         errorOutput, isNot(contains('try to fix errors in the source code')));
     expect(errorOutput, contains('re-run with\n--ignore-exceptions'));
+    expect(errorOutput, contains('consider filing a bug report'));
+    expect(
+        errorOutput,
+        contains(
+            RegExp(r'Please include the SDK version \([0-9]+\.[0-9]+\..*\)')));
   }
 
   test_lifecycle_exception_handling_ignore() async {
@@ -836,6 +841,15 @@
     expect(output, contains('Please fix the analysis issues'));
   }
 
+  test_lifecycle_migration_already_performed() async {
+    var projectContents = simpleProject(migrated: true);
+    var projectDir = createProjectDir(projectContents);
+    await assertRunFailure([projectDir], expectedExitCode: 0);
+    var output = logger.stdoutBuffer.toString();
+    expect(output,
+        contains('All sources appear to be already migrated.  Nothing to do.'));
+  }
+
   test_lifecycle_no_preview() async {
     var projectContents = simpleProject();
     var projectDir = createProjectDir(projectContents);
diff --git a/pkg/nnbd_migration/test/verify_tests_test.dart b/pkg/nnbd_migration/test/verify_tests_test.dart
index af19088..654c634 100644
--- a/pkg/nnbd_migration/test/verify_tests_test.dart
+++ b/pkg/nnbd_migration/test/verify_tests_test.dart
@@ -2,8 +2,8 @@
 // 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.
 
-import 'package:analysis_tool/package_root.dart' as package_root;
-import 'package:analysis_tool/verify_tests.dart';
+import 'package:analyzer_utilities/package_root.dart' as package_root;
+import 'package:analyzer_utilities/verify_tests.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
 
 void main() {
diff --git a/pkg/nnbd_migration/tool/steamroll_ecosystem.sh b/pkg/nnbd_migration/tool/steamroll_ecosystem.sh
index 9af3926..0c6374b 100755
--- a/pkg/nnbd_migration/tool/steamroll_ecosystem.sh
+++ b/pkg/nnbd_migration/tool/steamroll_ecosystem.sh
@@ -276,8 +276,8 @@
   case "${package_name}" in
     _fe_analyzer_shared) repo=sdk
       make_clone_from_package _fe_analyzer_shared "${repo}" master pkg/_fe_analyzer_shared ;;
-    analysis_tool) repo=sdk
-      make_clone_from_package analysis_tool "${repo}" master pkg/analysis_tool ;;
+    analyzer_utilities) repo=sdk
+      make_clone_from_package analyzer_utilities "${repo}" master pkg/analyzer_utilities ;;
     analyzer) repo=sdk
       make_clone_from_package analyzer "${repo}" master pkg/analyzer ;;
     build) repo=build
@@ -379,7 +379,7 @@
     # those here.
     case "${package_name}" in
       analyzer)
-        add_package_to_workspace "analysis_tool"
+        add_package_to_workspace "analyzer_utilities"
         ;;
     esac
     if [ -n "${NO_UPDATE}" ] || repo_changed_this_run "${repo}" ; then
@@ -447,4 +447,4 @@
 }
 
 
-main "$@"
\ No newline at end of file
+main "$@"
diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc
index dedd3bc..452346f 100644
--- a/runtime/bin/main_options.cc
+++ b/runtime/bin/main_options.cc
@@ -387,7 +387,6 @@
 
   bool enable_dartdev_analytics = false;
   bool disable_dartdev_analytics = false;
-  bool no_dartdev_analytics = false;
 
   // Parse out the vm options.
   while (i < argc) {
@@ -406,14 +405,12 @@
       const char* kVerboseDebug1 = "--verbose_debug";
       const char* kVerboseDebug2 = "--verbose-debug";
 
-      // The following flags are processed as DartDev flags and are not to
+      // The following two flags are processed as DartDev flags and are not to
       // be treated as if they are VM flags.
       const char* kEnableDartDevAnalytics1 = "--enable-analytics";
       const char* kEnableDartDevAnalytics2 = "--enable_analytics";
       const char* kDisableDartDevAnalytics1 = "--disable-analytics";
       const char* kDisableDartDevAnalytics2 = "--disable_analytics";
-      const char* kNoDartDevAnalytics1 = "--no-analytics";
-      const char* kNoDartDevAnalytics2 = "--no_analytics";
 
       if ((strncmp(argv[i], kPrintFlags1, strlen(kPrintFlags1)) == 0) ||
           (strncmp(argv[i], kPrintFlags2, strlen(kPrintFlags2)) == 0)) {
@@ -429,12 +426,6 @@
                           strlen(kEnableDartDevAnalytics2)) == 0)) {
         enable_dartdev_analytics = true;
         skipVmOption = true;
-      } else if ((strncmp(argv[i], kNoDartDevAnalytics1,
-                          strlen(kNoDartDevAnalytics1)) == 0) ||
-                 (strncmp(argv[i], kNoDartDevAnalytics2,
-                          strlen(kNoDartDevAnalytics2)) == 0)) {
-        no_dartdev_analytics = true;
-        skipVmOption = true;
       } else if ((strncmp(argv[i], kDisableDartDevAnalytics1,
                           strlen(kDisableDartDevAnalytics1)) == 0) ||
                  (strncmp(argv[i], kDisableDartDevAnalytics2,
@@ -527,9 +518,6 @@
     if (disable_dartdev_analytics) {
       dart_options->AddArgument("--disable-analytics");
     }
-    if (no_dartdev_analytics) {
-      dart_options->AddArgument("--no-analytics");
-    }
     return 0;
   }
 
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 556b548..e6ff59a 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -303,8 +303,11 @@
     for (intptr_t i = 0; i < num_types; i++) {
       type_parameter ^= type_parameters.TypeAt(i);
       if (!type_parameter.IsFinalized()) {
-        type_parameter.set_index(type_parameter.index() + offset);
+        ASSERT(type_parameter.index() == i);
+        type_parameter.set_index(offset + i);
         type_parameter.SetIsFinalized();
+      } else {
+        ASSERT(type_parameter.index() == offset + i);
       }
       // The declaration of a type parameter is canonical.
       ASSERT(type_parameter.IsDeclaration());
@@ -821,8 +824,11 @@
     for (intptr_t i = 0; i < num_type_params; i++) {
       type_param ^= type_params.TypeAt(i);
       if (!type_param.IsFinalized()) {
+        ASSERT(type_param.index() == i);
         type_param.set_index(num_parent_type_params + i);
         type_param.SetIsFinalized();
+      } else {
+        ASSERT(type_param.index() == num_parent_type_params + i);
       }
       // The declaration of a type parameter is canonical.
       ASSERT(type_param.IsDeclaration());
diff --git a/runtime/vm/heap/heap_test.cc b/runtime/vm/heap/heap_test.cc
index 4b3b061..b2c1009 100644
--- a/runtime/vm/heap/heap_test.cc
+++ b/runtime/vm/heap/heap_test.cc
@@ -583,6 +583,9 @@
 };
 
 VM_UNIT_TEST_CASE(CleanupBequestNeverReceived) {
+  // This test uses features from isolate groups
+  FLAG_enable_isolate_groups = true;
+
   const char* TEST_MESSAGE = "hello, world";
   Dart_Isolate parent = TestCase::CreateTestIsolate("parent");
   EXPECT_EQ(parent, Dart_CurrentIsolate());
@@ -615,6 +618,9 @@
 }
 
 VM_UNIT_TEST_CASE(ReceivesSendAndExitMessage) {
+  // This test uses features from isolate groups
+  FLAG_enable_isolate_groups = true;
+
   const char* TEST_MESSAGE = "hello, world";
   Dart_Isolate parent = TestCase::CreateTestIsolate("parent");
   EXPECT_EQ(parent, Dart_CurrentIsolate());
diff --git a/runtime/vm/heap/verifier.cc b/runtime/vm/heap/verifier.cc
index 4c2da50..4dc7da0 100644
--- a/runtime/vm/heap/verifier.cc
+++ b/runtime/vm/heap/verifier.cc
@@ -89,11 +89,22 @@
     : thread_(thread), instanceHandle_(Instance::Handle(thread->zone())) {}
 
 void VerifyCanonicalVisitor::VisitObject(ObjectPtr obj) {
+  // The caller of this function is walking heap pages using the
+  // ExclusivePageIterator - which holds the pages lock.
+  //
+  // If we allow handle verification, then any assignment to a handle will call
+  // `heap()->Contains()` for heap objects, which in return is implemented by
+  // walking pages using ExclusivePageIterator, which can cause a deadlock.
+  //
+  // Therefore we disable the handle verification here.
+  const bool old_verify_flag = FLAG_verify_handles;
+  FLAG_verify_handles = false;
+
   // TODO(dartbug.com/36097): The heap walk can encounter canonical objects of
   // other isolates. We should either scan live objects from the roots of each
   // individual isolate, or wait until we are ready to share constants across
   // isolates.
-  if (!FLAG_enable_isolate_groups) {
+  if (!FLAG_enable_isolate_groups || FLAG_precompiled_mode) {
     if ((obj->GetClassId() >= kInstanceCid) &&
         (obj->GetClassId() != kTypeArgumentsCid)) {
       if (obj->ptr()->IsCanonical()) {
@@ -107,6 +118,7 @@
       }
     }
   }
+  FLAG_verify_handles = old_verify_flag;
 }
 #endif  // defined(DEBUG)
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 82605a8..ea79c98 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -8326,6 +8326,7 @@
   // Note that parent pointers in newly instantiated signatures still points to
   // the original uninstantiated parent signatures. That is not a problem.
   const Function& parent = Function::Handle(zone, parent_function());
+  const intptr_t num_parent_type_params = NumParentTypeParameters();
 
   // See the comment on kCurrentAndEnclosingFree to understand why we don't
   // adjust 'num_free_fun_type_params' downward in this case.
@@ -8345,7 +8346,6 @@
     if (IsGeneric() || HasGenericParent()) {
       // We only consider the function type parameters declared by the parents
       // of this signature function as free.
-      const int num_parent_type_params = NumParentTypeParameters();
       if (num_parent_type_params < num_free_fun_type_params) {
         num_free_fun_type_params = num_parent_type_params;
       }
@@ -8354,6 +8354,8 @@
 
   Function& sig = Function::Handle(Function::NewSignatureFunction(
       owner, parent, TokenPosition::kNoSource, space));
+  const intptr_t offset =
+      sig.NumParentTypeParameters() - num_parent_type_params;
   AbstractType& type = AbstractType::Handle(zone);
 
   // Copy the type parameters and instantiate their bounds (if necessary).
@@ -8363,10 +8365,11 @@
     if (!type_params.IsNull()) {
       TypeArguments& instantiated_type_params = TypeArguments::Handle(zone);
       TypeParameter& type_param = TypeParameter::Handle(zone);
-      Class& cls = Class::Handle(zone);
+      const Class& null_class = Class::Handle(zone);
       String& param_name = String::Handle(zone);
       for (intptr_t i = 0; i < type_params.Length(); ++i) {
         type_param ^= type_params.TypeAt(i);
+        ASSERT(type_param.index() == num_parent_type_params + i);
         type = type_param.bound();
         if (!type.IsInstantiated(kAny, num_free_fun_type_params)) {
           type = type.InstantiateFrom(instantiator_type_arguments,
@@ -8377,12 +8380,14 @@
           if (type.IsNull()) {
             return Function::null();
           }
-          cls = type_param.parameterized_class();
+        }
+        if (offset > 0 || type.raw() != type_param.bound()) {
           param_name = type_param.name();
+          ASSERT(type_param.IsFunctionTypeParameter());
           ASSERT(type_param.IsFinalized());
           ASSERT(type_param.IsCanonical());
           type_param = TypeParameter::New(
-              cls, sig, type_param.index(), param_name, type,
+              null_class, sig, type_param.index() + offset, param_name, type,
               type_param.IsGenericCovariantImpl(), type_param.nullability(),
               type_param.token_pos());
           type_param.SetIsFinalized();
@@ -8838,6 +8843,9 @@
 
   // Set closure function's type parameters.
   auto& type_args_handle = TypeArguments::Handle(zone, type_parameters());
+  // This function cannot be local, therefore it has no generic parent.
+  // Its implicit closure function therefore has no generic parent function
+  // either. That is why it is safe to simply copy the type parameters.
   closure_function.set_type_parameters(type_args_handle);
   closure_function.UpdateCachedDefaultTypeArguments(thread);
 
@@ -20757,6 +20765,7 @@
     }
     object_store->set_canonical_type_parameters(table.Release());
   }
+  ASSERT(!type_parameter.IsDeclaration());
   return type_parameter.raw();
 }
 
@@ -20771,8 +20780,10 @@
   const TypeArguments& type_params = TypeArguments::Handle(
       zone, cls.IsNull() ? function.type_parameters() : cls.type_parameters());
   const intptr_t offset =
-      cls.IsNull() ? function.NumParentTypeParameters()
-                   : (cls.NumTypeArguments() - cls.NumTypeParameters());
+      IsFinalized()
+          ? (cls.IsNull() ? function.NumParentTypeParameters()
+                          : (cls.NumTypeArguments() - cls.NumTypeParameters()))
+          : 0;
   TypeParameter& type_parameter = TypeParameter::Handle(zone);
   type_parameter ^= type_params.TypeAt(index() - offset);
   ASSERT(!type_parameter.IsNull());
diff --git a/runtime/vm/thread_test.cc b/runtime/vm/thread_test.cc
index 048cda9..aaedbb7 100644
--- a/runtime/vm/thread_test.cc
+++ b/runtime/vm/thread_test.cc
@@ -296,7 +296,9 @@
   bool wait = true;
 
   EXPECT(isolate->heap()->GrowthControlState());
-  isolate->heap()->DisableGrowthControl();
+
+  NoHeapGrowthControlScope no_heap_growth_scope;
+
   for (intptr_t i = 0; i < kTaskCount; i++) {
     Dart::thread_pool()->Run<SimpleTaskWithZoneAllocation>(
         (i + 1), isolate, &threads[i], &sync, &monitor, &done_count, &wait);
diff --git a/tools/VERSION b/tools/VERSION
index 2193d6e..a76b3d2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 40
+PRERELEASE 41
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/find_base_commit.dart b/tools/bots/find_base_commit.dart
index 2a75eca..5e3c9e2 100755
--- a/tools/bots/find_base_commit.dart
+++ b/tools/bots/find_base_commit.dart
@@ -3,9 +3,7 @@
 // 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.
 
-// Find the newest commit that has a full set of results on the bots.
-
-// @dart = 2.9
+// Find the newest commit that has a full set of results on the builders.
 
 import 'dart:async';
 import 'dart:convert';
@@ -17,13 +15,13 @@
 
 void main(List<String> args) async {
   final parser = new ArgParser();
-  parser.addMultiOption("bot",
+  parser.addMultiOption("builder",
       abbr: "b",
-      help: "Select the bots matching the glob pattern [option is repeatable]",
+      help: "Select the builders matching the glob [option is repeatable]",
       splitCommas: false);
   parser.addOption("branch",
       abbr: "B",
-      help: "Select the bots building this branch",
+      help: "Select the builders building this branch",
       defaultsTo: "master");
   parser.addOption("count",
       abbr: "c", help: "List this many commits", defaultsTo: "1");
@@ -33,7 +31,7 @@
   if (options["help"]) {
     print("""
 Usage: find_base_commit.dart [OPTION]...
-Find the newest commit that has a full set of results on the bots.
+Find the newest commit that has a full set of results on the builders.
 
 The options are as follows:
 
@@ -43,34 +41,50 @@
 
   int count = int.parse(options["count"]);
   final globs = new List<Glob>.from(
-      options["bot"].map((String pattern) => new Glob(pattern)));
+      options["builder"].map((String pattern) => new Glob(pattern)));
 
   // Download the most recent builds from buildbucket.
-  int maxBuilds = 1000;
-  final url = Uri.parse(
-      "https://cr-buildbucket.appspot.com/_ah/api/buildbucket/v1/search"
-      "?bucket=luci.dart.ci.sandbox"
-      "&max_builds=$maxBuilds"
-      "&status=COMPLETED"
-      "&fields=builds(url%2Cparameters_json)");
+  const maxBuilds = 1000;
+  final url = Uri.parse("https://cr-buildbucket.appspot.com"
+      "/prpc/buildbucket.v2.Builds/SearchBuilds");
   const maxRetries = 3;
   const timeout = const Duration(seconds: 30);
-  Map<String, dynamic> object;
+  final query = jsonEncode({
+    "predicate": {
+      "builder": {"project": "dart", "bucket": "ci.sandbox"},
+      "status": "ENDED_MASK"
+    },
+    "pageSize": maxBuilds,
+    "fields": "builds.*.builder.builder,builds.*.input"
+  });
+  late Map<String, dynamic> searchResult;
   for (int i = 1; i <= maxRetries; i++) {
     try {
       final client = new HttpClient();
-      final request = await client.getUrl(url).timeout(timeout);
+      final request = await client.postUrl(url).timeout(timeout)
+        ..headers.contentType = ContentType.json
+        ..headers.add(HttpHeaders.acceptHeader, ContentType.json)
+        ..write(query);
       final response = await request.close().timeout(timeout);
-      object = await response
+      if (response.statusCode != 200) {
+        print("Failed to search for builds: "
+            "${response.statusCode}:${response.reasonPhrase}");
+        exit(1);
+      }
+      const prefix = ")]}'";
+      searchResult = await (response
           .cast<List<int>>()
           .transform(new Utf8Decoder())
+          .map((event) =>
+              event.startsWith(prefix) ? event.substring(prefix.length) : event)
           .transform(new JsonDecoder())
+          .cast<Map<String, dynamic>>()
           .first
-          .timeout(timeout);
+          .timeout(timeout));
       client.close();
       break;
     } on TimeoutException catch (e) {
-      final inSeconds = e.duration.inSeconds;
+      final inSeconds = e.duration?.inSeconds;
       stderr.writeln(
           "Attempt $i of $maxRetries timed out after $inSeconds seconds");
       if (i == maxRetries) {
@@ -81,57 +95,61 @@
   }
 
   // Locate the builds we're interested in and map them to each commit. The
-  // builds returned by the API are sorted with the newest first. Since bots
+  // builds returned by the API are sorted with the newest first. Since builders
   // don't build back in time and always build the latest commit whenever they
   // can, the first time we see a commit, we know it's newer than all commits
-  // we haven't seen yet. The insertion order into the botsForCommits map will
-  // then sorted with the newest commit first.
-  final builds = object["builds"];
-  final botsForCommits = <String, Set<String>>{};
+  // we haven't seen yet. The insertion order into the buildersForCommits map
+  // will then sorted with the newest commit first.
+  final builds = searchResult["builds"];
+  if (builds == null) {
+    print("No builds found");
+    exit(1);
+  }
+  final buildersForCommits = <String, Set<String>>{};
   for (final build in builds) {
-    final parameters = jsonDecode(build["parameters_json"]);
-    final bot = parameters["builder_name"];
-    if (bot.endsWith("-beta") ||
-        bot.endsWith("-dev") ||
-        bot.endsWith("-stable")) {
+    final builder = build["builder"]?["builder"];
+    if (builder is! String ||
+        builder.endsWith("-beta") ||
+        builder.endsWith("-dev") ||
+        builder.endsWith("-stable")) {
       // Ignore the release builders. The -try builders aren't in the
       // bucket we're reading.
       continue;
     }
-    if (globs.isNotEmpty && !globs.any((glob) => glob.matches(bot))) {
-      // Filter way bots we're not interested in.
+    if (globs.isNotEmpty && !globs.any((glob) => glob.matches(builder))) {
+      // Filter way builders we're not interested in.
       continue;
     }
-    final properties = parameters["properties"];
-    final branch = properties["branch"];
-    if (branch != null && branch != "refs/heads/${options['branch']}") {
-      // Ignore bots that are building the wrong branch.
+    final input = build["input"]?["gitilesCommit"];
+    if (input == null) {
+      // Ignore builds not triggered by a commit, e.g. fuzz-linux.
       continue;
     }
-    final commit = properties["revision"];
-    if (commit == null) {
-      // Ignore bots that aren't commit based, e.g. fuzz-linux.
+    final ref = input["ref"];
+    if (ref != "refs/heads/${options['branch']}") {
+      // Ignore builds on the wrong branch.
       continue;
     }
-    final botsForCommit =
-        botsForCommits.putIfAbsent(commit, () => new Set<String>());
-    botsForCommit.add(bot);
+    final commit = input["id"] as String;
+    final buildersForCommit =
+        buildersForCommits.putIfAbsent(commit, () => new Set<String>());
+    buildersForCommit.add(builder);
   }
 
-  if (botsForCommits.isEmpty) {
-    print("Failed to locate any commits having run on the bots");
+  if (buildersForCommits.isEmpty) {
+    print("Failed to locate any commits having run on the builders");
     exitCode = 1;
     return;
   }
 
   int maxBots = 0;
-  for (final commit in botsForCommits.keys) {
-    maxBots = max(maxBots, botsForCommits[commit].length);
+  for (final builders in buildersForCommits.values) {
+    maxBots = max(maxBots, builders.length);
   }
 
-  // List commits run on the most bots.
-  for (final commit in botsForCommits.keys
-      .where((commit) => botsForCommits[commit].length == maxBots)
+  // List commits run on the most builders.
+  for (final commit in buildersForCommits.keys
+      .where((commit) => buildersForCommits[commit]!.length == maxBots)
       .take(count)) {
     print(commit);
   }