Version 2.17.0-266.1.beta

Merge '2.17.0-266.0.dev' into beta
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 51bc8f5..d7bbb38 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -326,7 +326,7 @@
       "name": "frontend_server",
       "rootUri": "../pkg/frontend_server",
       "packageUri": "lib/",
-      "languageVersion": "2.7"
+      "languageVersion": "2.15"
     },
     {
       "name": "frontend_server_client",
@@ -415,13 +415,13 @@
       "name": "linter",
       "rootUri": "../third_party/pkg/linter",
       "packageUri": "lib/",
-      "languageVersion": "2.12"
+      "languageVersion": "2.15"
     },
     {
       "name": "lints",
       "rootUri": "../third_party/pkg/lints",
       "packageUri": "lib/",
-      "languageVersion": "2.12"
+      "languageVersion": "2.17"
     },
     {
       "name": "logging",
@@ -580,7 +580,7 @@
       "name": "shelf",
       "rootUri": "../third_party/pkg/shelf",
       "packageUri": "lib/",
-      "languageVersion": "2.12"
+      "languageVersion": "2.16"
     },
     {
       "name": "shelf_packages_handler",
@@ -688,7 +688,7 @@
       "name": "test",
       "rootUri": "../third_party/pkg/test/pkgs/test",
       "packageUri": "lib/",
-      "languageVersion": "2.12"
+      "languageVersion": "2.14"
     },
     {
       "name": "test_api",
@@ -700,7 +700,7 @@
       "name": "test_core",
       "rootUri": "../third_party/pkg/test/pkgs/test_core",
       "packageUri": "lib/",
-      "languageVersion": "2.12"
+      "languageVersion": "2.14"
     },
     {
       "name": "test_descriptor",
diff --git a/BUILD.gn b/BUILD.gn
index ad09ed8..4b587be 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -39,6 +39,7 @@
     # Fuchsia has run_vm_tests marked testonly.
     testonly = true
   }
+
   deps = [
     "runtime/bin:dart",
     "runtime/bin:entrypoints_verification_test",
@@ -59,6 +60,16 @@
     ]
   }
 
+  # We do not support AOT on ia32 and should therefore not provide native
+  # snapshot tooling.
+  if (dart_target_arch != "ia32" && dart_target_arch != "x86") {
+    if (dart_runtime_mode == "release") {
+      deps += [ "runtime/bin:analyze_snapshot_product" ]
+    } else {
+      deps += [ "runtime/bin:analyze_snapshot" ]
+    }
+  }
+
   if (is_linux || is_android) {
     deps += [ "runtime/bin:abstract_socket_test" ]
   }
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a99aaa..c1dec5b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,12 @@
   `Element.scrollIntoViewIfNeeded` should use the new `scrollIntoViewIfNeeded`
   definition instead.
 
+- Change `Performance.mark` and `Performance.measure` to accept their different
+  overloads. `mark` can now accept a `markOptions` map, and `measure` can now
+  accept a `startMark` and `endMark`, or a `measureOptions` map. Both methods
+  return their correct return types now as well - `PerformanceEntry?` and
+  `PerformanceMeasure?`, respectively.
+
 #### `dart:indexed_db`
 
 - `IdbFactory.supportsDatabaseNames` has been deprecated. It will always return
@@ -57,15 +63,20 @@
       throw UnsupportedError("keyLog not implemented");
   ```
 
+- **Breaking Change** [#34218](https://github.com/dart-lang/sdk/issues/34218):
+  Constants in `dart:io` following the `SCREAMING_CAPS` convention have been
+  removed (they were previously deprecated).  Please use the corresponding
+  `lowerCamelCase` constants instead.
+
 - Add a optional `keyLog` parameter to `SecureSocket.connect` and
   `SecureSocket.startConnect`.
 
 - Deprecate `SecureSocket.renegotiate` and `RawSecureSocket.renegotiate`,
   which were no-ops.
 
-#### `dart:isolate`
-
-- Add `Isolate.run` to run a function in a new isolate.
+- **Breaking Change** [#48513](https://github.com/dart-lang/sdk/issues/48513):
+  Add a new `allowLegacyUnsafeRenegotiation` poperty to `SecurityContext`,
+  which allows TLS renegotiation for client secure sockets.
 
 ### Tools
 
@@ -101,6 +112,49 @@
           [web]                  A web app that uses only core Dart libraries.
 ```
 
+#### Linter
+
+Updated the Linter to `1.22.0`, which includes changes that
+
+- fixes null-safe variance exceptions in `invariant_booleans`
+- updates `depend_on_referenced_packages` to treat `flutter_gen` as a virtual
+  package, not needing an explicit dependency.
+- updates `unnecessary_null_checks` and
+  `null_check_on_nullable_type_parameter` to handle
+  list/set/map literals, and `yield` and `await` expressions.
+- fixes `unnecessary_null_aware_assignments` property-access
+  false positives.
+- adds new lint: `use_super_parameters`.
+- adds new lint: `use_enums`.
+- adds new lint: `use_colored_box`.
+- improves performance for `sort_constructors`.
+- improves docs for `always_use_package_imports`,
+  `avoid_print`, and `avoid_relative_lib_imports` .
+- updates `avoid_void_async` to skip `main` functions.
+- updates `prefer_final_parameters` to not super on super params.
+- updates lints for enhanced-enums and super-initializer language
+  features.
+- updates `unnecessary_late` to report on variable names.
+- marks `null_check_on_nullable_type_parameter` stable.
+
+#### Dartdoc
+
+Updated dartdoc to 5.1.0, which includes changes that
+
+- support the enhanced enums feature
+- remove superfluous `[...]` links
+- fix `categoryOrder` option
+- display categorized extensions
+- add annotations to extensions
+- make minor improvements to performance
+
+## 2.16.2 - 2022-03-24
+
+This is a patch release that fixes a dart2js crash when building some Flutter
+web apps (issue [#47916][]).
+
+[#47916]: https://github.com/dart-lang/sdk/issues/47916
+
 ## 2.16.1 - 2022-02-09
 
 This is a patch release that fixes an AOT precompiler crash when building some
@@ -167,6 +221,14 @@
 
 [an issue]: https://github.com/dart-lang/sdk/issues/new
 
+- **Breaking Change** [#46100](https://github.com/dart-lang/sdk/issues/46100):
+  The deprecated standalone `pub` tool has been removed.
+  Its replacement is the `dart pub` command.
+  Should you find any issues, or missing features, in the replacement
+  command, kindly file [an issue][].
+
+[an issue]: https://github.com/dart-lang/pub/issues/new
+
 #### Pub
 
 - Fixed race conditions in `dart pub get`, `dart run` and `dart pub global run`.
@@ -176,8 +238,16 @@
   tracker.
 
   `dart --verbose pub [command]` will also cause the log file to be written.
+- `dart pub global activate --source=git` now takes arguments `--git-path` to
+  specify the path of the activated package in the pubspec and `--git-ref` to
+  specify the branch or revision to check out.
 - `dart pub add` can now add multiple packages in one command.
-
+- `dart pub token add` can now add a token for [pub.dev](https://pub.dev).
+- `dart pub uploader` has been removed. To manage uploaders for a package use
+  the `https://pub.dev/<packagename>/admin` web-interface.
+- Pub now supports a separate `pubspec_overrides.yaml` file that can contain
+  `dependency_overrides`. This makes it easier to avoid checking the local
+  overrides into version control.
 #### Linter
 
 Updated the Linter to `1.18.0`, which includes changes that
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cdfcdaf..ae27a6f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -24,17 +24,23 @@
 
 We occasionally take pull requests, e.g., for comment changes, but the main flow is to use the Gerrit review system as explained below.
 
-## Setting up Environment
+## Submitting patches directly from GitHub
 
-In order to submit a patch, you need to get the [depot\_tools](http://dev.chromium.org/developers/how-tos/depottools).
+This repository uses Gerrit for code reviews, rather than GitHub PRs. However, you may submit a GitHub PR from the GitHub interface, e.g. to edit some API documentation, and it will be automatically converted into a Gerrit CL by copybara. You can find the link to that CL from the GitHub "checks" interface, it will be the "details" of the "copybara" check. The PR will be automatically closed when the CL is reviewed and landed.
+
+## Setting up the environment
+
+In order to submit a patch from a local workspace, you need to get the [depot\_tools](http://dev.chromium.org/developers/how-tos/depottools).
 
 ## Getting the code
 
-To work with the Dart code, you need to download and build the development branch. Active development of Dart takes place on the `main` branch, from which we push "green" versions that have passed all tests to `dev` branch. Complete instructions are found at [Getting The Source](https://github.com/dart-lang/sdk/wiki/Building#getting-the-source)
+To work with the Dart code, you need to download and build the development branch. Active development of Dart takes place on the `main` branch, from which we push "green" versions that have passed all tests to `dev` branch. Complete instructions are found at [Getting The Source](https://github.com/dart-lang/sdk/wiki/Building#getting-the-source). **You must use the `gclient` tool (`fetch`), using `git clone` will not get you a functional environment!**
 
 ## Starting a patch with git
 
-Note: you can be in any branch when you run `git new-branch`
+Create a new branch using `git new-branch` (this is a command added by the aforementioned depot_tools). 
+
+You can be in any branch when you run `git new-branch`.
 
 ```bash
 git new-branch <feature name>
@@ -51,13 +57,13 @@
 ensure your branch is merging cleanly to `origin/main`.
 
 There are multiple ways to do this, but we generally recommend
-running:
+using `git rebase-update` (another feature added by depot_tools):
 
 ```bash
 git rebase-update
 ```
 
-Note: you can run this command from any branch.
+You can run this command from any branch.
 
 This command will fetch
 `origin/main`, rebase all your open branches, and delete
@@ -67,7 +73,7 @@
 
 ## Uploading the patch for review
 
-Upload the patch for review:
+Upload the patch to Gerrit for review using `git cl upload`:
 
 ```bash
 git cl upload -s
@@ -75,7 +81,7 @@
 
 The above command returns a URL for the review. Attach this review to your issue in https://dartbug.com.
 
-To update the cl, just commit your changes and run `git cl upload -s` for your branch.
+To update the cl, just commit your changes and run `git cl upload -s` for your branch again.
 
 If you have commit access, when the review is done and the patch is good to go, submit the patch on https://dart-review.googlesource.com:
 
@@ -92,7 +98,7 @@
 
 More detailed instructions for the `git cl` tools available on https://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_creating_uploading_a_cl
 
-## For committers: Merging external contributions
+## For committers: Merging contributions from non-members
 
 If the author of a patch is not a committer, they will need help landing the patch.
 Once a patch gets an LGTM, it's easy for a committer to merge it in.
@@ -109,7 +115,7 @@
 
 You should familiarize yourself with those guidelines.
 
-All files in the Dart project must start with the following header. If you add a new file please also add this. The year should be a single number (not a range; don't use "2011-2012", even if the original code did).  If you edit an existing file you don't have to update the year
+All files in the Dart project must start with the following header. If you add a new file please also add this. The year should be a single number (not a range; don't use "2011-2012", even if the original code did).  If you edit an existing file you don't have to update the year.
 
 ```dart
 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
diff --git a/DEPS b/DEPS
index dc14ff7..c8310ef 100644
--- a/DEPS
+++ b/DEPS
@@ -39,14 +39,14 @@
 
   # Checked-in SDK version. The checked-in SDK is a Dart SDK distribution in a
   # cipd package used to run Dart scripts in the build and test infrastructure.
-  "sdk_tag": "version:2.15.1",
+  "sdk_tag": "version:2.16.2",
 
   # co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
   # hashes. It requires access to the dart-build-access group, which EngProd
   # has.
-  "co19_rev": "a38d7c5685e64499cfbdbfe6548fbd5b63b57f15",
+  "co19_rev": "7baaec7cf49188b92ceed2af98beb1caf8939756",
   # This line prevents conflicts when both packages are rolled simultaneously.
-  "co19_2_rev": "995745937abffe9fc3a6441f9f0db27b2d706e4c",
+  "co19_2_rev": "b2034a17609472e374623f3dbe0efd9f5cb258af",
 
   # The internal benchmarks to use. See go/dart-benchmarks-internal
   "benchmarks_internal_rev": "076df10d9b77af337f2d8029725787155eb1cd52",
@@ -67,23 +67,23 @@
   # The list of revisions for these tools comes from Fuchsia, here:
   # https://fuchsia.googlesource.com/integration/+/HEAD/toolchain
   # If there are problems with the toolchain, contact fuchsia-toolchain@.
-  "clang_revision": "1aa59ff2f789776ebfa2d4b315fd3ea589652b4a",
-  "gn_revision": "b79031308cc878488202beb99883ec1f2efd9a6d",
+  "clang_revision": "c2592c374e469f343ecea82d6728609650924259",
+  "gn_revision": "d7c2209cebcfe37f46dba7be4e1a7000ffc342fb",
 
   # Scripts that make 'git cl format' work.
-  "clang_format_scripts_rev": "c09c8deeac31f05bd801995c475e7c8070f9ecda",
+  "clang_format_scripts_rev": "bb994c6f067340c1135eb43eed84f4b33cfa7397",
 
   "gperftools_revision": "180bfa10d7cb38e8b3784d60943d50e8fcef0dcb",
 
   # Revisions of /third_party/* dependencies.
   "args_rev": "3b3f55766af13d895d2020ec001a28e8dc147f91",
-  "async_rev": "80886150a5e6c58006c8ae5a6c2aa7108638e2a9",
+  "async_rev": "3f58c326bd4928fca9ddc10c72b19cb7ac659256",
   "bazel_worker_rev": "ceeba0982d4ff40d32371c9d35f3d2dc1868de20",
   "benchmark_harness_rev": "c546dbd9f639f75cd2f75de8df2eb9f8ea15e8e7",
   "boolean_selector_rev": "437e7f06c7e416bed91e16ae1df453555897e945",
   "boringssl_gen_rev": "ced85ef0a00bbca77ce5a91261a5f2ae61b1e62f",
   "boringssl_rev" : "87f316d7748268eb56f2dc147bd593254ae93198",
-  "browser-compat-data_tag": "v1.0.22",
+  "browser-compat-data_tag": "ac8cae697014da1ff7124fba33b0b4245cc6cd1b", # v1.0.22
   "browser_launcher_rev": "c6cc1025d6901926cf022e144ba109677e3548f1",
   "characters_rev": "6ec389c4dfa8fce14820dc5cbf6e693202e7e052",
   "charcode_rev": "84ea427711e24abf3b832923959caa7dd9a8514b",
@@ -109,8 +109,8 @@
   # For more details, see https://github.com/dart-lang/sdk/issues/30164
   "dart_style_rev": "d7b73536a8079331c888b7da539b80e6825270ea",
 
-  "dartdoc_rev" : "a39f378f8100b907e6285ac825967d764fd664ad",
-  "devtools_rev" : "b9f2039239cc72ac8b26f8a5fe46123f34d53ce1",
+  "dartdoc_rev" : "334072b0cad436c05f6bcecf8a1a59f2f0809b84",
+  "devtools_rev" : "2a707ca56c1a9d5eeef212c28c573548a051fdd2",
   "ffi_rev": "4dd32429880a57b64edaf54c9d5af8a9fa9a4ffb",
   "fixnum_rev": "848341f061359ef7ddc0cad472c2ecbb036b28ac",
   "file_rev": "1ebc38852ffed24b564910317982298b56c2cedd",
@@ -121,16 +121,16 @@
   "http_parser_rev": "202391286ddc13c4c3c284ac5b511f04697250ed",
   "http_rev": "1e42ffa181b263f7790f276a5465832bff7ce615",
   "icu_rev" : "81d656878ec611cb0b42d52c82e9dae93920d9ba",
-  "intl_tag": "0.17.0-nullsafety",
+  "intl_tag": "9669926609e7efc17dfd74fbb44ec719a7e573cc", # 0.17.0-nullsafety
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
   "json_rpc_2_rev": "7e00f893440a72de0637970325e4ea44bd1e8c8e",
-  "linter_tag": "1.18.0",
-  "lints_tag": "f9670df2a66e0ec12eb51554e70c1cbf56c8f5d0",
+  "linter_tag": "14c916a16e78315e212cf79e7ccf4c19159a1bda", # 1.22.0
+  "lints_tag": "8294e5648ab49474541527e2911e72e4c5aefe55", #2.0.0
   "logging_rev": "dfbe88b890c3b4f7bc06da5a7b3b43e9e263b688",
   "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
   "markdown_rev": "7479783f0493f6717e1d7ae31cb37d39a91026b2",
   "matcher_rev": "07595a7739d47a8315caba5a8e58fb9ae3d81261",
-  "mime_rev": "7f4252d469de032aa4df9f90e827dbac4b8efa48",
+  "mime_rev": "c2c5ffd594674f32dc277521369da1557a1622d3",
   "mockito_rev": "d39ac507483b9891165e422ec98d9fb480037c8b",
   "oauth2_rev": "7cd3284049fe5badbec9f2bea2afc41d14c01057",
   "package_config_rev": "8731bf10b5375542792a32a0f7c8a6f370583d96",
@@ -141,14 +141,14 @@
   "pool_rev": "7abe634002a1ba8a0928eded086062f1307ccfae",
   "process_rev": "56ece43b53b64c63ae51ec184b76bd5360c28d0b",
   "protobuf_rev": "c1eb6cb51af39ccbaa1a8e19349546586a5c8e31",
-  "pub_rev": "8f5ab7b1aba3b9f66b56246d77e167990339d317",
+  "pub_rev": "a3a102a549388a6dbfecc9252fabb618f9a2f5f7",
   "pub_semver_rev": "ea6c54019948dc03042c595ce9413e17aaf7aa38",
   "root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
   "rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
   "shelf_static_rev": "202ec1a53c9a830c17cf3b718d089cf7eba568ad",
   "shelf_packages_handler_rev": "78302e67c035047e6348e692b0c1182131f0fe35",
-  "shelf_proxy_tag": "v1.0.0",
-  "shelf_rev": "46483f896cc4308ee3d8e997030ae799b72aa16a",
+  "shelf_proxy_tag": "ccd311f64d8689e7a145d703ba267975d6df9e28", # 1.0.0
+  "shelf_rev": "78ac724a7944700340a3cef28c84bccbe62e9367",
   "shelf_web_socket_rev": "24fb8a04befa75a94ac63a27047b231d1a22aab4",
   "source_map_stack_trace_rev": "80709f2d2fe5086ab50d744a28a2d26ea4384a1b",
   "source_maps-0.9.4_rev": "38524",
@@ -163,7 +163,7 @@
   "test_process_rev": "7c73ec8a8a6e0e63d0ec27d70c21ca4323fb5e8f",
   "term_glyph_rev": "4885b7f8af6931e23d3aa6d1767ee3f9a626923d",
   "test_reflective_loader_rev": "fcfce37666672edac849d2af6dffc0f8df236a94",
-  "test_rev": "099dcc4d052a30c6921489cfbefa1c8531d12975",
+  "test_rev": "b6aba5544628730b7d6a38eae1aef9117a1bb235",
   "typed_data_rev": "29ce5a92b03326d0b8035916ac04f528874994bd",
   "usage_rev": "f0cb8f7cce8b675255c81488dbab8cf9f2f56404",
   "vector_math_rev": "0cbed0914d49a6a44555e6d5444c438a4a4c3fc1",
@@ -174,7 +174,7 @@
   "WebCore_rev": "bcb10901266c884e7b3740abc597ab95373ab55c",
   "webdev_rev": "832b096c0c24798d3df46faa7b7661fe930573c2",
   "webkit_inspection_protocol_rev": "dd6fb5d8b536e19cedb384d0bbf1f5631923f1e8",
-  "yaml_edit_rev": "df1452bfe1653286277a1a8f34dddf3e4fbedd9e",
+  "yaml_edit_rev": "4fadb43801b07f90b3f0c6065dbce4efc6d8d55e",
   "yaml_rev": "ad0779d1baa25c6b10a192d080efc45de02b6a32",
   "zlib_rev": "bf44340d1b6be1af8950bbdf664fec0cf5a831cc",
   "crashpad_rev": "bf327d8ceb6a669607b0dbab5a83a275d03f99ed",
@@ -601,7 +601,7 @@
     "packages": [
       {
       "package": "fuchsia/sdk/gn/mac-amd64",
-      "version": "git_revision:190502a955c482431c2edd0525e128423728b662"
+      "version": "git_revision:c9bdf5da65647923cb79c391824434125cb00bbe"
       }
     ],
     "condition": 'host_os == "mac" and host_cpu == "x64"',
@@ -611,7 +611,7 @@
     "packages": [
       {
       "package": "fuchsia/sdk/gn/linux-amd64",
-      "version": "git_revision:190502a955c482431c2edd0525e128423728b662"
+      "version": "git_revision:c9bdf5da65647923cb79c391824434125cb00bbe"
       }
     ],
     "condition": 'host_os == "linux" and host_cpu == "x64"',
@@ -698,6 +698,12 @@
 
 hooks = [
   {
+    # Generate the .dart_tool/package_confg.json file.
+    'name': 'Generate .dart_tool/package_confg.json',
+    'pattern': '.',
+    'action': ['python3', 'sdk/tools/generate_package_config.py'],
+  },
+  {
     # Pull Debian sysroot for i386 Linux
     'name': 'sysroot_i386',
     'pattern': '.',
diff --git a/benchmarks/AsyncLiveVars/dart/AsyncLiveVars.dart b/benchmarks/AsyncLiveVars/dart/AsyncLiveVars.dart
new file mode 100644
index 0000000..7969da6
--- /dev/null
+++ b/benchmarks/AsyncLiveVars/dart/AsyncLiveVars.dart
@@ -0,0 +1,305 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Micro-benchmark for testing async/await performance in presence of
+// different number of live values across await.
+
+import 'dart:async';
+
+import 'async_benchmark_base.dart' show AsyncBenchmarkBase;
+
+class MockClass {
+  static final String str = "${int.parse('42')}";
+  static final List<int> list =
+      List<int>.filled(int.parse('3'), int.parse('42'));
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  String get1() => str;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  List<int> get2() => list;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use1(String a0) => a0.length;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use2(String a0, List<int> a1) => a0.length + a1.length;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use4(String a0, List<int> a1, String a2, List<int> a3) =>
+      a0.length + a1.length + a2.length + a3.length;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use8(String a0, List<int> a1, String a2, List<int> a3, String a4,
+          List<int> a5, String a6, List<int> a7) =>
+      a0.length +
+      a1.length +
+      a2.length +
+      a3.length +
+      a4.length +
+      a5.length +
+      a6.length +
+      a7.length;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  Future<void> asyncMethod() async {}
+}
+
+class MockClass2 {
+  static int val1 = int.parse('42');
+  static int val2 = int.parse('43');
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  int get1() => val1;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  int get2() => val2;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use1(int a0) => a0;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use2(int a0, int a1) => a0 + a1;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use4(int a0, int a1, int a2, int a3) => a0 + a1 + a2 + a3;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  Future<void> asyncMethod() async {}
+}
+
+class LiveVarsBench extends AsyncBenchmarkBase {
+  LiveVarsBench(String name) : super(name);
+  @override
+  Future<void> exercise() async {
+    // These micro-benchmarks are too small, so
+    // make a larger number of iterations per measurement.
+    for (var i = 0; i < 10000; i++) {
+      await run();
+    }
+  }
+}
+
+class LiveObj1 extends LiveVarsBench {
+  LiveObj1() : super('AsyncLiveVars.LiveObj1');
+  final field1 = MockClass();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field1.use1(obj1);
+  }
+}
+
+class LiveObj2 extends LiveVarsBench {
+  LiveObj2() : super('AsyncLiveVars.LiveObj2');
+  final field1 = MockClass();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field1.use2(obj1, obj2);
+  }
+}
+
+class LiveObj4 extends LiveVarsBench {
+  LiveObj4() : super('AsyncLiveVars.LiveObj4');
+  final field1 = MockClass();
+  final field2 = MockClass();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    final obj3 = field2.get1();
+    final obj4 = field2.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field2.use1(obj3);
+    await field2.asyncMethod();
+    field1.use4(obj1, obj2, obj3, obj4);
+  }
+}
+
+class LiveObj8 extends LiveVarsBench {
+  LiveObj8() : super('AsyncLiveVars.LiveObj8');
+  final field1 = MockClass();
+  final field2 = MockClass();
+  final field3 = MockClass();
+  final field4 = MockClass();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    final obj3 = field2.get1();
+    final obj4 = field2.get2();
+    final obj5 = field3.get1();
+    final obj6 = field3.get2();
+    final obj7 = field4.get1();
+    final obj8 = field4.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field2.asyncMethod();
+    field3.use2(obj5, obj6);
+    await field4.asyncMethod();
+    field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
+  }
+}
+
+class LiveObj16 extends LiveVarsBench {
+  LiveObj16() : super('AsyncLiveVars.LiveObj16');
+  final field1 = MockClass();
+  final field2 = MockClass();
+  final field3 = MockClass();
+  final field4 = MockClass();
+  final field5 = MockClass();
+  final field6 = MockClass();
+  final field7 = MockClass();
+  final field8 = MockClass();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    final obj3 = field2.get1();
+    final obj4 = field2.get2();
+    final obj5 = field3.get1();
+    final obj6 = field3.get2();
+    final obj7 = field4.get1();
+    final obj8 = field4.get2();
+    final obj9 = field5.get1();
+    final obj10 = field5.get2();
+    final obj11 = field6.get1();
+    final obj12 = field6.get2();
+    final obj13 = field7.get1();
+    final obj14 = field7.get2();
+    final obj15 = field8.get1();
+    final obj16 = field8.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field2.asyncMethod();
+    field5.use2(obj11, obj12);
+    await field4.asyncMethod();
+    field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
+    field3.use8(obj9, obj10, obj11, obj12, obj13, obj14, obj15, obj16);
+  }
+}
+
+class LiveInt1 extends LiveVarsBench {
+  LiveInt1() : super('AsyncLiveVars.LiveInt1');
+  final field1 = MockClass2();
+  @override
+  Future<void> run() async {
+    final int1 = field1.get1();
+    await field1.asyncMethod();
+    field1.use1(int1);
+    await field1.asyncMethod();
+    field1.use1(int1);
+    await field1.asyncMethod();
+    field1.use1(int1);
+  }
+}
+
+class LiveInt4 extends LiveVarsBench {
+  LiveInt4() : super('AsyncLiveVars.LiveInt4');
+  final field1 = MockClass2();
+  final field2 = MockClass2();
+  @override
+  Future<void> run() async {
+    final int1 = field1.get1();
+    final int2 = field1.get2();
+    final int3 = field2.get1();
+    final int4 = field2.get2();
+    await field1.asyncMethod();
+    field1.use1(int1);
+    await field1.asyncMethod();
+    field2.use1(int3);
+    await field2.asyncMethod();
+    field1.use4(int1, int2, int3, int4);
+  }
+}
+
+class LiveObj2Int2 extends LiveVarsBench {
+  LiveObj2Int2() : super('AsyncLiveVars.LiveObj2Int2');
+  final field1 = MockClass();
+  final field2 = MockClass2();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    final int1 = field2.get1();
+    final int2 = field2.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field2.use1(int1);
+    await field2.asyncMethod();
+    field1.use2(obj1, obj2);
+    field2.use2(int1, int2);
+  }
+}
+
+class LiveObj4Int4 extends LiveVarsBench {
+  LiveObj4Int4() : super('AsyncLiveVars.LiveObj4Int4');
+  final field1 = MockClass();
+  final field2 = MockClass();
+  final field3 = MockClass2();
+  final field4 = MockClass2();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    final obj3 = field2.get1();
+    final obj4 = field2.get2();
+    final int1 = field3.get1();
+    final int2 = field3.get2();
+    final int3 = field4.get1();
+    final int4 = field4.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field2.asyncMethod();
+    field3.use2(int2, int4);
+    await field4.asyncMethod();
+    field2.use4(obj1, obj2, obj3, obj4);
+    field4.use4(int1, int2, int3, int4);
+  }
+}
+
+Future<void> main() async {
+  final benchmarks = [
+    LiveObj1(),
+    LiveObj2(),
+    LiveObj4(),
+    LiveObj8(),
+    LiveObj16(),
+    LiveInt1(),
+    LiveInt4(),
+    LiveObj2Int2(),
+    LiveObj4Int4()
+  ];
+  for (final bench in benchmarks) {
+    await bench.report();
+  }
+}
diff --git a/benchmarks/AsyncLiveVars/dart/async_benchmark_base.dart b/benchmarks/AsyncLiveVars/dart/async_benchmark_base.dart
new file mode 100644
index 0000000..9a415b9
--- /dev/null
+++ b/benchmarks/AsyncLiveVars/dart/async_benchmark_base.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:benchmark_harness/benchmark_harness.dart'
+    show PrintEmitter, ScoreEmitter;
+
+// Similar to BenchmarkBase from package:benchmark_harness.
+class AsyncBenchmarkBase {
+  final String name;
+  final ScoreEmitter emitter;
+
+  // Empty constructor.
+  const AsyncBenchmarkBase(this.name, {this.emitter = const PrintEmitter()});
+
+  // The benchmark code.
+  // This function is not used, if both [warmup] and [exercise] are overwritten.
+  Future<void> run() async {}
+
+  // Runs a short version of the benchmark. By default invokes [run] once.
+  Future<void> warmup() async {
+    await run();
+  }
+
+  // Exercises the benchmark. By default invokes [run] 10 times.
+  Future<void> exercise() async {
+    for (var i = 0; i < 10; i++) {
+      await run();
+    }
+  }
+
+  // Not measured setup code executed prior to the benchmark runs.
+  Future<void> setup() async {}
+
+  // Not measures teardown code executed after the benchark runs.
+  Future<void> teardown() async {}
+
+  // Measures the score for this benchmark by executing it repeatedly until
+  // time minimum has been reached.
+  static Future<double> measureFor(Function f, int minimumMillis) async {
+    final int minimumMicros = minimumMillis * 1000;
+    int iter = 0;
+    final watch = Stopwatch();
+    watch.start();
+    int elapsed = 0;
+    while (elapsed < minimumMicros) {
+      await f();
+      elapsed = watch.elapsedMicroseconds;
+      iter++;
+    }
+    return elapsed / iter;
+  }
+
+  // Measures the score for the benchmark and returns it.
+  Future<double> measure() async {
+    await setup();
+    // Warmup for at least 100ms. Discard result.
+    await measureFor(warmup, 100);
+    // Run the benchmark for at least 2000ms.
+    final result = await measureFor(exercise, 2000);
+    await teardown();
+    return result;
+  }
+
+  Future<void> report() async {
+    emitter.emit(name, await measure());
+  }
+}
diff --git a/benchmarks/AsyncLiveVars/dart2/AsyncLiveVars.dart b/benchmarks/AsyncLiveVars/dart2/AsyncLiveVars.dart
new file mode 100644
index 0000000..9176274
--- /dev/null
+++ b/benchmarks/AsyncLiveVars/dart2/AsyncLiveVars.dart
@@ -0,0 +1,307 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Micro-benchmark for testing async/await performance in presence of
+// different number of live values across await.
+
+// @dart=2.9
+
+import 'dart:async';
+
+import 'async_benchmark_base.dart' show AsyncBenchmarkBase;
+
+class MockClass {
+  static final String str = "${int.parse('42')}";
+  static final List<int> list =
+      List<int>.filled(int.parse('3'), int.parse('42'));
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  String get1() => str;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  List<int> get2() => list;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use1(String a0) => a0.length;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use2(String a0, List<int> a1) => a0.length + a1.length;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use4(String a0, List<int> a1, String a2, List<int> a3) =>
+      a0.length + a1.length + a2.length + a3.length;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use8(String a0, List<int> a1, String a2, List<int> a3, String a4,
+          List<int> a5, String a6, List<int> a7) =>
+      a0.length +
+      a1.length +
+      a2.length +
+      a3.length +
+      a4.length +
+      a5.length +
+      a6.length +
+      a7.length;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  Future<void> asyncMethod() async {}
+}
+
+class MockClass2 {
+  static int val1 = int.parse('42');
+  static int val2 = int.parse('43');
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  int get1() => val1;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  int get2() => val2;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use1(int a0) => a0;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use2(int a0, int a1) => a0 + a1;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  void use4(int a0, int a1, int a2, int a3) => a0 + a1 + a2 + a3;
+
+  @pragma('vm:never-inline')
+  @pragma('dart2js:noInline')
+  Future<void> asyncMethod() async {}
+}
+
+class LiveVarsBench extends AsyncBenchmarkBase {
+  LiveVarsBench(String name) : super(name);
+  @override
+  Future<void> exercise() async {
+    // These micro-benchmarks are too small, so
+    // make a larger number of iterations per measurement.
+    for (var i = 0; i < 10000; i++) {
+      await run();
+    }
+  }
+}
+
+class LiveObj1 extends LiveVarsBench {
+  LiveObj1() : super('AsyncLiveVars.LiveObj1');
+  final field1 = MockClass();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field1.use1(obj1);
+  }
+}
+
+class LiveObj2 extends LiveVarsBench {
+  LiveObj2() : super('AsyncLiveVars.LiveObj2');
+  final field1 = MockClass();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field1.use2(obj1, obj2);
+  }
+}
+
+class LiveObj4 extends LiveVarsBench {
+  LiveObj4() : super('AsyncLiveVars.LiveObj4');
+  final field1 = MockClass();
+  final field2 = MockClass();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    final obj3 = field2.get1();
+    final obj4 = field2.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field2.use1(obj3);
+    await field2.asyncMethod();
+    field1.use4(obj1, obj2, obj3, obj4);
+  }
+}
+
+class LiveObj8 extends LiveVarsBench {
+  LiveObj8() : super('AsyncLiveVars.LiveObj8');
+  final field1 = MockClass();
+  final field2 = MockClass();
+  final field3 = MockClass();
+  final field4 = MockClass();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    final obj3 = field2.get1();
+    final obj4 = field2.get2();
+    final obj5 = field3.get1();
+    final obj6 = field3.get2();
+    final obj7 = field4.get1();
+    final obj8 = field4.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field2.asyncMethod();
+    field3.use2(obj5, obj6);
+    await field4.asyncMethod();
+    field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
+  }
+}
+
+class LiveObj16 extends LiveVarsBench {
+  LiveObj16() : super('AsyncLiveVars.LiveObj16');
+  final field1 = MockClass();
+  final field2 = MockClass();
+  final field3 = MockClass();
+  final field4 = MockClass();
+  final field5 = MockClass();
+  final field6 = MockClass();
+  final field7 = MockClass();
+  final field8 = MockClass();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    final obj3 = field2.get1();
+    final obj4 = field2.get2();
+    final obj5 = field3.get1();
+    final obj6 = field3.get2();
+    final obj7 = field4.get1();
+    final obj8 = field4.get2();
+    final obj9 = field5.get1();
+    final obj10 = field5.get2();
+    final obj11 = field6.get1();
+    final obj12 = field6.get2();
+    final obj13 = field7.get1();
+    final obj14 = field7.get2();
+    final obj15 = field8.get1();
+    final obj16 = field8.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field2.asyncMethod();
+    field5.use2(obj11, obj12);
+    await field4.asyncMethod();
+    field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
+    field3.use8(obj9, obj10, obj11, obj12, obj13, obj14, obj15, obj16);
+  }
+}
+
+class LiveInt1 extends LiveVarsBench {
+  LiveInt1() : super('AsyncLiveVars.LiveInt1');
+  final field1 = MockClass2();
+  @override
+  Future<void> run() async {
+    final int1 = field1.get1();
+    await field1.asyncMethod();
+    field1.use1(int1);
+    await field1.asyncMethod();
+    field1.use1(int1);
+    await field1.asyncMethod();
+    field1.use1(int1);
+  }
+}
+
+class LiveInt4 extends LiveVarsBench {
+  LiveInt4() : super('AsyncLiveVars.LiveInt4');
+  final field1 = MockClass2();
+  final field2 = MockClass2();
+  @override
+  Future<void> run() async {
+    final int1 = field1.get1();
+    final int2 = field1.get2();
+    final int3 = field2.get1();
+    final int4 = field2.get2();
+    await field1.asyncMethod();
+    field1.use1(int1);
+    await field1.asyncMethod();
+    field2.use1(int3);
+    await field2.asyncMethod();
+    field1.use4(int1, int2, int3, int4);
+  }
+}
+
+class LiveObj2Int2 extends LiveVarsBench {
+  LiveObj2Int2() : super('AsyncLiveVars.LiveObj2Int2');
+  final field1 = MockClass();
+  final field2 = MockClass2();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    final int1 = field2.get1();
+    final int2 = field2.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field1.asyncMethod();
+    field2.use1(int1);
+    await field2.asyncMethod();
+    field1.use2(obj1, obj2);
+    field2.use2(int1, int2);
+  }
+}
+
+class LiveObj4Int4 extends LiveVarsBench {
+  LiveObj4Int4() : super('AsyncLiveVars.LiveObj4Int4');
+  final field1 = MockClass();
+  final field2 = MockClass();
+  final field3 = MockClass2();
+  final field4 = MockClass2();
+  @override
+  Future<void> run() async {
+    final obj1 = field1.get1();
+    final obj2 = field1.get2();
+    final obj3 = field2.get1();
+    final obj4 = field2.get2();
+    final int1 = field3.get1();
+    final int2 = field3.get2();
+    final int3 = field4.get1();
+    final int4 = field4.get2();
+    await field1.asyncMethod();
+    field1.use1(obj1);
+    await field2.asyncMethod();
+    field3.use2(int2, int4);
+    await field4.asyncMethod();
+    field2.use4(obj1, obj2, obj3, obj4);
+    field4.use4(int1, int2, int3, int4);
+  }
+}
+
+Future<void> main() async {
+  final benchmarks = [
+    LiveObj1(),
+    LiveObj2(),
+    LiveObj4(),
+    LiveObj8(),
+    LiveObj16(),
+    LiveInt1(),
+    LiveInt4(),
+    LiveObj2Int2(),
+    LiveObj4Int4()
+  ];
+  for (final bench in benchmarks) {
+    await bench.report();
+  }
+}
diff --git a/benchmarks/AsyncLiveVars/dart2/async_benchmark_base.dart b/benchmarks/AsyncLiveVars/dart2/async_benchmark_base.dart
new file mode 100644
index 0000000..9a415b9
--- /dev/null
+++ b/benchmarks/AsyncLiveVars/dart2/async_benchmark_base.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:benchmark_harness/benchmark_harness.dart'
+    show PrintEmitter, ScoreEmitter;
+
+// Similar to BenchmarkBase from package:benchmark_harness.
+class AsyncBenchmarkBase {
+  final String name;
+  final ScoreEmitter emitter;
+
+  // Empty constructor.
+  const AsyncBenchmarkBase(this.name, {this.emitter = const PrintEmitter()});
+
+  // The benchmark code.
+  // This function is not used, if both [warmup] and [exercise] are overwritten.
+  Future<void> run() async {}
+
+  // Runs a short version of the benchmark. By default invokes [run] once.
+  Future<void> warmup() async {
+    await run();
+  }
+
+  // Exercises the benchmark. By default invokes [run] 10 times.
+  Future<void> exercise() async {
+    for (var i = 0; i < 10; i++) {
+      await run();
+    }
+  }
+
+  // Not measured setup code executed prior to the benchmark runs.
+  Future<void> setup() async {}
+
+  // Not measures teardown code executed after the benchark runs.
+  Future<void> teardown() async {}
+
+  // Measures the score for this benchmark by executing it repeatedly until
+  // time minimum has been reached.
+  static Future<double> measureFor(Function f, int minimumMillis) async {
+    final int minimumMicros = minimumMillis * 1000;
+    int iter = 0;
+    final watch = Stopwatch();
+    watch.start();
+    int elapsed = 0;
+    while (elapsed < minimumMicros) {
+      await f();
+      elapsed = watch.elapsedMicroseconds;
+      iter++;
+    }
+    return elapsed / iter;
+  }
+
+  // Measures the score for the benchmark and returns it.
+  Future<double> measure() async {
+    await setup();
+    // Warmup for at least 100ms. Discard result.
+    await measureFor(warmup, 100);
+    // Run the benchmark for at least 2000ms.
+    final result = await measureFor(exercise, 2000);
+    await teardown();
+    return result;
+  }
+
+  Future<void> report() async {
+    emitter.emit(name, await measure());
+  }
+}
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index b38dc18..6cfcf26 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -359,7 +359,10 @@
   # not be compatible with newer ones. To achieve this, we insert a synthetic
   # define into the compile line.
   if (is_clang && (is_linux || is_mac)) {
-    if (is_linux) {
+    if (is_linux && host_cpu == "arm64") {
+      toolchain_stamp_file =
+          "//buildtools/linux-arm64/clang/.versions/clang.cipd_version"
+    } else if (is_linux) {
       toolchain_stamp_file =
           "//buildtools/linux-x64/clang/.versions/clang.cipd_version"
     } else {
@@ -777,10 +780,17 @@
   if (is_win) {
     # The only difference on windows is that the inlining is less aggressive.
     # (We accept the default level). Otherwise it is very slow.
-    cflags = [
-      "/O${debug_optimization_level}",  # Do some optimizations.
-      "/Oy-",  # Disable omitting frame pointers, must be after /O2.
-    ]
+    if (is_clang && debug_optimization_level != "2") {
+      cflags = [
+        "-d${debug_optimization_level}",  # Do some optimizations.
+        "/Oy-",  # Disable omitting frame pointers, must be after /O2.
+      ]
+    } else {
+      cflags = [
+        "/O${debug_optimization_level}",  # Do some optimizations.
+        "/Oy-",  # Disable omitting frame pointers, must be after /O2.
+      ]
+    }
   } else if (is_android) {
     # On Android we kind of optimize some things that don't affect debugging
     # much even when optimization is disabled to get the binary size down.
diff --git a/build/toolchain/linux/BUILD.gn b/build/toolchain/linux/BUILD.gn
index 5f9bc32..05630d0 100644
--- a/build/toolchain/linux/BUILD.gn
+++ b/build/toolchain/linux/BUILD.gn
@@ -21,6 +21,14 @@
   compiler_prefix = ""
 }
 
+if (host_cpu == "arm64") {
+  rebased_clang_dir =
+      rebase_path("//buildtools/linux-arm64/clang/bin", root_build_dir)
+} else {
+  rebased_clang_dir =
+      rebase_path("//buildtools/linux-x64/clang/bin", root_build_dir)
+}
+
 gcc_toolchain("arm") {
   prefix = "arm-linux-gnueabihf-"
   if (toolchain_prefix != "") {
@@ -42,7 +50,7 @@
 }
 
 gcc_toolchain("clang_arm") {
-  prefix = rebase_path("//buildtools/linux-x64/clang/bin", root_build_dir)
+  prefix = rebased_clang_dir
   cc = "${compiler_prefix}${prefix}/clang"
   cxx = "${compiler_prefix}${prefix}/clang++"
 
@@ -78,7 +86,7 @@
 }
 
 gcc_toolchain("clang_arm64") {
-  prefix = rebase_path("//buildtools/linux-x64/clang/bin", root_build_dir)
+  prefix = rebased_clang_dir
   cc = "${compiler_prefix}${prefix}/clang"
   cxx = "${compiler_prefix}${prefix}/clang++"
 
@@ -94,7 +102,7 @@
 }
 
 gcc_toolchain("clang_x86") {
-  prefix = rebase_path("//buildtools/linux-x64/clang/bin", root_build_dir)
+  prefix = rebased_clang_dir
   cc = "${compiler_prefix}${prefix}/clang"
   cxx = "${compiler_prefix}${prefix}/clang++"
 
@@ -126,7 +134,7 @@
 }
 
 gcc_toolchain("clang_x64") {
-  prefix = rebase_path("//buildtools/linux-x64/clang/bin", root_build_dir)
+  prefix = rebased_clang_dir
   cc = "${compiler_prefix}${prefix}/clang"
   cxx = "${compiler_prefix}${prefix}/clang++"
 
@@ -178,7 +186,7 @@
 }
 
 gcc_toolchain("clang_riscv32") {
-  prefix = rebase_path("//buildtools/linux-x64/clang/bin", root_build_dir)
+  prefix = rebased_clang_dir
   cc = "${compiler_prefix}${prefix}/clang"
   cxx = "${compiler_prefix}${prefix}/clang++"
 
@@ -214,7 +222,7 @@
 }
 
 gcc_toolchain("clang_riscv64") {
-  prefix = rebase_path("//buildtools/linux-x64/clang/bin", root_build_dir)
+  prefix = rebased_clang_dir
   cc = "${compiler_prefix}${prefix}/clang"
   cxx = "${compiler_prefix}${prefix}/clang++"
 
diff --git a/pkg/OWNERS b/pkg/OWNERS
index c3abcf9..eec08f0 100644
--- a/pkg/OWNERS
+++ b/pkg/OWNERS
@@ -1,2 +1,5 @@
 file:/tools/OWNERS_FOUNDATION #{LAST_RESORT_SUGGESTION}
 file:/tools/OWNERS_INFRA #{LAST_RESORT_SUGGESTION}
+
+# Test status file
+per-file pkg.status=file:/tools/OWNERS_ENG
diff --git a/pkg/_fe_analyzer_shared/lib/src/deferred_closure_heuristic.dart b/pkg/_fe_analyzer_shared/lib/src/deferred_closure_heuristic.dart
new file mode 100644
index 0000000..d5f823a
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/deferred_closure_heuristic.dart
@@ -0,0 +1,139 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/util/dependency_walker.dart';
+
+/// Data structure tracking the type inference dependencies between closures
+/// passed as invocation parameters.
+///
+/// [planClosureReconciliationStages] is used as part of support for
+/// https://github.com/dart-lang/language/issues/731 (improved inference for
+/// fold etc.) to choose the proper order in which to recursively analyze
+/// closures passed as invocation arguments.
+abstract class ClosureDependencies<TypeVariable, Closure> {
+  final List<_ClosureNode<Closure>> _closureNodes = [];
+
+  /// Construct a [ClosureDependencies] object that's prepared to determine the
+  /// order to resolve [closures] for a generic invocation involving the given
+  /// [typeVariables].
+  ClosureDependencies(
+      Iterable<Closure> closures, Iterable<TypeVariable> typeVariables) {
+    Map<TypeVariable, Set<_ClosureNode<Closure>>> closuresDependingOnTypeVar =
+        {};
+    Map<TypeVariable, Set<_ClosureNode<Closure>>> closuresConstrainingTypeVar =
+        {};
+    for (Closure closure in closures) {
+      _ClosureNode<Closure> closureNode = new _ClosureNode<Closure>(closure);
+      _closureNodes.add(closureNode);
+      for (TypeVariable v in typeVarsFreeInClosureArguments(closure)) {
+        (closuresDependingOnTypeVar[v] ??= {}).add(closureNode);
+      }
+      for (TypeVariable v in typeVarsFreeInClosureReturns(closure)) {
+        (closuresConstrainingTypeVar[v] ??= {}).add(closureNode);
+      }
+    }
+    for (TypeVariable typeVariable in typeVariables) {
+      for (_ClosureNode<Closure> closureNode
+          in closuresDependingOnTypeVar[typeVariable] ?? const {}) {
+        closureNode.dependencies
+            .addAll(closuresConstrainingTypeVar[typeVariable] ?? const {});
+      }
+    }
+  }
+
+  /// Computes the order in which to resolve the closures passed to the
+  /// constructor.
+  ///
+  /// Each entry in the returned list represents the set of closures that should
+  /// be visited during a single stage of resolution; after each stage, the
+  /// assignment of actual types to type variables should be refined.
+  ///
+  /// So, for example, if the closures in question are A, B, and C, and the
+  /// returned list is `[{A, B}, {C}]`, then first closures A and B should be
+  /// resolved, then the assignment of actual types to type variables should be
+  /// refined, and then C should be resolved, and then the final assignment of
+  /// actual types to type variables should be computed.
+  List<Set<Closure>> planClosureReconciliationStages() {
+    _DependencyWalker<Closure> walker = new _DependencyWalker<Closure>();
+    for (_ClosureNode<Closure> closureNode in _closureNodes) {
+      walker.walk(closureNode);
+    }
+    return walker.closureReconciliationStages;
+  }
+
+  /// If the type of the parameter corresponding to [closure] is a function
+  /// type, the set of type parameters referred to by the parameter types of
+  /// that parameter.  If the type of the parameter is not a function type, an
+  /// empty iterable should be returned.
+  ///
+  /// Should be overridden by the client.
+  Iterable<TypeVariable> typeVarsFreeInClosureArguments(Closure closure);
+
+  /// If the type of the parameter corresponding to [closure] is a function
+  /// type, the set of type parameters referred to by the return type of that
+  /// parameter.  If the type of the parameter is not a function type, the set
+  /// type parameters referred to by the type of the parameter should be
+  /// returned.
+  ///
+  /// Should be overridden by the client.
+  Iterable<TypeVariable> typeVarsFreeInClosureReturns(Closure closure);
+}
+
+/// Node type representing a single [Closure] for purposes of walking the
+/// graph of type inference dependencies among closures.
+class _ClosureNode<Closure> extends Node<_ClosureNode<Closure>> {
+  /// The [Closure] being represented by this node.
+  final Closure closure;
+
+  /// If not `null`, the index of the reconciliation stage to which this closure
+  /// has been assigned.
+  int? stageNum;
+
+  /// The nodes for the closures depended on by this closure.
+  final List<_ClosureNode<Closure>> dependencies = [];
+
+  _ClosureNode(this.closure);
+
+  @override
+  bool get isEvaluated => stageNum != null;
+
+  @override
+  List<_ClosureNode<Closure>> computeDependencies() => dependencies;
+}
+
+/// Derived class of [DependencyWalker] capable of walking the graph of type
+/// inference dependencies among closures.
+class _DependencyWalker<Closure>
+    extends DependencyWalker<_ClosureNode<Closure>> {
+  /// The set of closure reconciliation stages accumulated so far.
+  final List<Set<Closure>> closureReconciliationStages = [];
+
+  @override
+  void evaluate(_ClosureNode v) => evaluateScc([v]);
+
+  @override
+  void evaluateScc(List<_ClosureNode> nodes) {
+    int stageNum = 0;
+    for (_ClosureNode node in nodes) {
+      for (_ClosureNode dependency in node.dependencies) {
+        int? dependencyStageNum = dependency.stageNum;
+        if (dependencyStageNum != null && dependencyStageNum >= stageNum) {
+          stageNum = dependencyStageNum + 1;
+        }
+      }
+    }
+    if (closureReconciliationStages.length <= stageNum) {
+      closureReconciliationStages.add({});
+      // `stageNum` can't grow by more than 1 each time `evaluateScc` is called,
+      // so adding one stage is sufficient to make sure the list is now long
+      // enough.
+      assert(stageNum < closureReconciliationStages.length);
+    }
+    Set<Closure> stage = closureReconciliationStages[stageNum];
+    for (_ClosureNode node in nodes) {
+      node.stageNum = stageNum;
+      stage.add(node.closure);
+    }
+  }
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index 9a0d773..a72b342 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -344,6 +344,28 @@
   String toString() => 'DemoteViaExplicitWrite($node)';
 }
 
+/// Information gathered by flow analysis about an argument to either
+/// `identical` or `operator ==`.
+class EqualityInfo<Variable extends Object, Type extends Object> {
+  /// The [ExpressionInfo] for the expression.  This is used to determine
+  /// whether the expression is a `null` literal.
+  final ExpressionInfo<Variable, Type>? _expressionInfo;
+
+  /// The type of the expression on the LHS of `==` or `!=`.
+  final Type _type;
+
+  /// If the LHS of `==` or `!=` is a reference, the thing being referred to.
+  /// Otherwise `null`.
+  final ReferenceWithType<Variable, Type>? _reference;
+
+  EqualityInfo._(this._expressionInfo, this._type, this._reference);
+
+  @override
+  String toString() =>
+      'EqualityInfo(expressionInfo: $_expressionInfo, type: $_type, reference: '
+      '$_reference)';
+}
+
 /// A collection of flow models representing the possible outcomes of evaluating
 /// an expression that are relevant to flow analysis.
 class ExpressionInfo<Variable extends Object, Type extends Object> {
@@ -479,14 +501,27 @@
   /// [condition] should be the condition of the loop.
   void doStatement_end(Expression condition);
 
-  /// Call this method just after visiting a binary `==` or `!=` expression.
-  void equalityOp_end(Expression wholeExpression, Expression rightOperand,
-      Type rightOperandType,
-      {bool notEqual = false});
+  /// Call this method just after visiting either side of a binary `==` or `!=`
+  /// expression, or an argument to `identical`.
+  ///
+  /// Returns information about the expression that will later be needed by
+  /// [equalityOperation_end].
+  ///
+  /// Note: the return type is nullable because legacy type promotion doesn't
+  /// need to record information about equality operands.
+  EqualityInfo<Variable, Type>? equalityOperand_end(
+      Expression operand, Type type);
 
-  /// Call this method just after visiting the left hand side of a binary `==`
-  /// or `!=` expression.
-  void equalityOp_rightBegin(Expression leftOperand, Type leftOperandType);
+  /// Call this method just after visiting the operands of a binary `==` or `!=`
+  /// expression, or an invocation of `identical`.
+  ///
+  /// [leftOperandInfo] and [rightOperandInfo] should be the values returned by
+  /// [equalityOperand_end].
+  void equalityOperation_end(
+      Expression wholeExpression,
+      EqualityInfo<Variable, Type>? leftOperandInfo,
+      EqualityInfo<Variable, Type>? rightOperandInfo,
+      {bool notEqual = false});
 
   /// Retrieves the [ExpressionInfo] associated with [target], if known.  Will
   /// return `null` if (a) no info is associated with [target], or (b) another
@@ -1106,21 +1141,24 @@
   }
 
   @override
-  void equalityOp_end(Expression wholeExpression, Expression rightOperand,
-      Type rightOperandType,
-      {bool notEqual = false}) {
-    _wrap(
-        'equalityOp_end($wholeExpression, $rightOperand, $rightOperandType, '
-        'notEqual: $notEqual)',
-        () => _wrapped.equalityOp_end(
-            wholeExpression, rightOperand, rightOperandType,
-            notEqual: notEqual));
-  }
+  EqualityInfo<Variable, Type>? equalityOperand_end(
+          Expression operand, Type type) =>
+      _wrap('equalityOperand_end($operand, $type)',
+          () => _wrapped.equalityOperand_end(operand, type),
+          isQuery: true);
 
   @override
-  void equalityOp_rightBegin(Expression leftOperand, Type leftOperandType) {
-    _wrap('equalityOp_rightBegin($leftOperand, $leftOperandType)',
-        () => _wrapped.equalityOp_rightBegin(leftOperand, leftOperandType));
+  void equalityOperation_end(
+      Expression wholeExpression,
+      EqualityInfo<Variable, Type>? leftOperandInfo,
+      EqualityInfo<Variable, Type>? rightOperandInfo,
+      {bool notEqual = false}) {
+    _wrap(
+        'equalityOperation_end($wholeExpression, $leftOperandInfo, '
+        '$rightOperandInfo, notEqual: $notEqual)',
+        () => _wrapped.equalityOperation_end(
+            wholeExpression, leftOperandInfo, rightOperandInfo,
+            notEqual: notEqual));
   }
 
   @override
@@ -3408,26 +3446,6 @@
   _DemotionResult(this.promotedTypes, this.nonPromotionHistory);
 }
 
-/// [_FlowContext] representing an equality comparison using `==` or `!=`.
-class _EqualityOpContext<Variable extends Object, Type extends Object>
-    extends _BranchContext<Variable, Type> {
-  /// The type of the expression on the LHS of `==` or `!=`.
-  final Type _leftOperandType;
-
-  /// If the LHS of `==` or `!=` is a reference, the thing being referred to.
-  /// Otherwise `null`.
-  final ReferenceWithType<Variable, Type>? _leftOperandReference;
-
-  _EqualityOpContext(ExpressionInfo<Variable, Type>? conditionInfo,
-      this._leftOperandType, this._leftOperandReference)
-      : super(conditionInfo);
-
-  @override
-  String toString() =>
-      '_EqualityOpContext(conditionInfo: $_conditionInfo, lhsType: '
-      '$_leftOperandType)';
-}
-
 class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
         Expression extends Object, Variable extends Object, Type extends Object>
     implements FlowAnalysis<Node, Statement, Expression, Variable, Type> {
@@ -3465,8 +3483,6 @@
   /// Otherwise `null`.
   ReferenceWithType<Variable, Type>? _expressionReference;
 
-  int _functionNestingLevel = 0;
-
   final AssignedVariables<Node, Variable> _assignedVariables;
 
   /// Indicates whether initializers of implicitly typed variables should be
@@ -3602,22 +3618,30 @@
   }
 
   @override
-  void equalityOp_end(Expression wholeExpression, Expression rightOperand,
-      Type rightOperandType,
+  EqualityInfo<Variable, Type> equalityOperand_end(
+          Expression operand, Type type) =>
+      new EqualityInfo<Variable, Type>._(
+          _getExpressionInfo(operand), type, _getExpressionReference(operand));
+
+  @override
+  void equalityOperation_end(
+      Expression wholeExpression,
+      EqualityInfo<Variable, Type>? leftOperandInfo,
+      EqualityInfo<Variable, Type>? rightOperandInfo,
       {bool notEqual = false}) {
-    _EqualityOpContext<Variable, Type> context =
-        _stack.removeLast() as _EqualityOpContext<Variable, Type>;
-    ExpressionInfo<Variable, Type>? lhsInfo = context._conditionInfo;
+    // Note: leftOperandInfo and rightOperandInfo are nullable in the base class
+    // to account for the fact that legacy type promotion doesn't record
+    // information about legacy operands.  But since we are currently in full
+    // (post null safety) flow analysis logic, we can safely assume that they
+    // are not null.
     ReferenceWithType<Variable, Type>? lhsReference =
-        context._leftOperandReference;
-    Type leftOperandType = context._leftOperandType;
-    ExpressionInfo<Variable, Type>? rhsInfo = _getExpressionInfo(rightOperand);
+        leftOperandInfo!._reference;
     ReferenceWithType<Variable, Type>? rhsReference =
-        _getExpressionReference(rightOperand);
+        rightOperandInfo!._reference;
     TypeClassification leftOperandTypeClassification =
-        typeOperations.classifyType(leftOperandType);
+        typeOperations.classifyType(leftOperandInfo._type);
     TypeClassification rightOperandTypeClassification =
-        typeOperations.classifyType(rightOperandType);
+        typeOperations.classifyType(rightOperandInfo._type);
     if (leftOperandTypeClassification == TypeClassification.nullOrEquivalent &&
         rightOperandTypeClassification == TypeClassification.nullOrEquivalent) {
       booleanLiteral(wholeExpression, !notEqual);
@@ -3631,12 +3655,14 @@
       // but weak mode it might produce an "equal" result.  We don't want flow
       // analysis behavior to depend on mode, so we conservatively assume that
       // either result is possible.
-    } else if (lhsInfo is _NullInfo<Variable, Type> && rhsReference != null) {
+    } else if (leftOperandInfo._expressionInfo is _NullInfo<Variable, Type> &&
+        rhsReference != null) {
       ExpressionInfo<Variable, Type> equalityInfo =
           _current.tryMarkNonNullable(typeOperations, rhsReference);
       _storeExpressionInfo(
           wholeExpression, notEqual ? equalityInfo : equalityInfo.invert());
-    } else if (rhsInfo is _NullInfo<Variable, Type> && lhsReference != null) {
+    } else if (rightOperandInfo._expressionInfo is _NullInfo<Variable, Type> &&
+        lhsReference != null) {
       ExpressionInfo<Variable, Type> equalityInfo =
           _current.tryMarkNonNullable(typeOperations, lhsReference);
       _storeExpressionInfo(
@@ -3645,14 +3671,6 @@
   }
 
   @override
-  void equalityOp_rightBegin(Expression leftOperand, Type leftOperandType) {
-    _stack.add(new _EqualityOpContext<Variable, Type>(
-        _getExpressionInfo(leftOperand),
-        leftOperandType,
-        _getExpressionReference(leftOperand)));
-  }
-
-  @override
   ExpressionInfo<Variable, Type>? expressionInfoForTesting(Expression target) =>
       identical(target, _expressionWithInfo) ? _expressionInfo : null;
 
@@ -3734,7 +3752,6 @@
   void functionExpression_begin(Node node) {
     AssignedVariablesNodeInfo<Variable> info =
         _assignedVariables._getInfoForNode(node);
-    ++_functionNestingLevel;
     _current = _current.conservativeJoin(const [], info._written);
     _stack.add(new _FunctionExpressionContext(_current));
     _current = _current.conservativeJoin(_assignedVariables._anywhere._written,
@@ -3743,8 +3760,6 @@
 
   @override
   void functionExpression_end() {
-    --_functionNestingLevel;
-    assert(_functionNestingLevel >= 0);
     _SimpleContext<Variable, Type> context =
         _stack.removeLast() as _FunctionExpressionContext<Variable, Type>;
     _current = context._previous;
@@ -4501,12 +4516,16 @@
   void doStatement_end(Expression condition) {}
 
   @override
-  void equalityOp_end(Expression wholeExpression, Expression rightOperand,
-      Type rightOperandType,
-      {bool notEqual = false}) {}
+  EqualityInfo<Variable, Type>? equalityOperand_end(
+          Expression operand, Type type) =>
+      null;
 
   @override
-  void equalityOp_rightBegin(Expression leftOperand, Type leftOperandType) {}
+  void equalityOperation_end(
+      Expression wholeExpression,
+      EqualityInfo<Variable, Type>? leftOperandInfo,
+      EqualityInfo<Variable, Type>? rightOperandInfo,
+      {bool notEqual = false}) {}
 
   @override
   ExpressionInfo<Variable, Type>? expressionInfoForTesting(Expression target) {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/builders.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/builders.dart
index 03669ef..695f56b 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/builders.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/builders.dart
@@ -114,6 +114,18 @@
   Future<TypeDeclaration> declarationOf(covariant Identifier identifier);
 }
 
+/// The interface used by [Macro]s to get the inferred type for an
+/// [OmittedTypeAnnotation].
+///
+/// Only available in the definition phase of macro expansion.
+abstract class TypeInferrer {
+  /// Infers a real type annotation for [omittedType].
+  ///
+  /// If no type could be inferred, then a type annotation representing the
+  /// dynamic type will be given.
+  Future<TypeAnnotation> inferType(covariant OmittedTypeAnnotation omittedType);
+}
+
 /// The base class for builders in the definition phase. These can convert
 /// any [TypeAnnotation] into its corresponding [TypeDeclaration], and also
 /// reflect more deeply on those.
@@ -123,7 +135,8 @@
         IdentifierResolver,
         TypeResolver,
         ClassIntrospector,
-        TypeDeclarationResolver {}
+        TypeDeclarationResolver,
+        TypeInferrer {}
 
 /// The apis used by [Macro]s that run on classes, to fill in the definitions
 /// of any external declarations within that class.
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
index 6563aef..e0a09f2 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
@@ -211,6 +211,18 @@
   });
 }
 
+class OmittedTypeAnnotationCode extends TypeAnnotationCode {
+  final OmittedTypeAnnotation typeAnnotation;
+
+  OmittedTypeAnnotationCode(this.typeAnnotation);
+
+  @override
+  CodeKind get kind => CodeKind.omittedTypeAnnotation;
+
+  @override
+  List<Object> get parts => [typeAnnotation];
+}
+
 /// A piece of code representing a valid named type parameter.
 class TypeParameterCode implements Code {
   final TypeAnnotationCode? bound;
@@ -250,6 +262,7 @@
   functionTypeAnnotation,
   namedTypeAnnotation,
   nullableTypeAnnotation,
+  omittedTypeAnnotation,
   parameter,
   raw,
   typeParameter,
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
index c24cd91..cc9ef15 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
@@ -56,6 +56,19 @@
   Iterable<TypeAnnotation> get typeArguments;
 }
 
+/// An omitted type annotation.
+///
+/// This will be given whenever there is no explicit type annotation for a
+/// declaration.
+///
+/// These type annotations can still produce valid [Code] objects, which will
+/// result in the inferred type being emitted into the resulting code (or
+/// dynamic).
+///
+/// In the definition phase, you may also ask explicitly for the inferred type
+/// using the `inferType` API.
+abstract class OmittedTypeAnnotation implements TypeAnnotation {}
+
 /// The interface representing a resolved type.
 ///
 /// Resolved types understand exactly what type they represent, and can be
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
index a645eae..e94c61e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -69,15 +69,27 @@
 /// Entrypoint to be spawned with [Isolate.spawnUri] or [Process.start].
 ///
 /// Supports the client side of the macro expansion protocol.
-void main(_, [SendPort? sendPort]) {
+void main(List<String> arguments, [SendPort? sendPort]) {
   // Function that sends the result of a [Serializer] using either [sendPort]
   // or [stdout].
   void Function(Serializer) sendResult;
 
-  // The stream for incoming messages, could be either a ReceivePort or stdin.
+  // The stream for incoming messages, could be either a ReceivePort, stdin, or
+  // a socket.
   Stream<Object?> messageStream;
 
-  withSerializationMode($_modeMarker, () {
+  String? socketAddress;
+  int? socketPort;
+  if (arguments.isNotEmpty) {
+    if (arguments.length != 2) {
+      throw new ArgumentError(
+          'Expected exactly two or zero arguments, got \$arguments.');
+    }
+    socketAddress = arguments.first;
+    socketPort = int.parse(arguments[1]);
+  }
+
+  withSerializationMode($_modeMarker, () async {
     if (sendPort != null) {
       ReceivePort receivePort = new ReceivePort();
       messageStream = receivePort;
@@ -87,12 +99,20 @@
       // isolate.
       sendPort.send(receivePort.sendPort);
     } else {
-      sendResult = _sendStdoutResult;
+      late Stream<List<int>> inputStream;
+      if (socketAddress != null && socketPort != null) {
+        var socket = await Socket.connect(socketAddress, socketPort);
+        sendResult = _sendIOSinkResultFactory(socket);
+        inputStream = socket;
+      } else {
+        sendResult = _sendIOSinkResultFactory(stdout);
+        inputStream = stdin;
+      }
       if (serializationMode == SerializationMode.byteDataClient) {
-        messageStream = MessageGrouper(stdin).messageStream;
+        messageStream = MessageGrouper(inputStream).messageStream;
       } else if (serializationMode == SerializationMode.jsonClient) {
-        messageStream = stdin
-          .transform(const Utf8Decoder())
+        messageStream = const Utf8Decoder()
+          .bind(inputStream)
           .transform(const LineSplitter())
           .map((line) => jsonDecode(line)!);
       } else {
@@ -289,10 +309,14 @@
         sendRequest,
         remoteInstance: request.classIntrospector,
         serializationZoneId: request.serializationZoneId);
+    var typeInferrer = ClientTypeInferrer(
+        sendRequest,
+        remoteInstance: request.typeInferrer,
+        serializationZoneId: request.serializationZoneId);
 
     var result = await executeDefinitionMacro(
         instance, request.declaration, identifierResolver, classIntrospector,
-        typeResolver, typeDeclarationResolver);
+        typeResolver, typeDeclarationResolver, typeInferrer);
     return new SerializableResponse(
         responseType: MessageType.macroExecutionResult,
         response: result,
@@ -335,26 +359,28 @@
   }
 }
 
-/// Sends [serializer.result] to [stdout].
+/// Returns a function which takes a [Serializer] and sends its result to
+/// [sink].
 ///
 /// Serializes the result to a string if using JSON.
-void _sendStdoutResult(Serializer serializer) {
-  if (serializationMode == SerializationMode.jsonClient) {
-    stdout.writeln(jsonEncode(serializer.result));
-  } else if (serializationMode == SerializationMode.byteDataClient) {
-    Uint8List result = (serializer as ByteDataSerializer).result;
-    int length = result.lengthInBytes;
-    stdout.add([
-      length >> 24 & 0xff,
-      length >> 16 & 0xff,
-      length >> 8 & 0xff,
-      length & 0xff,
-    ]);
-    stdout.add(result);
-  } else {
-    throw new UnsupportedError(
-        'Unsupported serialization mode \$serializationMode for '
-        'ProcessExecutor');
-  }
-}
+void Function(Serializer) _sendIOSinkResultFactory(IOSink sink) =>
+    (Serializer serializer) {
+      if (serializationMode == SerializationMode.jsonClient) {
+        sink.writeln(jsonEncode(serializer.result));
+      } else if (serializationMode == SerializationMode.byteDataClient) {
+        Uint8List result = (serializer as ByteDataSerializer).result;
+        int length = result.lengthInBytes;
+        sink.add([
+          length >> 24 & 0xff,
+          length >> 16 & 0xff,
+          length >> 8 & 0xff,
+          length & 0xff,
+        ]);
+        sink.add(result);
+      } else {
+        throw new UnsupportedError(
+            'Unsupported serialization mode \$serializationMode for '
+            'ProcessExecutor');
+      }
+    };
 ''';
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
index c80c9a5..f178c40 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
@@ -66,15 +66,30 @@
       IdentifierResolver identifierResolver,
       TypeResolver typeResolver,
       ClassIntrospector classIntrospector,
-      TypeDeclarationResolver typeDeclarationResolver);
+      TypeDeclarationResolver typeDeclarationResolver,
+      TypeInferrer typeInferrer);
 
   /// Combines multiple [MacroExecutionResult]s into a single library
   /// augmentation file, and returns a [String] representing that file.
   ///
   /// The [resolveIdentifier] argument should return the import uri to be used
   /// for that identifier.
-  String buildAugmentationLibrary(Iterable<MacroExecutionResult> macroResults,
-      ResolvedIdentifier Function(Identifier) resolveIdentifier);
+  ///
+  /// The [inferOmittedType] argument is used to get the inferred type for a
+  /// given [OmittedTypeAnnotation].
+  ///
+  /// If [omittedTypes] is provided, [inferOmittedType] is allowed to return
+  /// `null` for types that have not yet been inferred. In this case a fresh
+  /// name will be used for the omitted type in the generated library code and
+  /// the omitted type will be mapped to the fresh name in [omittedTypes].
+  ///
+  /// The generated library files content must be deterministic, including the
+  /// generation of fresh names for import prefixes and omitted types.
+  String buildAugmentationLibrary(
+      Iterable<MacroExecutionResult> macroResults,
+      ResolvedIdentifier Function(Identifier) resolveIdentifier,
+      TypeAnnotation? Function(OmittedTypeAnnotation) inferOmittedType,
+      {Map<OmittedTypeAnnotation, String>? omittedTypes});
 
   /// Tell the executor to shut down and clean up any resources it may have
   /// allocated.
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/augmentation_library.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/augmentation_library.dart
index fd0b26bc..0ecb10f 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/augmentation_library.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/augmentation_library.dart
@@ -9,48 +9,85 @@
 /// [MacroExecutor.buildAugmentationLibrary].
 mixin AugmentationLibraryBuilder on MacroExecutor {
   @override
-  String buildAugmentationLibrary(Iterable<MacroExecutionResult> macroResults,
-      ResolvedIdentifier Function(Identifier) resolveIdentifier) {
-    StringBuffer importsBuffer = new StringBuffer();
-    StringBuffer directivesBuffer = new StringBuffer();
-    Map<Uri, String> importPrefixes = {};
-    int nextPrefix = 0;
+  String buildAugmentationLibrary(
+      Iterable<MacroExecutionResult> macroResults,
+      ResolvedIdentifier Function(Identifier) resolveIdentifier,
+      TypeAnnotation? Function(OmittedTypeAnnotation) typeInferrer,
+      {Map<OmittedTypeAnnotation, String>? omittedTypes}) {
+    Map<Uri, _SynthesizedNamePart> importNames = {};
+    Map<OmittedTypeAnnotation, _SynthesizedNamePart> typeNames = {};
+    List<_Part> importParts = [];
+    List<_Part> directivesParts = [];
+    List<_StringPart> stringParts = [];
+    StringBuffer directivesStringPartBuffer = new StringBuffer();
+
+    void flushStringParts() {
+      if (directivesStringPartBuffer.isNotEmpty) {
+        _StringPart stringPart =
+            new _StringPart(directivesStringPartBuffer.toString());
+        directivesParts.add(stringPart);
+        stringParts.add(stringPart);
+        directivesStringPartBuffer = new StringBuffer();
+      }
+    }
 
     // Keeps track of the last part written in `lastDirectivePart`.
     String lastDirectivePart = '';
-    void writeDirectivePart(String part) {
+    void writeDirectiveStringPart(String part) {
       lastDirectivePart = part;
-      directivesBuffer.write(part);
+      directivesStringPartBuffer.write(part);
+    }
+
+    void writeDirectiveSynthesizedNamePart(_SynthesizedNamePart part) {
+      flushStringParts();
+      lastDirectivePart = '';
+      directivesParts.add(part);
     }
 
     void buildCode(Code code) {
       for (Object part in code.parts) {
         if (part is String) {
-          writeDirectivePart(part);
+          writeDirectiveStringPart(part);
         } else if (part is Code) {
           buildCode(part);
         } else if (part is Identifier) {
           ResolvedIdentifier resolved = resolveIdentifier(part);
-          String? prefix;
+          _SynthesizedNamePart? prefix;
           if (resolved.uri != null) {
-            prefix = importPrefixes.putIfAbsent(resolved.uri!, () {
-              String prefix = 'i${nextPrefix++}';
-              importsBuffer.writeln("import '${resolved.uri}' as $prefix;");
+            prefix = importNames.putIfAbsent(resolved.uri!, () {
+              _SynthesizedNamePart prefix = new _SynthesizedNamePart();
+              importParts.add(new _StringPart("import '${resolved.uri}' as "));
+              importParts.add(prefix);
+              importParts.add(new _StringPart(";\n"));
               return prefix;
             });
           }
           if (resolved.kind == IdentifierKind.instanceMember) {
             // Qualify with `this.` if we don't have a receiver.
             if (!lastDirectivePart.trimRight().endsWith('.')) {
-              writeDirectivePart('this.');
+              writeDirectiveStringPart('this.');
             }
           } else if (prefix != null) {
-            writeDirectivePart('${prefix}.');
+            writeDirectiveSynthesizedNamePart(prefix);
+            writeDirectiveStringPart('.');
           }
           if (resolved.kind == IdentifierKind.staticInstanceMember) {
-            writeDirectivePart('${resolved.staticScope!}.');
+            writeDirectiveStringPart('${resolved.staticScope!}.');
           }
-          writeDirectivePart('${part.name}');
+          writeDirectiveStringPart('${part.name}');
+        } else if (part is OmittedTypeAnnotation) {
+          TypeAnnotation? type = typeInferrer(part);
+          if (type == null) {
+            if (omittedTypes != null) {
+              _SynthesizedNamePart name =
+                  typeNames.putIfAbsent(part, () => new _SynthesizedNamePart());
+              writeDirectiveSynthesizedNamePart(name);
+            } else {
+              throw new ArgumentError("No type inferred for $part");
+            }
+          } else {
+            buildCode(type.code);
+          }
         } else {
           throw new ArgumentError(
               'Code objects only support String, Identifier, and Code '
@@ -63,7 +100,7 @@
     for (MacroExecutionResult result in macroResults) {
       for (DeclarationCode augmentation in result.libraryAugmentations) {
         buildCode(augmentation);
-        directivesBuffer.writeln();
+        writeDirectiveStringPart('\n');
       }
       for (MapEntry<String, Iterable<DeclarationCode>> entry
           in result.classAugmentations.entries) {
@@ -74,13 +111,79 @@
     }
     for (MapEntry<String, List<DeclarationCode>> entry
         in mergedClassResults.entries) {
-      directivesBuffer.writeln('augment class ${entry.key} {');
+      writeDirectiveStringPart('augment class ${entry.key} {\n');
       for (DeclarationCode augmentation in entry.value) {
         buildCode(augmentation);
-        directivesBuffer.writeln();
+        writeDirectiveStringPart('\n');
       }
-      directivesBuffer.writeln('}');
+      writeDirectiveStringPart('}\n');
     }
-    return '$importsBuffer\n\n$directivesBuffer';
+    flushStringParts();
+
+    if (importNames.isNotEmpty) {
+      String prefix = _computeFreshPrefix(stringParts, 'prefix');
+      int index = 0;
+      for (_SynthesizedNamePart part in importNames.values) {
+        part.text = '$prefix${index++}';
+      }
+    }
+    if (omittedTypes != null && typeNames.isNotEmpty) {
+      String prefix = _computeFreshPrefix(stringParts, 'OmittedType');
+      int index = 0;
+      typeNames.forEach(
+          (OmittedTypeAnnotation omittedType, _SynthesizedNamePart part) {
+        String name = '$prefix${index++}';
+        part.text = name;
+        omittedTypes[omittedType] = name;
+      });
+    }
+
+    StringBuffer sb = new StringBuffer();
+    for (_Part part in importParts) {
+      sb.write(part.text);
+    }
+    sb.write('\n');
+    for (_Part part in directivesParts) {
+      sb.write(part.text);
+    }
+
+    return sb.toString();
   }
 }
+
+abstract class _Part {
+  String get text;
+}
+
+class _SynthesizedNamePart implements _Part {
+  late String text;
+}
+
+class _StringPart implements _Part {
+  final String text;
+
+  _StringPart(this.text);
+}
+
+/// Computes a name starting with [name] that is unique with respect to the
+/// text in [stringParts].
+///
+/// This algorithm assumes that no two parts in [stringParts] occur in direct
+/// sequence where they are used, i.e. there is always at least one
+/// [_SynthesizedNamePart] between them.
+String _computeFreshPrefix(List<_StringPart> stringParts, String name) {
+  int index = -1;
+  String prefix = name;
+  for (_StringPart part in stringParts) {
+    while (part.text.contains(prefix)) {
+      index++;
+      prefix = '$name$index';
+    }
+  }
+  if (index > 0) {
+    // Add a separator when an index was needed. This is to ensure that
+    // suffixing number to [prefix] doesn't blend the digits.
+    prefix = '${prefix}_';
+  }
+  return prefix;
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/builder_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/builder_impls.dart
index 6f522e0..b7391e9 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/builder_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/builder_impls.dart
@@ -129,14 +129,16 @@
 
 /// Base class for all [DefinitionBuilder]s.
 class DefinitionBuilderBase extends DeclarationBuilderBase
-    implements TypeDeclarationResolver {
+    implements TypeDeclarationResolver, TypeInferrer {
   final TypeDeclarationResolver typeDeclarationResolver;
+  final TypeInferrer typeInferrer;
 
   DefinitionBuilderBase(
       IdentifierResolver identifierResolver,
       ClassIntrospector classIntrospector,
       TypeResolver typeResolver,
       this.typeDeclarationResolver,
+      this.typeInferrer,
       {Map<String, List<DeclarationCode>>? parentClassAugmentations,
       List<DeclarationCode>? parentLibraryAugmentations})
       : super(identifierResolver, classIntrospector, typeResolver,
@@ -146,6 +148,10 @@
   @override
   Future<TypeDeclaration> declarationOf(IdentifierImpl identifier) =>
       typeDeclarationResolver.declarationOf(identifier);
+
+  @override
+  Future<TypeAnnotation> inferType(OmittedTypeAnnotationImpl omittedType) =>
+      typeInferrer.inferType(omittedType);
 }
 
 class ClassDefinitionBuilderImpl extends DefinitionBuilderBase
@@ -159,10 +165,11 @@
       ClassIntrospector classIntrospector,
       TypeResolver typeResolver,
       TypeDeclarationResolver typeDeclarationResolver,
+      TypeInferrer typeInferrer,
       {Map<String, List<DeclarationCode>>? parentClassAugmentations,
       List<DeclarationCode>? parentLibraryAugmentations})
       : super(identifierResolver, classIntrospector, typeResolver,
-            typeDeclarationResolver,
+            typeDeclarationResolver, typeInferrer,
             parentClassAugmentations: parentClassAugmentations,
             parentLibraryAugmentations: parentLibraryAugmentations);
 
@@ -173,7 +180,7 @@
         (await classIntrospector.constructorsOf(declaration))
             .firstWhere((constructor) => constructor.identifier == identifier);
     return new ConstructorDefinitionBuilderImpl(constructor, identifierResolver,
-        classIntrospector, typeResolver, typeDeclarationResolver,
+        classIntrospector, typeResolver, typeDeclarationResolver, typeInferrer,
         parentClassAugmentations: _classAugmentations,
         parentLibraryAugmentations: _libraryAugmentations);
   }
@@ -183,7 +190,7 @@
     FieldDeclaration field = (await classIntrospector.fieldsOf(declaration))
         .firstWhere((field) => field.identifier == identifier);
     return new VariableDefinitionBuilderImpl(field, identifierResolver,
-        classIntrospector, typeResolver, typeDeclarationResolver,
+        classIntrospector, typeResolver, typeDeclarationResolver, typeInferrer,
         parentClassAugmentations: _classAugmentations,
         parentLibraryAugmentations: _libraryAugmentations);
   }
@@ -193,7 +200,7 @@
     MethodDeclaration method = (await classIntrospector.methodsOf(declaration))
         .firstWhere((method) => method.identifier == identifier);
     return new FunctionDefinitionBuilderImpl(method, identifierResolver,
-        classIntrospector, typeResolver, typeDeclarationResolver,
+        classIntrospector, typeResolver, typeDeclarationResolver, typeInferrer,
         parentClassAugmentations: _classAugmentations,
         parentLibraryAugmentations: _libraryAugmentations);
   }
@@ -210,10 +217,11 @@
       ClassIntrospector classIntrospector,
       TypeResolver typeResolver,
       TypeDeclarationResolver typeDeclarationResolver,
+      TypeInferrer typeInferrer,
       {Map<String, List<DeclarationCode>>? parentClassAugmentations,
       List<DeclarationCode>? parentLibraryAugmentations})
       : super(identifierResolver, classIntrospector, typeResolver,
-            typeDeclarationResolver,
+            typeDeclarationResolver, typeInferrer,
             parentClassAugmentations: parentClassAugmentations,
             parentLibraryAugmentations: parentLibraryAugmentations);
 
@@ -242,10 +250,11 @@
       ClassIntrospector classIntrospector,
       TypeResolver typeResolver,
       TypeDeclarationResolver typeDeclarationResolver,
+      TypeInferrer typeInferrer,
       {Map<String, List<DeclarationCode>>? parentClassAugmentations,
       List<DeclarationCode>? parentLibraryAugmentations})
       : super(identifierResolver, classIntrospector, typeResolver,
-            typeDeclarationResolver,
+            typeDeclarationResolver, typeInferrer,
             parentClassAugmentations: parentClassAugmentations,
             parentLibraryAugmentations: parentLibraryAugmentations);
 
@@ -272,10 +281,11 @@
       ClassIntrospector classIntrospector,
       TypeResolver typeResolver,
       TypeDeclarationResolver typeDeclarationResolver,
+      TypeInferrer typeInferrer,
       {Map<String, List<DeclarationCode>>? parentClassAugmentations,
       List<DeclarationCode>? parentLibraryAugmentations})
       : super(identifierResolver, classIntrospector, typeResolver,
-            typeDeclarationResolver,
+            typeDeclarationResolver, typeInferrer,
             parentClassAugmentations: parentClassAugmentations,
             parentLibraryAugmentations: parentLibraryAugmentations);
 
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/execute_macro.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/execute_macro.dart
index af3224f..67b8171 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/execute_macro.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/execute_macro.dart
@@ -105,13 +105,19 @@
     IdentifierResolver identifierResolver,
     ClassIntrospector classIntrospector,
     TypeResolver typeResolver,
-    TypeDeclarationResolver typeDeclarationResolver) async {
+    TypeDeclarationResolver typeDeclarationResolver,
+    TypeInferrer typeInferrer) async {
   if (declaration is FunctionDeclaration) {
     if (macro is ConstructorDefinitionMacro &&
         declaration is ConstructorDeclaration) {
       ConstructorDefinitionBuilderImpl builder =
-          new ConstructorDefinitionBuilderImpl(declaration, identifierResolver,
-              classIntrospector, typeResolver, typeDeclarationResolver);
+          new ConstructorDefinitionBuilderImpl(
+              declaration,
+              identifierResolver,
+              classIntrospector,
+              typeResolver,
+              typeDeclarationResolver,
+              typeInferrer);
       await macro.buildDefinitionForConstructor(declaration, builder);
       return builder.result;
     } else {
@@ -120,7 +126,8 @@
           identifierResolver,
           classIntrospector,
           typeResolver,
-          typeDeclarationResolver);
+          typeDeclarationResolver,
+          typeInferrer);
       if (macro is MethodDefinitionMacro && declaration is MethodDeclaration) {
         await macro.buildDefinitionForMethod(declaration, builder);
         return builder.result;
@@ -135,7 +142,8 @@
         identifierResolver,
         classIntrospector,
         typeResolver,
-        typeDeclarationResolver);
+        typeDeclarationResolver,
+        typeInferrer);
     if (macro is FieldDefinitionMacro && declaration is FieldDeclaration) {
       await macro.buildDefinitionForField(declaration, builder);
       return builder.result;
@@ -149,7 +157,8 @@
         identifierResolver,
         classIntrospector,
         typeResolver,
-        typeDeclarationResolver);
+        typeDeclarationResolver,
+        typeInferrer);
     await macro.buildDefinitionForClass(declaration, builder);
     return builder.result;
   }
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/executor_base.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/executor_base.dart
index 940fa5a..5609883 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/executor_base.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/executor_base.dart
@@ -114,6 +114,21 @@
               response.serialize(serializer);
               sendResult(serializer);
               break;
+            case MessageType.inferTypeRequest:
+              InferTypeRequest request =
+                  new InferTypeRequest.deserialize(deserializer, zoneId);
+              TypeAnnotationImpl inferredType =
+                  await (request.typeInferrer.instance as TypeInferrer)
+                      .inferType(request.omittedType) as TypeAnnotationImpl;
+              SerializableResponse response = new SerializableResponse(
+                  response: inferredType,
+                  requestId: request.id,
+                  responseType: MessageType.remoteInstance,
+                  serializationZoneId: zoneId);
+              Serializer serializer = serializerFactory();
+              response.serialize(serializer);
+              sendResult(serializer);
+              break;
             case MessageType.isExactlyTypeRequest:
               IsExactlyTypeRequest request =
                   new IsExactlyTypeRequest.deserialize(deserializer, zoneId);
@@ -147,15 +162,35 @@
             case MessageType.declarationOfRequest:
               DeclarationOfRequest request =
                   new DeclarationOfRequest.deserialize(deserializer, zoneId);
-              TypeDeclarationResolver resolver = request
-                  .typeDeclarationResolver.instance as TypeDeclarationResolver;
-              SerializableResponse response = new SerializableResponse(
-                  requestId: request.id,
-                  responseType: MessageType.remoteInstance,
-                  response: (await resolver.declarationOf(request.identifier)
-                      // TODO: Consider refactoring to avoid the need for this.
-                      as TypeDeclarationImpl),
-                  serializationZoneId: zoneId);
+              SerializableResponse response;
+              try {
+                TypeDeclarationResolver resolver = request
+                    .typeDeclarationResolver
+                    .instance as TypeDeclarationResolver;
+                response = new SerializableResponse(
+                    requestId: request.id,
+                    responseType: MessageType.remoteInstance,
+                    response: (await resolver.declarationOf(request.identifier)
+                        // TODO: Consider refactoring to avoid the need for
+                        //  this.
+                        as TypeDeclarationImpl),
+                    serializationZoneId: zoneId);
+              } on ArgumentError catch (error) {
+                response = new SerializableResponse(
+                    error: '$error',
+                    requestId: request.id,
+                    responseType: MessageType.argumentError,
+                    serializationZoneId: zoneId);
+              } catch (error, stackTrace) {
+                // TODO(johnniwinther,jakemac): How should we handle errors in
+                // general?
+                response = new SerializableResponse(
+                    error: '$error',
+                    stackTrace: '$stackTrace',
+                    requestId: request.id,
+                    responseType: MessageType.error,
+                    serializationZoneId: zoneId);
+              }
               Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendResult(serializer);
@@ -278,8 +313,11 @@
 
   /// These calls are handled by the higher level executor.
   @override
-  String buildAugmentationLibrary(Iterable<MacroExecutionResult> macroResults,
-          ResolvedIdentifier Function(Identifier) resolveIdentifier) =>
+  String buildAugmentationLibrary(
+          Iterable<MacroExecutionResult> macroResults,
+          ResolvedIdentifier Function(Identifier) resolveIdentifier,
+          TypeAnnotation? Function(OmittedTypeAnnotation) inferOmittedType,
+          {Map<OmittedTypeAnnotation, String>? omittedTypes}) =>
       throw new StateError('Unreachable');
 
   @override
@@ -313,7 +351,8 @@
           IdentifierResolver identifierResolver,
           TypeResolver typeResolver,
           ClassIntrospector classIntrospector,
-          TypeDeclarationResolver typeDeclarationResolver) =>
+          TypeDeclarationResolver typeDeclarationResolver,
+          TypeInferrer typeInferrer) =>
       _sendRequest((zoneId) => new ExecuteDefinitionsPhaseRequest(
           macro,
           declaration,
@@ -333,6 +372,10 @@
               instance: typeDeclarationResolver,
               id: RemoteInstance.uniqueId,
               kind: RemoteInstanceKind.typeDeclarationResolver),
+          new RemoteInstanceImpl(
+              instance: typeInferrer,
+              id: RemoteInstance.uniqueId,
+              kind: RemoteInstanceKind.typeInferrer),
           serializationZoneId: zoneId));
 
   @override
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart
index 857674e..29c443b 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart
@@ -156,6 +156,18 @@
   }
 }
 
+class OmittedTypeAnnotationImpl extends TypeAnnotationImpl
+    implements OmittedTypeAnnotation {
+  OmittedTypeAnnotationImpl({required int id})
+      : super(id: id, isNullable: false);
+
+  @override
+  TypeAnnotationCode get code => new OmittedTypeAnnotationCode(this);
+
+  @override
+  RemoteInstanceKind get kind => RemoteInstanceKind.omittedTypeAnnotation;
+}
+
 abstract class DeclarationImpl extends RemoteInstance implements Declaration {
   final IdentifierImpl identifier;
 
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/message_grouper.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/message_grouper.dart
index 7e2d390..382dd81 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/message_grouper.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/message_grouper.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:math';
 import 'dart:typed_data';
 
 /// Collects messages from an input stream of bytes.
@@ -14,18 +13,11 @@
   /// The input bytes stream subscription.
   late final StreamSubscription _inputStreamSubscription;
 
-  /// The length of the current message to read, or `-1` if we are currently
-  /// reading the length.
-  int _length = -1;
-
   /// The buffer to store the length bytes in.
-  BytesBuilder _lengthBuffer = new BytesBuilder();
+  final _FixedBuffer _lengthBuffer = new _FixedBuffer(4);
 
   /// If reading raw data, buffer for the data.
-  Uint8List _messageBuffer = new Uint8List(0);
-
-  /// The position to write the next byte in [_messageBuffer].
-  int _messagePos = 0;
+  _FixedBuffer? _messageBuffer;
 
   late StreamController<Uint8List> _messageStreamController =
       new StreamController<Uint8List>(onCancel: () {
@@ -38,45 +30,36 @@
   }
 
   void _handleBytes(List<int> bytes, [int offset = 0]) {
-    if (_length == -1) {
-      while (_lengthBuffer.length < 4 && offset < bytes.length) {
+    final _FixedBuffer? messageBuffer = _messageBuffer;
+    if (messageBuffer == null) {
+      while (offset < bytes.length && !_lengthBuffer.isReady) {
         _lengthBuffer.addByte(bytes[offset++]);
       }
-      if (_lengthBuffer.length >= 4) {
-        Uint8List lengthBytes = _lengthBuffer.takeBytes();
-        _length = lengthBytes[0] << 24 |
-            lengthBytes[1] << 16 |
-            lengthBytes[2] << 8 |
-            lengthBytes[3];
+      if (_lengthBuffer.isReady) {
+        int length = _lengthBuffer[0] << 24 |
+            _lengthBuffer[1] << 16 |
+            _lengthBuffer[2] << 8 |
+            _lengthBuffer[3];
+        // Reset the length reading state.
+        _lengthBuffer.reset();
+        // Switch to the message payload reading state.
+        _messageBuffer = new _FixedBuffer(length);
+        _handleBytes(bytes, offset);
+      } else {
+        // Continue reading the length.
+        return;
       }
-    }
+    } else {
+      // Read the data from `bytes`.
+      while (offset < bytes.length && !messageBuffer.isReady) {
+        messageBuffer.addByte(bytes[offset++]);
+      }
 
-    // Just pass along `bytes` without a copy if we can, and reset our state
-    if (offset == 0 && bytes.length == _length && bytes is Uint8List) {
-      _length = -1;
-      _messageStreamController.add(bytes);
-      return;
-    }
-
-    // Initialize a new buffer.
-    if (_messagePos == 0) {
-      _messageBuffer = new Uint8List(_length);
-    }
-
-    // Read the data from `bytes`.
-    int lenToRead = min(_length - _messagePos, bytes.length - offset);
-    while (lenToRead-- > 0) {
-      _messageBuffer[_messagePos++] = bytes[offset++];
-    }
-
-    // If we completed a message, add it to the output stream, reset our state,
-    // and call ourselves again if we have more data to read.
-    if (_messagePos >= _length) {
-      _messageStreamController.add(_messageBuffer);
-      _length = -1;
-      _messagePos = 0;
-
-      if (offset < bytes.length) {
+      // If we completed a message, add it to the output stream.
+      if (messageBuffer.isReady) {
+        _messageStreamController.add(messageBuffer.bytes);
+        // Switch to the length reading state.
+        _messageBuffer = null;
         _handleBytes(bytes, offset);
       }
     }
@@ -89,3 +72,27 @@
     _messageStreamController.close();
   }
 }
+
+/// A buffer of fixed length.
+class _FixedBuffer {
+  final Uint8List bytes;
+
+  /// The offset in [bytes].
+  int _offset = 0;
+
+  _FixedBuffer(int length) : bytes = new Uint8List(length);
+
+  /// Return `true` when the required number of bytes added.
+  bool get isReady => _offset == bytes.length;
+
+  int operator [](int index) => bytes[index];
+
+  void addByte(int byte) {
+    bytes[_offset++] = byte;
+  }
+
+  /// Reset the number of added bytes to zero.
+  void reset() {
+    _offset = 0;
+  }
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/multi_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/multi_executor.dart
index aa7c907..654669d 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/multi_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/multi_executor.dart
@@ -49,14 +49,16 @@
           IdentifierResolver identifierResolver,
           TypeResolver typeResolver,
           ClassIntrospector classIntrospector,
-          TypeDeclarationResolver typeDeclarationResolver) =>
+          TypeDeclarationResolver typeDeclarationResolver,
+          TypeInferrer typeInferrer) =>
       _executors[macro]!.executeDefinitionsPhase(
           macro,
           declaration,
           identifierResolver,
           typeResolver,
           classIntrospector,
-          typeDeclarationResolver);
+          typeDeclarationResolver,
+          typeInferrer);
 
   @override
   Future<MacroExecutionResult> executeTypesPhase(MacroInstanceIdentifier macro,
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart
index ed67219..07b02f0 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart
@@ -23,19 +23,25 @@
 /// programs spawned must use the corresponding `client` variant.
 ///
 /// This is the only public api exposed by this library.
-Future<MacroExecutor> start(SerializationMode serializationMode) async =>
+Future<MacroExecutor> start(SerializationMode serializationMode,
+        CommunicationChannel communicationChannel) async =>
     new MultiMacroExecutor((Uri library, String name,
         {Uri? precompiledKernelUri}) {
+      // TODO: We actually assume this is a full precompiled AOT binary, and
+      // not a kernel file. We launch it directly using `Process.start`.
       if (precompiledKernelUri == null) {
         throw new UnsupportedError(
             'This environment requires a non-null `precompiledKernelUri` to be '
             'passed when loading macros.');
       }
-
-      // TODO: We actually assume this is a full precompiled AOT binary, and not
-      // a kernel file. We launch it directly using `Process.start`.
-      return _SingleProcessMacroExecutor.start(
-          library, name, serializationMode, precompiledKernelUri.toFilePath());
+      switch (communicationChannel) {
+        case CommunicationChannel.stdio:
+          return _SingleProcessMacroExecutor.startWithStdio(library, name,
+              serializationMode, precompiledKernelUri.toFilePath());
+        case CommunicationChannel.socket:
+          return _SingleProcessMacroExecutor.startWithSocket(library, name,
+              serializationMode, precompiledKernelUri.toFilePath());
+      }
     });
 
 /// Actual implementation of the separate process based macro executor.
@@ -55,8 +61,65 @@
       : super(
             messageStream: messageStream, serializationMode: serializationMode);
 
-  static Future<_SingleProcessMacroExecutor> start(Uri library, String name,
-      SerializationMode serializationMode, String programPath) async {
+  static Future<_SingleProcessMacroExecutor> startWithSocket(
+      Uri library,
+      String name,
+      SerializationMode serializationMode,
+      String programPath) async {
+    late ServerSocket serverSocket;
+    // Try an ipv6 address loopback first, and fall back on ipv4.
+    try {
+      serverSocket = await ServerSocket.bind(InternetAddress.loopbackIPv6, 0);
+    } on SocketException catch (_) {
+      serverSocket = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
+    }
+    Process process = await Process.start(programPath, [
+      serverSocket.address.address,
+      serverSocket.port.toString(),
+    ]);
+    process.stderr
+        .transform(const Utf8Decoder())
+        .listen((content) => throw new RemoteException(content));
+    process.stdout.transform(const Utf8Decoder()).listen(
+        (event) => print('Stdout from MacroExecutor at $programPath:\n$event'));
+
+    Completer<Socket> clientCompleter = new Completer();
+    serverSocket.listen((client) {
+      clientCompleter.complete(client);
+    });
+    Socket client = await clientCompleter.future;
+
+    Stream<Object> messageStream;
+
+    if (serializationMode == SerializationMode.byteDataServer) {
+      messageStream = new MessageGrouper(client).messageStream;
+    } else if (serializationMode == SerializationMode.jsonServer) {
+      messageStream = const Utf8Decoder()
+          .bind(client)
+          .transform(const LineSplitter())
+          .map((line) => jsonDecode(line)!);
+    } else {
+      throw new UnsupportedError(
+          'Unsupported serialization mode \$serializationMode for '
+          'ProcessExecutor');
+    }
+
+    return new _SingleProcessMacroExecutor(
+        onClose: () {
+          client.close();
+          serverSocket.close();
+          process.kill();
+        },
+        messageStream: messageStream,
+        outSink: client,
+        serializationMode: serializationMode);
+  }
+
+  static Future<_SingleProcessMacroExecutor> startWithStdio(
+      Uri library,
+      String name,
+      SerializationMode serializationMode,
+      String programPath) async {
     Process process = await Process.start(programPath, []);
     process.stderr
         .transform(const Utf8Decoder())
@@ -115,3 +178,8 @@
     }
   }
 }
+
+enum CommunicationChannel {
+  socket,
+  stdio,
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/protocol.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/protocol.dart
index 73d051c6..b868726 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/protocol.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/protocol.dart
@@ -92,6 +92,10 @@
         deserializer.moveNext();
         stackTrace = deserializer.expectNullableString();
         break;
+      case MessageType.argumentError:
+        deserializer.moveNext();
+        error = deserializer.expectString();
+        break;
       case MessageType.macroClassIdentifier:
         response = new MacroClassIdentifierImpl.deserialize(deserializer);
         break;
@@ -140,6 +144,9 @@
         serializer.addString(error!.toString());
         serializer.addNullableString(stackTrace);
         break;
+      case MessageType.argumentError:
+        serializer.addString(error!.toString());
+        break;
       default:
         response.serializeNullable(serializer);
     }
@@ -322,6 +329,7 @@
   final RemoteInstanceImpl typeResolver;
   final RemoteInstanceImpl classIntrospector;
   final RemoteInstanceImpl typeDeclarationResolver;
+  final RemoteInstanceImpl typeInferrer;
 
   ExecuteDefinitionsPhaseRequest(
       this.macro,
@@ -330,6 +338,7 @@
       this.typeResolver,
       this.classIntrospector,
       this.typeDeclarationResolver,
+      this.typeInferrer,
       {required int serializationZoneId})
       : super(serializationZoneId: serializationZoneId);
 
@@ -343,6 +352,7 @@
         typeResolver = RemoteInstance.deserialize(deserializer),
         classIntrospector = RemoteInstance.deserialize(deserializer),
         typeDeclarationResolver = RemoteInstance.deserialize(deserializer),
+        typeInferrer = RemoteInstance.deserialize(deserializer),
         super.deserialize(deserializer, serializationZoneId);
 
   void serialize(Serializer serializer) {
@@ -353,6 +363,7 @@
     typeResolver.serialize(serializer);
     classIntrospector.serialize(serializer);
     typeDeclarationResolver.serialize(serializer);
+    typeInferrer.serialize(serializer);
 
     super.serialize(serializer);
   }
@@ -519,8 +530,35 @@
   }
 }
 
+/// A request to get an inferred [TypeAnnotation] for an
+/// [OmittedTypeAnnotation].
+class InferTypeRequest extends Request {
+  final OmittedTypeAnnotationImpl omittedType;
+  final RemoteInstanceImpl typeInferrer;
+
+  InferTypeRequest(this.omittedType, this.typeInferrer,
+      {required int serializationZoneId})
+      : super(serializationZoneId: serializationZoneId);
+
+  /// When deserializing we have already consumed the message type, so we don't
+  /// consume it again.
+  InferTypeRequest.deserialize(
+      Deserializer deserializer, int serializationZoneId)
+      : omittedType = RemoteInstance.deserialize(deserializer),
+        typeInferrer = RemoteInstance.deserialize(deserializer),
+        super.deserialize(deserializer, serializationZoneId);
+
+  @override
+  void serialize(Serializer serializer) {
+    serializer.addInt(MessageType.inferTypeRequest.index);
+    omittedType.serialize(serializer);
+    typeInferrer.serialize(serializer);
+    super.serialize(serializer);
+  }
+}
+
 /// Client side implementation of an [IdentifierResolver], which creates a
-/// [ResolveIdentifierRequest] and passes it to a given [sendRequest] function
+/// [ResolveIdentifierRequest] and passes it to a given [_sendRequest] function
 /// which can return the [Response].
 class ClientIdentifierResolver implements IdentifierResolver {
   /// The actual remote instance of this type resolver.
@@ -735,6 +773,31 @@
   }
 }
 
+/// Client side implementation of a [TypeInferrer], converts all
+/// invocations into remote procedure calls.
+class ClientTypeInferrer implements TypeInferrer {
+  /// The actual remote instance of this type resolver.
+  final RemoteInstanceImpl remoteInstance;
+
+  /// The ID of the zone in which to find the original type resolver.
+  final int serializationZoneId;
+
+  /// A function that can send a request and return a response using an
+  /// arbitrary communication channel.
+  final Future<Response> Function(Request request) sendRequest;
+
+  ClientTypeInferrer(this.sendRequest,
+      {required this.remoteInstance, required this.serializationZoneId});
+
+  @override
+  Future<TypeAnnotation> inferType(
+      OmittedTypeAnnotationImpl omittedType) async {
+    InferTypeRequest request = new InferTypeRequest(omittedType, remoteInstance,
+        serializationZoneId: serializationZoneId);
+    return _handleResponse<TypeAnnotation>(await sendRequest(request));
+  }
+}
+
 /// An exception that occurred remotely, the exception object and stack trace
 /// are serialized as [String]s and both included in the [toString] output.
 class RemoteException implements Exception {
@@ -752,11 +815,15 @@
 T _handleResponse<T>(Response response) {
   if (response.responseType == MessageType.error) {
     throw new RemoteException(response.error!.toString(), response.stackTrace);
+  } else if (response.responseType == MessageType.argumentError) {
+    throw new ArgumentError(response.error!.toString());
   }
+
   return response.response as T;
 }
 
 enum MessageType {
+  argumentError,
   boolean,
   constructorsOfRequest,
   declarationOfRequest,
@@ -773,6 +840,7 @@
   instantiateMacroRequest,
   resolveIdentifierRequest,
   resolveTypeRequest,
+  inferTypeRequest,
   isExactlyTypeRequest,
   isSubtypeOfRequest,
   loadMacroRequest,
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart
index fc45704..4ec67e4 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart
@@ -100,11 +100,13 @@
   namedStaticType,
   methodDeclaration,
   namedTypeAnnotation,
+  omittedTypeAnnotation,
   parameterDeclaration,
   staticType,
   typeAliasDeclaration,
   typeParameterDeclaration,
   typeResolver,
   typeDeclarationResolver,
+  typeInferrer,
   variableDeclaration,
 }
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
index d039f7d..8cf9207 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
@@ -22,6 +22,7 @@
       case RemoteInstanceKind.staticType:
       case RemoteInstanceKind.typeDeclarationResolver:
       case RemoteInstanceKind.typeResolver:
+      case RemoteInstanceKind.typeInferrer:
         // These are simple wrappers, just pass in the kind
         return new RemoteInstanceImpl(id: id, kind: kind) as T;
       case RemoteInstanceKind.classDeclaration:
@@ -48,6 +49,9 @@
       case RemoteInstanceKind.namedTypeAnnotation:
         moveNext();
         return _expectNamedTypeAnnotation(id) as T;
+      case RemoteInstanceKind.omittedTypeAnnotation:
+        moveNext();
+        return _expectOmittedTypeAnnotation(id) as T;
       case RemoteInstanceKind.parameterDeclaration:
         moveNext();
         return _expectParameterDeclaration(id) as T;
@@ -80,6 +84,13 @@
         typeArguments: (this..moveNext())._expectRemoteInstanceList(),
       );
 
+  OmittedTypeAnnotation _expectOmittedTypeAnnotation(int id) {
+    expectBool(); // Always `false`.
+    return new OmittedTypeAnnotationImpl(
+      id: id,
+    );
+  }
+
   FunctionTypeAnnotation _expectFunctionTypeAnnotation(int id) =>
       new FunctionTypeAnnotationImpl(
         id: id,
@@ -274,6 +285,9 @@
       case CodeKind.nullableTypeAnnotation:
         return new NullableTypeAnnotationCode((this..moveNext()).expectCode())
             as T;
+      case CodeKind.omittedTypeAnnotation:
+        return new OmittedTypeAnnotationCode(RemoteInstance.deserialize(this))
+            as T;
       case CodeKind.parameter:
         return new ParameterCode(
             defaultValue: (this..moveNext()).expectNullableCode(),
@@ -354,6 +368,11 @@
         NullableTypeAnnotationCode self = this as NullableTypeAnnotationCode;
         self.underlyingType.serialize(serializer);
         return;
+      case CodeKind.omittedTypeAnnotation:
+        OmittedTypeAnnotationCode self = this as OmittedTypeAnnotationCode;
+        (self.typeAnnotation as OmittedTypeAnnotationImpl)
+            .serialize(serializer);
+        return;
       case CodeKind.parameter:
         ParameterCode self = this as ParameterCode;
         self.defaultValue.serializeNullable(serializer);
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index a9ec81e..830127a 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -687,6 +687,52 @@
         r"""Try providing type arguments for the literal explicitly to disambiguate it.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name)> templateCantHaveNamedParameters =
+    const Template<Message Function(String name)>(
+        problemMessageTemplate:
+            r"""'#name' can't be declared with named parameters.""",
+        withArguments: _withArgumentsCantHaveNamedParameters);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)> codeCantHaveNamedParameters =
+    const Code<Message Function(String name)>(
+  "CantHaveNamedParameters",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsCantHaveNamedParameters(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeCantHaveNamedParameters,
+      problemMessage: """'${name}' can't be declared with named parameters.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name)>
+    templateCantHaveOptionalParameters =
+    const Template<Message Function(String name)>(
+        problemMessageTemplate:
+            r"""'#name' can't be declared with optional parameters.""",
+        withArguments: _withArgumentsCantHaveOptionalParameters);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)> codeCantHaveOptionalParameters =
+    const Code<Message Function(String name)>(
+  "CantHaveOptionalParameters",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsCantHaveOptionalParameters(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeCantHaveOptionalParameters,
+      problemMessage:
+          """'${name}' can't be declared with optional parameters.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeCantInferPackagesFromManyInputs =
     messageCantInferPackagesFromManyInputs;
 
@@ -2736,6 +2782,14 @@
     problemMessage: r"""Unable to decode bytes as UTF-8.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeEnumAbstractMember = messageEnumAbstractMember;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageEnumAbstractMember = const MessageCode(
+    "EnumAbstractMember",
+    problemMessage: r"""Enums can't declare abstract members.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
     Message Function(
         String
@@ -2778,14 +2832,38 @@
     problemMessage: r"""Enum constructors can't be torn off.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name)>
+    templateEnumContainsRestrictedInstanceDeclaration =
+    const Template<Message Function(String name)>(
+        problemMessageTemplate:
+            r"""An enum can't declare a non-abstract member named '#name'.""",
+        withArguments: _withArgumentsEnumContainsRestrictedInstanceDeclaration);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)>
+    codeEnumContainsRestrictedInstanceDeclaration =
+    const Code<Message Function(String name)>(
+  "EnumContainsRestrictedInstanceDeclaration",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsEnumContainsRestrictedInstanceDeclaration(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeEnumContainsRestrictedInstanceDeclaration,
+      problemMessage:
+          """An enum can't declare a non-abstract member named '${name}'.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeEnumContainsValuesDeclaration =
     messageEnumContainsValuesDeclaration;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const MessageCode messageEnumContainsValuesDeclaration = const MessageCode(
     "EnumContainsValuesDeclaration",
-    problemMessage:
-        r"""Enums can't contain declarations of members with the name 'values'.""");
+    problemMessage: r"""An enum can't declare a member named 'values'.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeEnumDeclarationEmpty = messageEnumDeclarationEmpty;
@@ -2807,16 +2885,6 @@
         r"""Try removing the factory constructor declaration.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeEnumEntryWithTypeArgumentsWithoutArguments =
-    messageEnumEntryWithTypeArgumentsWithoutArguments;
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageEnumEntryWithTypeArgumentsWithoutArguments =
-    const MessageCode("EnumEntryWithTypeArgumentsWithoutArguments",
-        problemMessage:
-            r"""Missing arguments in enum constructor invocation.""");
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeEnumFactoryRedirectsToConstructor =
     messageEnumFactoryRedirectsToConstructor;
 
@@ -2827,6 +2895,35 @@
         r"""Enum factory constructors can't redirect to generative constructors.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name, String name2)>
+    templateEnumImplementerContainsRestrictedInstanceDeclaration =
+    const Template<Message Function(String name, String name2)>(
+        problemMessageTemplate:
+            r"""'#name' has 'Enum' as a superinterface and can't contain non-static members with name '#name2'.""",
+        withArguments:
+            _withArgumentsEnumImplementerContainsRestrictedInstanceDeclaration);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, String name2)>
+    codeEnumImplementerContainsRestrictedInstanceDeclaration =
+    const Code<Message Function(String name, String name2)>(
+  "EnumImplementerContainsRestrictedInstanceDeclaration",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsEnumImplementerContainsRestrictedInstanceDeclaration(
+    String name, String name2) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  return new Message(codeEnumImplementerContainsRestrictedInstanceDeclaration,
+      problemMessage:
+          """'${name}' has 'Enum' as a superinterface and can't contain non-static members with name '${name2}'.""",
+      arguments: {'name': name, 'name2': name2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String name)>
     templateEnumImplementerContainsValuesDeclaration =
     const Template<Message Function(String name)>(
@@ -2861,6 +2958,38 @@
     correctionMessage: r"""Try moving the enum to the top-level.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name)> templateEnumInheritsRestricted =
+    const Template<Message Function(String name)>(
+        problemMessageTemplate:
+            r"""An enum can't inherit a member named '#name'.""",
+        withArguments: _withArgumentsEnumInheritsRestricted);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)> codeEnumInheritsRestricted =
+    const Code<Message Function(String name)>(
+  "EnumInheritsRestricted",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsEnumInheritsRestricted(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeEnumInheritsRestricted,
+      problemMessage: """An enum can't inherit a member named '${name}'.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeEnumInheritsRestrictedMember =
+    messageEnumInheritsRestrictedMember;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageEnumInheritsRestrictedMember = const MessageCode(
+    "EnumInheritsRestrictedMember",
+    severity: Severity.context,
+    problemMessage: r"""This is the inherited member""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeEnumInstantiation = messageEnumInstantiation;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -3333,27 +3462,29 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<Message Function(String string2)>
+const Template<Message Function(String string, String string2)>
     templateExperimentDisabledInvalidLanguageVersion =
-    const Template<Message Function(String string2)>(
+    const Template<Message Function(String string, String string2)>(
         problemMessageTemplate:
-            r"""This requires the null safety language feature, which requires language version of #string2 or higher.""",
+            r"""This requires the '#string' language feature, which requires language version of #string2 or higher.""",
         withArguments: _withArgumentsExperimentDisabledInvalidLanguageVersion);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String string2)>
+const Code<Message Function(String string, String string2)>
     codeExperimentDisabledInvalidLanguageVersion =
-    const Code<Message Function(String string2)>(
+    const Code<Message Function(String string, String string2)>(
         "ExperimentDisabledInvalidLanguageVersion",
         analyzerCodes: <String>["ParserErrorCode.EXPERIMENT_NOT_ENABLED"]);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsExperimentDisabledInvalidLanguageVersion(String string2) {
+Message _withArgumentsExperimentDisabledInvalidLanguageVersion(
+    String string, String string2) {
+  if (string.isEmpty) throw 'No string provided';
   if (string2.isEmpty) throw 'No string provided';
   return new Message(codeExperimentDisabledInvalidLanguageVersion,
       problemMessage:
-          """This requires the null safety language feature, which requires language version of ${string2} or higher.""",
-      arguments: {'string2': string2});
+          """This requires the '${string}' language feature, which requires language version of ${string2} or higher.""",
+      arguments: {'string': string, 'string2': string2});
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -3389,6 +3520,123 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            string)> templateExperimentNotEnabledOffByDefault = const Template<
+        Message Function(String string)>(
+    problemMessageTemplate:
+        r"""This requires the experimental '#string' language feature to be enabled.""",
+    correctionMessageTemplate:
+        r"""Try passing the '--enable-experiment=#string' command line option.""",
+    withArguments: _withArgumentsExperimentNotEnabledOffByDefault);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)>
+    codeExperimentNotEnabledOffByDefault =
+    const Code<Message Function(String string)>(
+        "ExperimentNotEnabledOffByDefault",
+        analyzerCodes: <String>["ParserErrorCode.EXPERIMENT_NOT_ENABLED"]);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsExperimentNotEnabledOffByDefault(String string) {
+  if (string.isEmpty) throw 'No string provided';
+  return new Message(codeExperimentNotEnabledOffByDefault,
+      problemMessage:
+          """This requires the experimental '${string}' language feature to be enabled.""",
+      correctionMessage: """Try passing the '--enable-experiment=${string}' command line option.""",
+      arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            string)> templateExperimentOptOutComment = const Template<
+        Message Function(String string)>(
+    problemMessageTemplate:
+        r"""This is the annotation that opts out this library from the '#string' language feature.""",
+    withArguments: _withArgumentsExperimentOptOutComment);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)> codeExperimentOptOutComment =
+    const Code<Message Function(String string)>("ExperimentOptOutComment",
+        severity: Severity.context);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsExperimentOptOutComment(String string) {
+  if (string.isEmpty) throw 'No string provided';
+  return new Message(codeExperimentOptOutComment,
+      problemMessage:
+          """This is the annotation that opts out this library from the '${string}' language feature.""",
+      arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String string,
+        String
+            string2)> templateExperimentOptOutExplicit = const Template<
+        Message Function(String string, String string2)>(
+    problemMessageTemplate:
+        r"""The '#string' language feature is disabled for this library.""",
+    correctionMessageTemplate:
+        r"""Try removing the `@dart=` annotation or setting the language version to #string2 or higher.""",
+    withArguments: _withArgumentsExperimentOptOutExplicit);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string, String string2)>
+    codeExperimentOptOutExplicit =
+    const Code<Message Function(String string, String string2)>(
+  "ExperimentOptOutExplicit",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsExperimentOptOutExplicit(String string, String string2) {
+  if (string.isEmpty) throw 'No string provided';
+  if (string2.isEmpty) throw 'No string provided';
+  return new Message(codeExperimentOptOutExplicit,
+      problemMessage:
+          """The '${string}' language feature is disabled for this library.""",
+      correctionMessage:
+          """Try removing the `@dart=` annotation or setting the language version to ${string2} or higher.""",
+      arguments: {'string': string, 'string2': string2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String string,
+        String
+            string2)> templateExperimentOptOutImplicit = const Template<
+        Message Function(String string, String string2)>(
+    problemMessageTemplate:
+        r"""The '#string' language feature is disabled for this library.""",
+    correctionMessageTemplate:
+        r"""Try removing the package language version or setting the language version to #string2 or higher.""",
+    withArguments: _withArgumentsExperimentOptOutImplicit);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string, String string2)>
+    codeExperimentOptOutImplicit =
+    const Code<Message Function(String string, String string2)>(
+  "ExperimentOptOutImplicit",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsExperimentOptOutImplicit(String string, String string2) {
+  if (string.isEmpty) throw 'No string provided';
+  if (string2.isEmpty) throw 'No string provided';
+  return new Message(codeExperimentOptOutImplicit,
+      problemMessage:
+          """The '${string}' language feature is disabled for this library.""",
+      correctionMessage:
+          """Try removing the package language version or setting the language version to ${string2} or higher.""",
+      arguments: {'string': string, 'string2': string2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeExplicitExtensionArgumentMismatch =
     messageExplicitExtensionArgumentMismatch;
 
@@ -3963,6 +4211,36 @@
         r"""Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a NativeType integer with a fixed size.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String string, String name)>
+    templateFfiCompoundImplementsFinalizable =
+    const Template<Message Function(String string, String name)>(
+        problemMessageTemplate:
+            r"""#string '#name' can't implement Finalizable.""",
+        correctionMessageTemplate:
+            r"""Try removing the implements clause from '#name'.""",
+        withArguments: _withArgumentsFfiCompoundImplementsFinalizable);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string, String name)>
+    codeFfiCompoundImplementsFinalizable =
+    const Code<Message Function(String string, String name)>(
+  "FfiCompoundImplementsFinalizable",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiCompoundImplementsFinalizable(
+    String string, String name) {
+  if (string.isEmpty) throw 'No string provided';
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeFfiCompoundImplementsFinalizable,
+      problemMessage: """${string} '${name}' can't implement Finalizable.""",
+      correctionMessage:
+          """Try removing the implements clause from '${name}'.""",
+      arguments: {'string': string, 'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
     Message Function(
         String string,
@@ -5491,6 +5769,33 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            name)> templateInstanceAndSynthesizedStaticConflict = const Template<
+        Message Function(String name)>(
+    problemMessageTemplate:
+        r"""This instance member conflicts with the synthesized static member called '#name'.""",
+    withArguments: _withArgumentsInstanceAndSynthesizedStaticConflict);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)>
+    codeInstanceAndSynthesizedStaticConflict =
+    const Code<Message Function(String name)>(
+        "InstanceAndSynthesizedStaticConflict",
+        analyzerCodes: <String>["CONFLICTING_STATIC_AND_INSTANCE"]);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsInstanceAndSynthesizedStaticConflict(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeInstanceAndSynthesizedStaticConflict,
+      problemMessage:
+          """This instance member conflicts with the synthesized static member called '${name}'.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(int count, int count2)>
     templateInstantiationTooFewArguments =
     const Template<Message Function(int count, int count2)>(
@@ -6100,6 +6405,17 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeInvalidEscapeStarted = messageInvalidEscapeStarted;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageInvalidEscapeStarted = const MessageCode(
+    "InvalidEscapeStarted",
+    index: 126,
+    problemMessage: r"""The string '\' can't stand alone.""",
+    correctionMessage:
+        r"""Try adding another backslash (\) to escape the '\'.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String name)>
     templateInvalidGetterSetterTypeFieldContext =
     const Template<Message Function(String name)>(
@@ -6424,11 +6740,34 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeInvalidUnicodeEscape = messageInvalidUnicodeEscape;
+const Code<Null> codeInvalidUnicodeEscapeUBracket =
+    messageInvalidUnicodeEscapeUBracket;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageInvalidUnicodeEscape = const MessageCode(
-    "InvalidUnicodeEscape",
+const MessageCode messageInvalidUnicodeEscapeUBracket = const MessageCode(
+    "InvalidUnicodeEscapeUBracket",
+    index: 125,
+    problemMessage:
+        r"""An escape sequence starting with '\u{' must be followed by 1 to 6 hexadecimal digits followed by a '}'.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeInvalidUnicodeEscapeUNoBracket =
+    messageInvalidUnicodeEscapeUNoBracket;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageInvalidUnicodeEscapeUNoBracket = const MessageCode(
+    "InvalidUnicodeEscapeUNoBracket",
+    index: 124,
+    problemMessage:
+        r"""An escape sequence starting with '\u' must be followed by 4 hexadecimal digits.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeInvalidUnicodeEscapeUStarted =
+    messageInvalidUnicodeEscapeUStarted;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageInvalidUnicodeEscapeUStarted = const MessageCode(
+    "InvalidUnicodeEscapeUStarted",
     index: 38,
     problemMessage:
         r"""An escape sequence starting with '\u' must be followed by 4 hexadecimal digits or from 1 to 6 digits between '{' and '}'.""");
@@ -6569,17 +6908,6 @@
         r"""Try removing the 'external' keyword or adding a JS interop annotation.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeJsInteropIndexNotSupported =
-    messageJsInteropIndexNotSupported;
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageJsInteropIndexNotSupported = const MessageCode(
-    "JsInteropIndexNotSupported",
-    problemMessage:
-        r"""JS interop classes do not support [] and []= operator methods.""",
-    correctionMessage: r"""Try replacing with a normal method.""");
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
     Message Function(
         String name,
@@ -6685,6 +7013,16 @@
     correctionMessage: r"""Try annotating the member with `external`.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeJsInteropOperatorsNotSupported =
+    messageJsInteropOperatorsNotSupported;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageJsInteropOperatorsNotSupported = const MessageCode(
+    "JsInteropOperatorsNotSupported",
+    problemMessage: r"""JS interop classes do not support operator methods.""",
+    correctionMessage: r"""Try replacing this with a normal method.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String name)>
     templateJsInteropStaticInteropWithInstanceMembers =
     const Template<Message Function(String name)>(
@@ -7895,72 +8233,6 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeNonNullableOptOutComment = messageNonNullableOptOutComment;
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageNonNullableOptOutComment = const MessageCode(
-    "NonNullableOptOutComment",
-    severity: Severity.context,
-    problemMessage:
-        r"""This is the annotation that opts out this library from null safety features.""");
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<
-    Message Function(
-        String
-            string)> templateNonNullableOptOutExplicit = const Template<
-        Message Function(String string)>(
-    problemMessageTemplate:
-        r"""Null safety features are disabled for this library.""",
-    correctionMessageTemplate:
-        r"""Try removing the `@dart=` annotation or setting the language version to #string or higher.""",
-    withArguments: _withArgumentsNonNullableOptOutExplicit);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String string)> codeNonNullableOptOutExplicit =
-    const Code<Message Function(String string)>(
-  "NonNullableOptOutExplicit",
-);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsNonNullableOptOutExplicit(String string) {
-  if (string.isEmpty) throw 'No string provided';
-  return new Message(codeNonNullableOptOutExplicit,
-      problemMessage: """Null safety features are disabled for this library.""",
-      correctionMessage:
-          """Try removing the `@dart=` annotation or setting the language version to ${string} or higher.""",
-      arguments: {'string': string});
-}
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<
-    Message Function(
-        String
-            string)> templateNonNullableOptOutImplicit = const Template<
-        Message Function(String string)>(
-    problemMessageTemplate:
-        r"""Null safety features are disabled for this library.""",
-    correctionMessageTemplate:
-        r"""Try removing the package language version or setting the language version to #string or higher.""",
-    withArguments: _withArgumentsNonNullableOptOutImplicit);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String string)> codeNonNullableOptOutImplicit =
-    const Code<Message Function(String string)>(
-  "NonNullableOptOutImplicit",
-);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsNonNullableOptOutImplicit(String string) {
-  if (string.isEmpty) throw 'No string provided';
-  return new Message(codeNonNullableOptOutImplicit,
-      problemMessage: """Null safety features are disabled for this library.""",
-      correctionMessage:
-          """Try removing the package language version or setting the language version to ${string} or higher.""",
-      arguments: {'string': string});
-}
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeNonPartOfDirectiveInPart = messageNonPartOfDirectiveInPart;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -8177,6 +8449,96 @@
         r"""Try moving the '?..' operator to be the first cascade operator in the sequence.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String string2)>
+    templateNullSafetyDisabledInvalidLanguageVersion =
+    const Template<Message Function(String string2)>(
+        problemMessageTemplate:
+            r"""This requires the null safety language feature, which requires language version of #string2 or higher.""",
+        withArguments: _withArgumentsNullSafetyDisabledInvalidLanguageVersion);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string2)>
+    codeNullSafetyDisabledInvalidLanguageVersion =
+    const Code<Message Function(String string2)>(
+        "NullSafetyDisabledInvalidLanguageVersion",
+        analyzerCodes: <String>["ParserErrorCode.EXPERIMENT_NOT_ENABLED"]);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsNullSafetyDisabledInvalidLanguageVersion(String string2) {
+  if (string2.isEmpty) throw 'No string provided';
+  return new Message(codeNullSafetyDisabledInvalidLanguageVersion,
+      problemMessage:
+          """This requires the null safety language feature, which requires language version of ${string2} or higher.""",
+      arguments: {'string2': string2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeNullSafetyOptOutComment = messageNullSafetyOptOutComment;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageNullSafetyOptOutComment = const MessageCode(
+    "NullSafetyOptOutComment",
+    severity: Severity.context,
+    problemMessage:
+        r"""This is the annotation that opts out this library from null safety features.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            string)> templateNullSafetyOptOutExplicit = const Template<
+        Message Function(String string)>(
+    problemMessageTemplate:
+        r"""Null safety features are disabled for this library.""",
+    correctionMessageTemplate:
+        r"""Try removing the `@dart=` annotation or setting the language version to #string or higher.""",
+    withArguments: _withArgumentsNullSafetyOptOutExplicit);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)> codeNullSafetyOptOutExplicit =
+    const Code<Message Function(String string)>(
+  "NullSafetyOptOutExplicit",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsNullSafetyOptOutExplicit(String string) {
+  if (string.isEmpty) throw 'No string provided';
+  return new Message(codeNullSafetyOptOutExplicit,
+      problemMessage: """Null safety features are disabled for this library.""",
+      correctionMessage:
+          """Try removing the `@dart=` annotation or setting the language version to ${string} or higher.""",
+      arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            string)> templateNullSafetyOptOutImplicit = const Template<
+        Message Function(String string)>(
+    problemMessageTemplate:
+        r"""Null safety features are disabled for this library.""",
+    correctionMessageTemplate:
+        r"""Try removing the package language version or setting the language version to #string or higher.""",
+    withArguments: _withArgumentsNullSafetyOptOutImplicit);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)> codeNullSafetyOptOutImplicit =
+    const Code<Message Function(String string)>(
+  "NullSafetyOptOutImplicit",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsNullSafetyOptOutImplicit(String string) {
+  if (string.isEmpty) throw 'No string provided';
+  return new Message(codeNullSafetyOptOutImplicit,
+      problemMessage: """Null safety features are disabled for this library.""",
+      correctionMessage:
+          """Try removing the package language version or setting the language version to ${string} or higher.""",
+      arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String name)> templateNullableInterfaceError =
     const Template<Message Function(String name)>(
         problemMessageTemplate:
@@ -9679,6 +10041,17 @@
     correctionMessage: r"""Try replacing '?.' with '.'""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeSuperParameterInitializerOutsideConstructor =
+    messageSuperParameterInitializerOutsideConstructor;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageSuperParameterInitializerOutsideConstructor =
+    const MessageCode("SuperParameterInitializerOutsideConstructor",
+        problemMessage:
+            r"""Super-initializer formal parameters can only be used in generative constructors.""",
+        correctionMessage: r"""Try removing 'super.'.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String name)>
     templateSuperclassHasNoConstructor =
     const Template<Message Function(String name)>(
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/formal_parameter_kind.dart b/pkg/_fe_analyzer_shared/lib/src/parser/formal_parameter_kind.dart
index 7e2e6f9..fa76b8b 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/formal_parameter_kind.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/formal_parameter_kind.dart
@@ -4,21 +4,35 @@
 
 library _fe_analyzer_shared.parser.formal_parameter_kind;
 
-// TODO(johnniwinther): Update this to support required named arguments.
 enum FormalParameterKind {
-  mandatory,
+  requiredPositional,
+  requiredNamed,
   optionalNamed,
   optionalPositional,
 }
 
-bool isMandatoryFormalParameterKind(FormalParameterKind type) {
-  return FormalParameterKind.mandatory == type;
-}
+extension FormalParameterKindExtension on FormalParameterKind {
+  bool get isRequiredPositional {
+    return FormalParameterKind.requiredPositional == this;
+  }
 
-bool isOptionalNamedFormalParameterKind(FormalParameterKind type) {
-  return FormalParameterKind.optionalNamed == type;
-}
+  bool get isOptionalNamed {
+    return FormalParameterKind.optionalNamed == this;
+  }
 
-bool isOptionalPositionalFormalParameterKind(FormalParameterKind type) {
-  return FormalParameterKind.optionalPositional == type;
+  bool get isOptionalPositional {
+    return FormalParameterKind.optionalPositional == this;
+  }
+
+  bool get isRequiredNamed {
+    return FormalParameterKind.requiredNamed == this;
+  }
+
+  bool get isRequired => isRequiredPositional || isRequiredNamed;
+
+  bool get isOptional => isOptionalPositional || isOptionalNamed;
+
+  bool get isPositional => isRequiredPositional || isOptionalPositional;
+
+  bool get isNamed => isRequiredNamed || isOptionalNamed;
 }
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
index b8da52c..4359e27 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
@@ -63,10 +63,7 @@
 import 'directive_context.dart';
 
 import 'formal_parameter_kind.dart'
-    show
-        FormalParameterKind,
-        isMandatoryFormalParameterKind,
-        isOptionalPositionalFormalParameterKind;
+    show FormalParameterKind, FormalParameterKindExtension;
 
 import 'forwarding_listener.dart' show ForwardingListener, NullListener;
 
@@ -1398,7 +1395,8 @@
         token = ensureCloseParen(token, begin);
         break;
       }
-      token = parseFormalParameter(token, FormalParameterKind.mandatory, kind);
+      token = parseFormalParameter(
+          token, FormalParameterKind.requiredPositional, kind);
       next = token.next!;
       if (!optional(',', next)) {
         Token next = token.next!;
@@ -1525,6 +1523,7 @@
     if (isModifier(next)) {
       if (optional('required', next)) {
         if (parameterKind == FormalParameterKind.optionalNamed) {
+          parameterKind = FormalParameterKind.requiredNamed;
           requiredToken = token = next;
           next = token.next!;
         }
@@ -1742,11 +1741,10 @@
       // TODO(danrubel): Consider removing the last parameter from the
       // handleValuedFormalParameter event... it appears to be unused.
       listener.handleValuedFormalParameter(equal, next);
-      if (isMandatoryFormalParameterKind(parameterKind)) {
+      if (parameterKind.isRequiredPositional) {
         reportRecoverableError(
             equal, codes.messageRequiredParameterWithDefault);
-      } else if (isOptionalPositionalFormalParameterKind(parameterKind) &&
-          identical(':', value)) {
+      } else if (parameterKind.isOptionalPositional && identical(':', value)) {
         reportRecoverableError(
             equal, codes.messagePositionalParameterWithEquals);
       } else if (inFunctionType ||
@@ -2151,10 +2149,35 @@
     Token beginToken = token;
     token = parseMetadataStar(token);
     token = ensureIdentifier(token, IdentifierContext.enumValueDeclaration);
-    token = parseConstructorReference(token, ConstructorReferenceContext.Const,
-        /* typeArg = */ null, /* isImplicitTypeName = */ true);
+    bool hasTypeArgumentsOrDot = false;
+    {
+      // This is almost a verbatim copy of [parseConstructorReference] inserted
+      // to provide better recovery.
+      Token start = token;
+      listener.handleNoTypeNameInConstructorReference(token.next!);
+      listener.beginConstructorReference(start);
+      TypeParamOrArgInfo typeArg = computeTypeParamOrArg(token);
+      if (typeArg != noTypeParamOrArg) {
+        hasTypeArgumentsOrDot = true;
+      }
+      token = typeArg.parseArguments(token, this);
+      Token? period = null;
+      if (optional('.', token.next!)) {
+        hasTypeArgumentsOrDot = true;
+        period = token.next!;
+        token = ensureIdentifier(
+            period,
+            IdentifierContext
+                .constructorReferenceContinuationAfterTypeArguments);
+      } else {
+        listener.handleNoConstructorReferenceContinuationAfterTypeArguments(
+            token.next!);
+      }
+      listener.endConstructorReference(
+          start, period, token.next!, ConstructorReferenceContext.Const);
+    }
     Token next = token.next!;
-    if (optional('(', next) || optional('<', next)) {
+    if (optional('(', next) || hasTypeArgumentsOrDot) {
       token = parseConstructorInvocationArguments(token);
     } else {
       listener.handleNoArguments(token);
@@ -4638,19 +4661,14 @@
 
   Token parseConstructorReference(
       Token token, ConstructorReferenceContext constructorReferenceContext,
-      [TypeParamOrArgInfo? typeArg, bool isImplicitTypeName = false]) {
+      [TypeParamOrArgInfo? typeArg]) {
+    // Note that there's an almost verbatim copy in [parseEnumElement] so
+    // any change here should be added there too.
     Token start;
-    if (isImplicitTypeName) {
-      listener.handleNoTypeNameInConstructorReference(token.next!);
-      start = token;
-    } else {
-      start = ensureIdentifier(token, IdentifierContext.constructorReference);
-    }
+    start = ensureIdentifier(token, IdentifierContext.constructorReference);
     listener.beginConstructorReference(start);
-    if (!isImplicitTypeName) {
-      token = parseQualifiedRestOpt(
-          start, IdentifierContext.constructorReferenceContinuation);
-    }
+    token = parseQualifiedRestOpt(
+        start, IdentifierContext.constructorReferenceContinuation);
     typeArg ??= computeTypeParamOrArg(token);
     token = typeArg.parseArguments(token, this);
     Token? period = null;
@@ -6710,7 +6728,7 @@
         break;
       }
       Token? colon = null;
-      if (optional(':', next.next!)) {
+      if (optional(':', next.next!) || /* recovery */ optional(':', next)) {
         token =
             ensureIdentifier(token, IdentifierContext.namedArgumentReference)
                 .next!;
@@ -7028,11 +7046,17 @@
         // to determine if this is part of a conditional expression
         //
         Listener originalListener = listener;
-        listener = new ForwardingListener();
-        // TODO(danrubel): consider using TokenStreamGhostWriter here
+        TokenStreamRewriter? originalRewriter = cachedRewriter;
+        listener = new NullListener();
+        UndoableTokenStreamRewriter undoableTokenStreamRewriter =
+            new UndoableTokenStreamRewriter();
+        cachedRewriter = undoableTokenStreamRewriter;
         Token afterExpression =
             parseExpressionWithoutCascade(afterIdentifier).next!;
+        // Undo all changes and reset.
+        undoableTokenStreamRewriter.undo();
         listener = originalListener;
+        cachedRewriter = originalRewriter;
 
         if (optional(':', afterExpression)) {
           // Looks like part of a conditional expression.
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/quote.dart b/pkg/_fe_analyzer_shared/lib/src/parser/quote.dart
index c9ec28a..fe146ce 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/quote.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/quote.dart
@@ -193,8 +193,9 @@
       code = $LF;
     } else if (!isRaw && code == $BACKSLASH) {
       if (codeUnits.length == ++i) {
+        // This should only be reachable in error cases.
         listener.handleUnescapeError(
-            codes.messageInvalidUnicodeEscape, location, i, /* length = */ 1);
+            codes.messageInvalidEscapeStarted, location, i, /* length = */ 1);
         return new String.fromCharCodes(codeUnits);
       }
       code = codeUnits[i];
@@ -240,47 +241,78 @@
       } else if (code == $u) {
         int begin = i;
         if (codeUnits.length == i + 1) {
-          listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
-              location, begin, codeUnits.length + 1 - begin);
+          listener.handleUnescapeError(
+              codes.messageInvalidUnicodeEscapeUStarted,
+              location,
+              begin,
+              codeUnits.length + 1 - begin);
           return new String.fromCharCodes(codeUnits);
         }
         code = codeUnits[i + 1];
+        bool foundEndBracket = false;
         if (code == $OPEN_CURLY_BRACKET) {
           // Expect 1-6 hex digits followed by '}'.
           if (codeUnits.length == ++i) {
-            listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
-                location, begin, i + 1 - begin);
+            listener.handleUnescapeError(
+                codes.messageInvalidUnicodeEscapeUBracket,
+                location,
+                begin,
+                i + 1 - begin);
             return new String.fromCharCodes(codeUnits);
           }
           code = 0;
           for (int j = 0; j < 7; j++) {
             if (codeUnits.length == ++i) {
-              listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
-                  location, begin, i + 1 - begin);
+              listener.handleUnescapeError(
+                  codes.messageInvalidUnicodeEscapeUBracket,
+                  location,
+                  begin,
+                  i + 1 - begin);
               return new String.fromCharCodes(codeUnits);
             }
             int digit = codeUnits[i];
-            if (j != 0 && digit == $CLOSE_CURLY_BRACKET) break;
+            if (j != 0 && digit == $CLOSE_CURLY_BRACKET) {
+              foundEndBracket = true;
+              break;
+            } else if (j == 6) {
+              break;
+            }
             if (!isHexDigit(digit)) {
-              listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
-                  location, begin, i + 2 - begin);
+              listener.handleUnescapeError(
+                  codes.messageInvalidUnicodeEscapeUBracket,
+                  location,
+                  begin,
+                  i + 2 - begin);
               return new String.fromCharCodes(codeUnits);
             }
             code = (code << 4) + hexDigitValue(digit);
           }
+          if (!foundEndBracket) {
+            listener.handleUnescapeError(
+                codes.messageInvalidUnicodeEscapeUBracket,
+                location,
+                begin,
+                i + 1 - begin);
+          }
         } else {
           // Expect exactly 4 hex digits.
           if (codeUnits.length <= i + 4) {
-            listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
-                location, begin, codeUnits.length + 1 - begin);
+            listener.handleUnescapeError(
+                codes.messageInvalidUnicodeEscapeUNoBracket,
+                location,
+                begin,
+                codeUnits.length + 1 - begin);
             return new String.fromCharCodes(codeUnits);
           }
           code = 0;
           for (int j = 0; j < 4; j++) {
             int digit = codeUnits[++i];
             if (!isHexDigit(digit)) {
-              listener.handleUnescapeError(codes.messageInvalidUnicodeEscape,
-                  location, begin, i + 1 - begin);
+              listener.handleUnescapeError(
+                  codes.messageInvalidUnicodeEscapeUNoBracket,
+                  location,
+                  begin,
+                  i + 1 - begin);
               return new String.fromCharCodes(codeUnits);
             }
             code = (code << 4) + hexDigitValue(digit);
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/stack_listener.dart b/pkg/_fe_analyzer_shared/lib/src/parser/stack_listener.dart
index 4f5ad4e..a9c8bedb 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/stack_listener.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/stack_listener.dart
@@ -56,6 +56,7 @@
   Initializers,
   Labels,
   Metadata,
+  MixinApplicationBuilder,
   Modifiers,
   Name,
   OperatorList,
diff --git a/pkg/_fe_analyzer_shared/lib/src/scanner/characters.dart b/pkg/_fe_analyzer_shared/lib/src/scanner/characters.dart
index 6e0710a..305d071 100644
--- a/pkg/_fe_analyzer_shared/lib/src/scanner/characters.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/scanner/characters.dart
@@ -132,16 +132,3 @@
   if (hexDigit <= $9) return hexDigit - $0;
   return (hexDigit | ($a ^ $A)) - ($a - 10);
 }
-
-bool isUnicodeScalarValue(int value) {
-  return value < $FIRST_SURROGATE ||
-      (value > $LAST_SURROGATE && value <= $LAST_CODE_POINT);
-}
-
-bool isUtf16LeadSurrogate(int value) {
-  return value >= 0xd800 && value <= 0xdbff;
-}
-
-bool isUtf16TrailSurrogate(int value) {
-  return value >= 0xdc00 && value <= 0xdfff;
-}
diff --git a/pkg/_fe_analyzer_shared/lib/src/util/dependency_walker.dart b/pkg/_fe_analyzer_shared/lib/src/util/dependency_walker.dart
new file mode 100644
index 0000000..b044921
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/util/dependency_walker.dart
@@ -0,0 +1,152 @@
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// An instance of [DependencyWalker] contains the core algorithms for
+/// walking a dependency graph and evaluating nodes in a safe order.
+abstract class DependencyWalker<NodeType extends Node<NodeType>> {
+  /// Called by [walk] to evaluate a single non-cyclical node, after
+  /// all that node's dependencies have been evaluated.
+  void evaluate(NodeType v);
+
+  /// Called by [walk] to evaluate a strongly connected component
+  /// containing one or more nodes.  All dependencies of the strongly
+  /// connected component have been evaluated.
+  void evaluateScc(List<NodeType> scc);
+
+  /// Walk the dependency graph starting at [startingPoint], finding
+  /// strongly connected components and evaluating them in a safe order
+  /// by calling [evaluate] and [evaluateScc].
+  ///
+  /// This is an implementation of Tarjan's strongly connected
+  /// components algorithm
+  /// (https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm).
+  void walk(NodeType startingPoint) {
+    // TODO(paulberry): consider rewriting in a non-recursive way so
+    // that long dependency chains don't cause stack overflow.
+
+    // TODO(paulberry): in the event that an exception occurs during
+    // the walk, restore the state of the [Node] data structures so
+    // that further evaluation will be safe.
+
+    if (startingPoint.isEvaluated) return;
+
+    // The index which will be assigned to the next node that is
+    // freshly visited.
+    int index = 1;
+
+    // Stack of nodes which have been seen so far and whose strongly
+    // connected component is still being determined.  Nodes are only
+    // popped off the stack when they are evaluated, so sometimes the
+    // stack contains nodes that were visited after the current node.
+    List<NodeType> stack = <NodeType>[];
+
+    void strongConnect(NodeType node) {
+      bool hasTrivialCycle = false;
+
+      // Assign the current node an index and add it to the stack.  We
+      // haven't seen any of its dependencies yet, so set its lowLink
+      // to its index, indicating that so far it is the only node in
+      // its strongly connected component.
+      node._index = node._lowLink = index++;
+      stack.add(node);
+
+      // Consider the node's dependencies one at a time.
+      for (NodeType dependency in Node.getDependencies(node)) {
+        // If the dependency has already been evaluated, it can't be
+        // part of this node's strongly connected component, so we can
+        // skip it.
+        if (dependency.isEvaluated) {
+          continue;
+        }
+        if (identical(node, dependency)) {
+          // If a node includes itself as a dependency, there is no need to
+          // explore the dependency further.
+          hasTrivialCycle = true;
+        } else if (dependency._index == 0) {
+          // The dependency hasn't been seen yet, so recurse on it.
+          strongConnect(dependency);
+          // If the dependency's lowLink refers to a node that was
+          // visited before the current node, that means that the
+          // current node, the dependency, and the node referred to by
+          // the dependency's lowLink are all part of the same
+          // strongly connected component, so we need to update the
+          // current node's lowLink accordingly.
+          if (dependency._lowLink < node._lowLink) {
+            node._lowLink = dependency._lowLink;
+          }
+        } else {
+          // The dependency has already been seen, so it is part of
+          // the current node's strongly connected component.  If it
+          // was visited earlier than the current node's lowLink, then
+          // it is a new addition to the current node's strongly
+          // connected component, so we need to update the current
+          // node's lowLink accordingly.
+          if (dependency._index < node._lowLink) {
+            node._lowLink = dependency._index;
+          }
+        }
+      }
+
+      // If the current node's lowLink is the same as its index, then
+      // we have finished visiting a strongly connected component, so
+      // pop the stack and evaluate it before moving on.
+      if (node._lowLink == node._index) {
+        // The strongly connected component has only one node.  If there is a
+        // cycle, it's a trivial one.
+        if (identical(stack.last, node)) {
+          stack.removeLast();
+          if (hasTrivialCycle) {
+            evaluateScc(<NodeType>[node]);
+          } else {
+            evaluate(node);
+          }
+        } else {
+          // There are multiple nodes in the strongly connected
+          // component.
+          List<NodeType> scc = <NodeType>[];
+          while (true) {
+            NodeType otherNode = stack.removeLast();
+            scc.add(otherNode);
+            if (identical(otherNode, node)) {
+              break;
+            }
+          }
+          evaluateScc(scc);
+        }
+      }
+    }
+
+    // Kick off the algorithm starting with the starting point.
+    strongConnect(startingPoint);
+  }
+}
+
+/// Instances of [Node] represent nodes in a dependency graph.  The
+/// type parameter, [NodeType], is the derived type (this affords some
+/// extra type safety by making it difficult to accidentally construct
+/// bridges between unrelated dependency graphs).
+abstract class Node<NodeType> {
+  /// Index used by Tarjan's strongly connected components algorithm.
+  /// Zero means the node has not been visited yet; a nonzero value
+  /// counts the order in which the node was visited.
+  int _index = 0;
+
+  /// Low link used by Tarjan's strongly connected components
+  /// algorithm.  This represents the smallest [_index] of all the nodes
+  /// in the strongly connected component to which this node belongs.
+  int _lowLink = 0;
+
+  List<NodeType>? _dependencies;
+
+  /// Indicates whether this node has been evaluated yet.
+  bool get isEvaluated;
+
+  /// Compute the dependencies of this node.
+  List<NodeType> computeDependencies();
+
+  /// Gets the dependencies of the given node, computing them if necessary.
+  static List<NodeType> getDependencies<NodeType>(Node<NodeType> node) {
+    return node._dependencies ??= node.computeDependencies();
+  }
+}
diff --git a/pkg/_fe_analyzer_shared/pubspec.yaml b/pkg/_fe_analyzer_shared/pubspec.yaml
index e4718a0..e8855e1 100644
--- a/pkg/_fe_analyzer_shared/pubspec.yaml
+++ b/pkg/_fe_analyzer_shared/pubspec.yaml
@@ -1,5 +1,5 @@
 name: _fe_analyzer_shared
-version: 36.0.0
+version: 38.0.0
 description: Logic that is shared between the front_end and analyzer packages.
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/_fe_analyzer_shared
 
diff --git a/pkg/_fe_analyzer_shared/test/deferred_closure_heuristic_test.dart b/pkg/_fe_analyzer_shared/test/deferred_closure_heuristic_test.dart
new file mode 100644
index 0000000..ab4b8f9
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/deferred_closure_heuristic_test.dart
@@ -0,0 +1,138 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/deferred_closure_heuristic.dart';
+import 'package:test/test.dart';
+
+main() {
+  test('single', () {
+    // If there is just a single closure and no type variables, it is selected.
+    var f = Closure('f');
+    expect(
+        _TestClosureDeps(typeVars: [], closures: [f])
+            .planClosureReconciliationStages(),
+        [
+          {f}
+        ]);
+  });
+
+  test('simple dependency', () {
+    // If f depends on g, then g is selected first, and then f.
+    var f = Closure('f', argTypes: ['T']);
+    var g = Closure('g', retTypes: ['T']);
+    expect(
+        _TestClosureDeps(typeVars: ['T'], closures: [f, g])
+            .planClosureReconciliationStages(),
+        [
+          {g},
+          {f}
+        ]);
+  });
+
+  test('long chain', () {
+    // If f depends on g and g depends on h, then we do three separate stages:
+    // h, then g, then f.
+    var f = Closure('f', argTypes: ['T']);
+    var g = Closure('g', argTypes: ['U'], retTypes: ['T']);
+    var h = Closure('h', retTypes: ['U']);
+    expect(
+        _TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h])
+            .planClosureReconciliationStages(),
+        [
+          {h},
+          {g},
+          {f}
+        ]);
+  });
+
+  test('unrelated closure', () {
+    // Closures that are independent of all the others are inferred during the
+    // first stage.
+    var f = Closure('f', argTypes: ['T']);
+    var g = Closure('g', retTypes: ['T']);
+    var h = Closure('h');
+    expect(
+        _TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h])
+            .planClosureReconciliationStages(),
+        [
+          {g, h},
+          {f}
+        ]);
+  });
+
+  test('independent chains', () {
+    // If f depends on g, and h depends on i, then g and i are selected first,
+    // and then f and h.
+    var f = Closure('f', argTypes: ['T']);
+    var g = Closure('g', retTypes: ['T']);
+    var h = Closure('h', argTypes: ['U']);
+    var i = Closure('i', retTypes: ['U']);
+    expect(
+        _TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h, i])
+            .planClosureReconciliationStages(),
+        [
+          {g, i},
+          {f, h}
+        ]);
+  });
+
+  test('diamond', () {
+    // Test a diamond dependency shape: f depends on g and h; g and h both
+    // depend on i.
+    var f = Closure('f', argTypes: ['T', 'U']);
+    var g = Closure('g', argTypes: ['V'], retTypes: ['T']);
+    var h = Closure('h', argTypes: ['V'], retTypes: ['U']);
+    var i = Closure('i', retTypes: ['V']);
+    expect(
+        _TestClosureDeps(typeVars: ['T', 'U', 'V'], closures: [f, g, h, i])
+            .planClosureReconciliationStages(),
+        [
+          {i},
+          {g, h},
+          {f}
+        ]);
+  });
+
+  test('cycle', () {
+    // A dependency cycle is inferred all at once.
+    var f = Closure('f', argTypes: ['T']);
+    var g = Closure('g', argTypes: ['U']);
+    var h = Closure('h', argTypes: ['U'], retTypes: ['T']);
+    var i = Closure('i', argTypes: ['T'], retTypes: ['U']);
+    expect(
+        _TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h, i])
+            .planClosureReconciliationStages(),
+        [
+          {h, i},
+          {f, g}
+        ]);
+  });
+}
+
+class Closure {
+  final String name;
+  final List<String> argTypes;
+  final List<String> retTypes;
+
+  Closure(this.name, {this.argTypes = const [], this.retTypes = const []});
+
+  @override
+  String toString() => name;
+}
+
+class _TestClosureDeps extends ClosureDependencies<String, Closure> {
+  final List<String> typeVars;
+  final List<Closure> closures;
+
+  _TestClosureDeps({required this.typeVars, required this.closures})
+      : super(closures, typeVars);
+
+  @override
+  Set<String> typeVarsFreeInClosureArguments(Closure closure) =>
+      closure.argTypes.toSet();
+
+  @override
+  Set<String> typeVarsFreeInClosureReturns(Closure closure) =>
+      closure.retTypes.toSet();
+}
diff --git a/pkg/_fe_analyzer_shared/test/macros/api/api_test_data.dart b/pkg/_fe_analyzer_shared/test/macros/api/api_test_data.dart
index 5a96c10..ad5e78b 100644
--- a/pkg/_fe_analyzer_shared/test/macros/api/api_test_data.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/api/api_test_data.dart
@@ -11,18 +11,55 @@
 set setter(_) => null;
 
 @ClassMacro()
-class Class1 {}
+class Class1 {
+  var field1;
+
+  Class1();
+}
 
 @ClassMacro()
 abstract class Class2 {}
 
 @ClassMacro()
-class Class3 extends Class2 {}
+class Class3 extends Class2 implements Interface1 {
+  var field1;
+  var field2;
 
-mixin Mixin {}
+  Class3.new();
+  Class3.named();
+  factory Class3.fact() => Class3.named();
+  factory Class3.redirect() = Class3.named;
+
+  void method1() {}
+  void method2() {}
+
+  get getter1 => null;
+  set setter1(_) {}
+
+  get property1 => null;
+  set property1(_) {}
+
+  static var staticField1;
+  static void staticMethod1() {}
+}
 
 @ClassMacro()
-class Class4 extends Class1 with Mixin {}
+class Class4 extends Class1 with Mixin1 {}
+
+@ClassMacro()
+class Class5 extends Class2
+    with Mixin1, Mixin2
+    implements Interface1, Interface2 {}
+
+mixin Mixin1 {}
+
+mixin Mixin2 {}
+
+@ClassMacro()
+abstract class Interface1 {}
+
+@ClassMacro()
+abstract class Interface2 {}
 
 @FunctionMacro()
 void topLevelFunction1(Class1 a, {Class1? b, required Class2? c}) {}
diff --git a/pkg/_fe_analyzer_shared/test/macros/api/api_test_expectations.dart b/pkg/_fe_analyzer_shared/test/macros/api/api_test_expectations.dart
index c826563..8c5a201 100644
--- a/pkg/_fe_analyzer_shared/test/macros/api/api_test_expectations.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/api/api_test_expectations.dart
@@ -5,10 +5,50 @@
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
 
 const Map<String, ClassData> expectedClassData = {
-  'Class1': ClassData(superclassOf: 'Object'),
+  'Class1': ClassData(
+      superclassOf: 'Object', fieldsOf: ['field1'], constructorsOf: ['']),
   'Class2': ClassData(isAbstract: true, superclassOf: 'Object'),
-  'Class3': ClassData(superclassOf: 'Class2', superSuperclassOf: 'Object'),
-  'Class4': ClassData(superclassOf: 'Class1', superSuperclassOf: 'Object'),
+  'Class3': ClassData(
+      superclassOf: 'Class2',
+      superSuperclassOf: 'Object',
+      interfacesOf: [
+        'Interface1'
+      ],
+      // TODO(johnniwinther): Should we require a specific order?
+      fieldsOf: [
+        'field1',
+        'field2',
+        'staticField1',
+      ],
+      // TODO(johnniwinther): Should we require a specific order?
+      methodsOf: [
+        'method1',
+        'method2',
+        'getter1',
+        'property1',
+        'staticMethod1',
+        'setter1',
+        'property1',
+      ],
+      // TODO(johnniwinther): Should we require a specific order?
+      constructorsOf: [
+        // TODO(johnniwinther): Should we normalize no-name constructor names?
+        '',
+        'named',
+        'fact',
+        'redirect',
+      ]),
+  'Class4': ClassData(
+      superclassOf: 'Class1',
+      superSuperclassOf: 'Object',
+      mixinsOf: ['Mixin1']),
+  'Class5': ClassData(
+      superclassOf: 'Class2',
+      superSuperclassOf: 'Object',
+      mixinsOf: ['Mixin1', 'Mixin2'],
+      interfacesOf: ['Interface1', 'Interface2']),
+  'Interface1': ClassData(isAbstract: true, superclassOf: 'Object'),
+  'Interface2': ClassData(isAbstract: true, superclassOf: 'Object'),
 };
 
 const Map<String, FunctionData> expectedFunctionData = {
@@ -44,10 +84,17 @@
   }
 }
 
-Future<void> throws(Future<void> Function() f, property) async {
+Future<void> throws(Future<void> Function() f, property,
+    {String? Function(Object)? expectedError}) async {
   try {
     await f();
-  } catch (_) {
+  } catch (e) {
+    if (expectedError != null) {
+      String? errorMessage = expectedError(e);
+      if (errorMessage != null) {
+        throw 'Unexpected exception on $property: $errorMessage';
+      }
+    }
     return;
   }
   throw 'Expected throws on $property';
@@ -90,6 +137,49 @@
         expect(expected.superSuperclassOf, superSuperclassOf?.identifier.name,
             '$name.superSuperclassOf');
       }
+      List<ClassDeclaration> mixinsOf =
+          await classIntrospector.mixinsOf(declaration);
+      expect(
+          expected.mixinsOf.length, mixinsOf.length, '$name.mixinsOf.length');
+      for (int i = 0; i < mixinsOf.length; i++) {
+        expect(expected.mixinsOf[i], mixinsOf[i].identifier.name,
+            '$name.mixinsOf[$i]');
+      }
+      List<ClassDeclaration> interfacesOf =
+          await classIntrospector.interfacesOf(declaration);
+      expect(expected.interfacesOf.length, interfacesOf.length,
+          '$name.interfacesOf.length');
+      for (int i = 0; i < interfacesOf.length; i++) {
+        expect(expected.interfacesOf[i], interfacesOf[i].identifier.name,
+            '$name.interfacesOf[$i]');
+      }
+
+      List<FieldDeclaration> fieldsOf =
+          await classIntrospector.fieldsOf(declaration);
+      expect(
+          expected.fieldsOf.length, fieldsOf.length, '$name.fieldsOf.length');
+      for (int i = 0; i < fieldsOf.length; i++) {
+        expect(expected.fieldsOf[i], fieldsOf[i].identifier.name,
+            '$name.fieldsOf[$i]');
+      }
+
+      List<MethodDeclaration> methodsOf =
+          await classIntrospector.methodsOf(declaration);
+      expect(expected.methodsOf.length, methodsOf.length,
+          '$name.methodsOf.length');
+      for (int i = 0; i < methodsOf.length; i++) {
+        expect(expected.methodsOf[i], methodsOf[i].identifier.name,
+            '$name.methodsOf[$i]');
+      }
+
+      List<ConstructorDeclaration> constructorsOf =
+          await classIntrospector.constructorsOf(declaration);
+      expect(expected.constructorsOf.length, constructorsOf.length,
+          '$name.constructorsOf.length');
+      for (int i = 0; i < constructorsOf.length; i++) {
+        expect(expected.constructorsOf[i], constructorsOf[i].identifier.name,
+            '$name.constructorsOf[$i]');
+      }
     }
     // TODO(johnniwinther): Test more properties when there are supported.
   } else {
@@ -161,17 +251,52 @@
   await check(macroApiData, 'field=', expectThrows: true);
 }
 
+Future<void> checkTypeDeclarationResolver(
+    TypeDeclarationResolver typeDeclarationResolver,
+    Map<Identifier, String?> test) async {
+  Future<void> check(Identifier identifier, String name,
+      {bool expectThrows: false}) async {
+    if (expectThrows) {
+      await throws(() async {
+        await typeDeclarationResolver.declarationOf(identifier);
+      }, '$name from $identifier',
+          expectedError: (e) => e is! ArgumentError
+              ? 'Expected ArgumentError, got ${e.runtimeType}: $e'
+              : null);
+    } else {
+      TypeDeclaration result =
+          await typeDeclarationResolver.declarationOf(identifier);
+      expect(name, result.identifier.name, '$name from $identifier');
+    }
+  }
+
+  test.forEach((Identifier identifier, String? expectedName) {
+    check(identifier, expectedName ?? identifier.name,
+        expectThrows: expectedName == null);
+  });
+}
+
 class ClassData {
   final bool isAbstract;
   final bool isExternal;
   final String superclassOf;
   final String? superSuperclassOf;
+  final List<String> interfacesOf;
+  final List<String> mixinsOf;
+  final List<String> fieldsOf;
+  final List<String> methodsOf;
+  final List<String> constructorsOf;
 
   const ClassData(
       {this.isAbstract: false,
       this.isExternal: false,
       required this.superclassOf,
-      this.superSuperclassOf});
+      this.superSuperclassOf,
+      this.interfacesOf: const [],
+      this.mixinsOf: const [],
+      this.fieldsOf: const [],
+      this.methodsOf: const [],
+      this.constructorsOf: const []});
 }
 
 class FunctionData {
diff --git a/pkg/_fe_analyzer_shared/test/macros/api/api_test_macro.dart b/pkg/_fe_analyzer_shared/test/macros/api/api_test_macro.dart
index d54db3b..3e04aae 100644
--- a/pkg/_fe_analyzer_shared/test/macros/api/api_test_macro.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/api/api_test_macro.dart
@@ -6,8 +6,6 @@
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
 import 'api_test_expectations.dart';
 
-
-
 macro class ClassMacro
     implements ClassTypesMacro, ClassDeclarationsMacro, ClassDefinitionMacro {
   const ClassMacro();
@@ -25,6 +23,9 @@
   FutureOr<void> buildDefinitionForClass(
       ClassDeclaration clazz, ClassDefinitionBuilder builder) async {
     await checkClassDeclaration(clazz, classIntrospector: builder);
+    await checkIdentifierResolver(builder);
+    await checkTypeDeclarationResolver(builder,
+        {clazz.identifier : clazz.identifier.name});
   }
 }
 
@@ -51,5 +52,6 @@
       FunctionDeclaration function, FunctionDefinitionBuilder builder) async {
     checkFunctionDeclaration(function);
     await checkIdentifierResolver(builder);
+    await checkTypeDeclarationResolver(builder, {function.identifier: null});
   }
 }
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor/augmentation_library_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor/augmentation_library_test.dart
index af3d9d7..cc99e93 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor/augmentation_library_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor/augmentation_library_test.dart
@@ -2,6 +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:_fe_analyzer_shared/src/macros/executor/introspection_impls.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor/remote_instance.dart';
 import 'package:test/fake.dart';
 import 'package:test/test.dart';
@@ -15,18 +16,26 @@
 
 void main() {
   group('AugmentationLibraryBuilder', () {
+    final intIdentifier = TestIdentifier(
+        id: RemoteInstance.uniqueId,
+        name: 'int',
+        kind: IdentifierKind.topLevelMember,
+        staticScope: null,
+        uri: Uri.parse('dart:core'));
+
     test('can combine multiple execution results', () {
       var results = [
         for (var i = 0; i < 2; i++)
           MacroExecutionResultImpl(classAugmentations: {
             for (var j = 0; j < 3; j++)
               'Foo$i$j': [
-                DeclarationCode.fromString('int get i => $i;\n'),
-                DeclarationCode.fromString('int get j => $j;\n'),
+                DeclarationCode.fromParts([intIdentifier, ' get i => $i;\n']),
+                DeclarationCode.fromParts([intIdentifier, ' get j => $j;\n']),
               ]
           }, libraryAugmentations: [
             for (var j = 0; j < 3; j++)
-              DeclarationCode.fromString('int get i${i}j$j => ${i + j};\n'),
+              DeclarationCode.fromParts(
+                  [intIdentifier, ' get i${i}j$j => ${i + j};\n']),
           ], newTypeNames: [
             'Foo${i}0',
             'Foo${i}1',
@@ -34,37 +43,42 @@
           ]),
       ];
       var library = _TestExecutor().buildAugmentationLibrary(
-          results, (Identifier i) => (i as TestIdentifier).resolved);
+          results,
+          (Identifier i) => (i as TestIdentifier).resolved,
+          (OmittedTypeAnnotation i) =>
+              (i as TestOmittedTypeAnnotation).inferredType);
       expect(library, equalsIgnoringWhitespace('''
-        int get i0j0 => 0;
-        int get i0j1 => 1;
-        int get i0j2 => 2;
-        int get i1j0 => 1;
-        int get i1j1 => 2;
-        int get i1j2 => 3;
+        import 'dart:core' as prefix0;
+
+        prefix0.int get i0j0 => 0;
+        prefix0.int get i0j1 => 1;
+        prefix0.int get i0j2 => 2;
+        prefix0.int get i1j0 => 1;
+        prefix0.int get i1j1 => 2;
+        prefix0.int get i1j2 => 3;
         augment class Foo00 {
-          int get i => 0;
-          int get j => 0;
+          prefix0.int get i => 0;
+          prefix0.int get j => 0;
         }
         augment class Foo01 {
-          int get i => 0;
-          int get j => 1;
+          prefix0.int get i => 0;
+          prefix0.int get j => 1;
         }
         augment class Foo02 {
-          int get i => 0;
-          int get j => 2;
+          prefix0.int get i => 0;
+          prefix0.int get j => 2;
         }
         augment class Foo10 {
-          int get i => 1;
-          int get j => 0;
+          prefix0.int get i => 1;
+          prefix0.int get j => 0;
         }
         augment class Foo11 {
-          int get i => 1;
-          int get j => 1;
+          prefix0.int get i => 1;
+          prefix0.int get j => 1;
         }
         augment class Foo12 {
-          int get i => 1;
-          int get j => 2;
+          prefix0.int get i => 1;
+          prefix0.int get j => 2;
         }
       '''));
     });
@@ -112,7 +126,9 @@
               '<',
               barIdentifier,
               '<T>> {\n',
-              'late int ${barInstanceMember.name};\n',
+              'late ',
+              intIdentifier,
+              ' ${barInstanceMember.name};\n',
               barIdentifier,
               '<T> build() => new ',
               barIdentifier,
@@ -130,19 +146,141 @@
         )
       ];
       var library = _TestExecutor().buildAugmentationLibrary(
-          results, (Identifier i) => (i as TestIdentifier).resolved);
+          results,
+          (Identifier i) => (i as TestIdentifier).resolved,
+          (OmittedTypeAnnotation i) =>
+              (i as TestOmittedTypeAnnotation).inferredType);
       expect(library, equalsIgnoringWhitespace('''
-        import 'package:foo/foo.dart' as i0;
-        import 'package:builder/builder.dart' as i1;
-        import 'package:bar/bar.dart' as i2;
+        import 'package:foo/foo.dart' as prefix0;
+        import 'package:builder/builder.dart' as prefix1;
+        import 'package:bar/bar.dart' as prefix2;
+        import 'dart:core' as prefix3;
 
-        class FooBuilder<T extends i0.Foo> implements i1.Builder<i2.Bar<T>> {
-          late int baz;
+        class FooBuilder<T extends prefix0.Foo> implements prefix1.Builder<prefix2.Bar<T>> {
+          late prefix3.int baz;
 
-          i2.Bar<T> build() => new i2.Bar()..baz = i2.Bar.zap;
+          prefix2.Bar<T> build() => new prefix2.Bar()..baz = prefix2.Bar.zap;
         }
       '''));
     });
+
+    test('can handle omitted type annotations', () {
+      var results = [
+        MacroExecutionResultImpl(classAugmentations: {}, libraryAugmentations: [
+          DeclarationCode.fromParts([
+            OmittedTypeAnnotationCode(
+                TestOmittedTypeAnnotation(NamedTypeAnnotationImpl(
+              id: RemoteInstance.uniqueId,
+              identifier: intIdentifier,
+              isNullable: false,
+              typeArguments: [],
+            ))),
+            ' x = 1;',
+          ]),
+        ], newTypeNames: []),
+      ];
+      var library = _TestExecutor().buildAugmentationLibrary(
+          results,
+          (Identifier i) => (i as TestIdentifier).resolved,
+          (OmittedTypeAnnotation i) =>
+              (i as TestOmittedTypeAnnotation).inferredType);
+      expect(library, equalsIgnoringWhitespace('''
+        import 'dart:core' as prefix0;
+
+        prefix0.int x = 1;
+      '''));
+    });
+
+    test('can handle name conflicts', () {
+      var omittedType0 = TestOmittedTypeAnnotation();
+      var omittedType1 = TestOmittedTypeAnnotation();
+
+      var omittedTypeIdentifier = TestIdentifier(
+          id: RemoteInstance.uniqueId,
+          name: 'OmittedType',
+          kind: IdentifierKind.topLevelMember,
+          staticScope: null,
+          uri: Uri.parse('package:foo/foo.dart'));
+      var omittedTypeIdentifier0 = TestIdentifier(
+          id: RemoteInstance.uniqueId,
+          name: 'OmittedType0',
+          kind: IdentifierKind.topLevelMember,
+          staticScope: null,
+          uri: Uri.parse('package:bar/bar.dart'));
+      var prefixInstanceMember = TestIdentifier(
+          id: RemoteInstance.uniqueId,
+          name: 'prefix',
+          kind: IdentifierKind.instanceMember,
+          staticScope: null,
+          uri: Uri.parse('package:bar/bar.dart'));
+      var prefix0InstanceMember = TestIdentifier(
+          id: RemoteInstance.uniqueId,
+          name: 'prefix0',
+          kind: IdentifierKind.instanceMember,
+          staticScope: null,
+          uri: Uri.parse('package:bar/bar.dart'));
+      var prefix1StaticMember = TestIdentifier(
+          id: RemoteInstance.uniqueId,
+          name: 'prefix1',
+          kind: IdentifierKind.staticInstanceMember,
+          staticScope: 'OmittedType1',
+          uri: Uri.parse('package:bar/bar.dart'));
+      var results = [
+        MacroExecutionResultImpl(
+          classAugmentations: {},
+          libraryAugmentations: [
+            DeclarationCode.fromParts([
+              'class OmittedType {\n  ',
+              omittedType0.code,
+              ' method(',
+              omittedType1.code,
+              ' o) {\n    ',
+              intIdentifier,
+              ' ${prefixInstanceMember.name} = 0;\n    ',
+              omittedTypeIdentifier,
+              ' ${prefix0InstanceMember.name} = ',
+              'new ',
+              omittedTypeIdentifier,
+              '();\n    ',
+              'new ',
+              omittedTypeIdentifier0,
+              '()..',
+              prefixInstanceMember,
+              ' = ',
+              prefix1StaticMember,
+              ';',
+              '\n  }',
+              '\n}',
+            ]),
+          ],
+          newTypeNames: [
+            'OmittedType',
+          ],
+        )
+      ];
+      var omittedTypes = <OmittedTypeAnnotation, String>{};
+      var library = _TestExecutor().buildAugmentationLibrary(
+          results,
+          (Identifier i) => (i as TestIdentifier).resolved,
+          (OmittedTypeAnnotation i) =>
+              (i as TestOmittedTypeAnnotation).inferredType,
+          omittedTypes: omittedTypes);
+      expect(library, equalsIgnoringWhitespace('''
+        import 'dart:core' as prefix2_0;
+        import 'package:foo/foo.dart' as prefix2_1;
+        import 'package:bar/bar.dart' as prefix2_2;
+
+        class OmittedType {
+          OmittedType2_0 method(OmittedType2_1 o) {
+            prefix2_0.int prefix = 0;
+            prefix2_1.OmittedType prefix0 = new prefix2_1.OmittedType();
+            new prefix2_2.OmittedType0()..prefix = prefix2_2.OmittedType1.prefix1;
+          }
+        }
+      '''));
+      expect(omittedTypes[omittedType0], 'OmittedType2_0');
+      expect(omittedTypes[omittedType1], 'OmittedType2_1');
+    });
   });
 }
 
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart
index 13b54ac..c70ad25 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart
@@ -11,7 +11,9 @@
 import 'package:_fe_analyzer_shared/src/macros/executor/isolated_executor.dart'
     as isolatedExecutor;
 import 'package:_fe_analyzer_shared/src/macros/executor/process_executor.dart'
-    as processExecutor;
+    as processExecutor show start;
+import 'package:_fe_analyzer_shared/src/macros/executor/process_executor.dart'
+    hide start;
 
 import 'package:test/test.dart';
 
@@ -26,11 +28,15 @@
   late File simpleMacroFile;
   late Directory tmpDir;
 
-  for (var executorKind in ['Isolated', 'Process']) {
+  for (var executorKind in [
+    'Isolated',
+    'ProcessSocket',
+    'ProcessStdio',
+  ]) {
     group('$executorKind executor', () {
       for (var mode in [
         SerializationMode.byteDataServer,
-        SerializationMode.jsonServer
+        SerializationMode.jsonServer,
       ]) {
         final clientMode = mode == SerializationMode.byteDataServer
             ? SerializationMode.byteDataClient
@@ -42,7 +48,12 @@
                 File(Platform.script.resolve('simple_macro.dart').toFilePath());
             executor = executorKind == 'Isolated'
                 ? await isolatedExecutor.start(mode)
-                : await processExecutor.start(mode);
+                : executorKind == 'ProcessSocket'
+                    ? await processExecutor.start(
+                        mode, CommunicationChannel.socket)
+                    : await processExecutor.start(
+                        mode, CommunicationChannel.stdio);
+
             tmpDir = Directory.systemTemp.createTempSync('executor_test');
             macroUri = simpleMacroFile.absolute.uri;
 
@@ -271,7 +282,7 @@
                 expect(
                     result.libraryAugmentations.single.debugString().toString(),
                     equalsIgnoringWhitespace('''
-                String get delegate_myVariable => _myVariable;'''));
+                /*inferred*/String get delegate_myVariable => _myVariable;'''));
               });
 
               test('on fields', () async {
@@ -319,7 +330,8 @@
                     FakeIdentifierResolver(),
                     Fixtures.testTypeResolver,
                     Fixtures.testClassIntrospector,
-                    Fixtures.testTypeDeclarationResolver);
+                    Fixtures.testTypeDeclarationResolver,
+                    Fixtures.testTypeInferrer);
                 expect(result.classAugmentations, isEmpty);
                 expect(
                     result.libraryAugmentations.single.debugString().toString(),
@@ -341,7 +353,8 @@
                     FakeIdentifierResolver(),
                     Fixtures.testTypeResolver,
                     Fixtures.testClassIntrospector,
-                    Fixtures.testTypeDeclarationResolver);
+                    Fixtures.testTypeDeclarationResolver,
+                    Fixtures.testTypeInferrer);
                 expect(definitionResult.classAugmentations, hasLength(1));
                 var augmentationStrings = definitionResult
                     .classAugmentations['MyClass']!
@@ -359,7 +372,8 @@
                     FakeIdentifierResolver(),
                     Fixtures.testTypeResolver,
                     Fixtures.testClassIntrospector,
-                    Fixtures.testTypeDeclarationResolver);
+                    Fixtures.testTypeDeclarationResolver,
+                    Fixtures.testTypeInferrer);
                 expect(definitionResult.classAugmentations, hasLength(1));
                 expect(
                     definitionResult.classAugmentations['MyClass']!.first
@@ -376,7 +390,8 @@
                     FakeIdentifierResolver(),
                     Fixtures.testTypeResolver,
                     Fixtures.testClassIntrospector,
-                    Fixtures.testTypeDeclarationResolver);
+                    Fixtures.testTypeDeclarationResolver,
+                    Fixtures.testTypeInferrer);
                 expect(result.classAugmentations, isEmpty);
                 expect(
                     result.libraryAugmentations.single.debugString().toString(),
@@ -398,7 +413,8 @@
                     FakeIdentifierResolver(),
                     Fixtures.testTypeResolver,
                     Fixtures.testClassIntrospector,
-                    Fixtures.testTypeDeclarationResolver);
+                    Fixtures.testTypeDeclarationResolver,
+                    Fixtures.testTypeInferrer);
                 expect(result.classAugmentations, isEmpty);
                 expect(
                     result.libraryAugmentations.single.debugString().toString(),
@@ -421,14 +437,15 @@
                     FakeIdentifierResolver(),
                     Fixtures.testTypeResolver,
                     Fixtures.testClassIntrospector,
-                    Fixtures.testTypeDeclarationResolver);
+                    Fixtures.testTypeDeclarationResolver,
+                    Fixtures.testTypeInferrer);
                 expect(result.classAugmentations, isEmpty);
                 expect(
                     result.libraryAugmentations
                         .map((a) => a.debugString().toString()),
                     unorderedEquals([
                       equalsIgnoringWhitespace('''
-                augment String get _myVariable {
+                augment /*inferred*/String get _myVariable {
                   print('parentClass: ');
                   print('isExternal: false');
                   print('isFinal: true');
@@ -436,11 +453,11 @@
                   return augment super;
                 }'''),
                       equalsIgnoringWhitespace('''
-                augment set _myVariable(String value) {
+                augment set _myVariable(/*inferred*/String value) {
                   augment super = value;
                 }'''),
                       equalsIgnoringWhitespace('''
-                augment final String _myVariable = 'new initial value' + augment super;
+                augment final /*inferred*/String _myVariable = 'new initial value' + augment super;
                 '''),
                     ]));
               });
@@ -452,7 +469,8 @@
                     FakeIdentifierResolver(),
                     Fixtures.testTypeResolver,
                     Fixtures.testClassIntrospector,
-                    Fixtures.testTypeDeclarationResolver);
+                    Fixtures.testTypeDeclarationResolver,
+                    Fixtures.testTypeInferrer);
                 expect(definitionResult.classAugmentations, hasLength(1));
                 expect(
                     definitionResult.classAugmentations['MyClass']!
@@ -468,7 +486,8 @@
                     FakeIdentifierResolver(),
                     Fixtures.testTypeResolver,
                     Fixtures.testClassIntrospector,
-                    Fixtures.testTypeDeclarationResolver);
+                    Fixtures.testTypeDeclarationResolver,
+                    Fixtures.testTypeInferrer);
                 expect(definitionResult.classAugmentations, hasLength(1));
                 var augmentationStrings = definitionResult
                     .classAugmentations['MyClass']!
@@ -491,7 +510,7 @@
 }
 
 final constructorDefinitionMatcher = equalsIgnoringWhitespace('''
-augment MyClass.myConstructor() {
+augment MyClass.myConstructor(/*inferred*/String myField, ) {
   print('definingClass: MyClass');
   print('isFactory: false');
   print('isAbstract: false');
@@ -499,6 +518,7 @@
   print('isGetter: false');
   print('isSetter: false');
   print('returnType: MyClass');
+  print('positionalParam: String (inferred) myField');
   return augment super();
 }''');
 
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor/simple_macro.dart b/pkg/_fe_analyzer_shared/test/macros/executor/simple_macro.dart
index b5a2d2d..a7d31f1 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor/simple_macro.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor/simple_macro.dart
@@ -54,11 +54,6 @@
   FutureOr<void> buildDeclarationsForConstructor(
       ConstructorDeclaration constructor,
       ClassMemberDeclarationBuilder builder) {
-    if (constructor.positionalParameters.isNotEmpty ||
-        constructor.namedParameters.isNotEmpty) {
-      throw new UnsupportedError(
-          'Can only run on constructors with no parameters!');
-    }
     var className = constructor.definingClass.name;
     var constructorName = constructor.identifier.name;
     builder.declareInClass(DeclarationCode.fromString(
@@ -69,12 +64,6 @@
   @override
   FutureOr<void> buildDeclarationsForFunction(
       FunctionDeclaration function, DeclarationBuilder builder) {
-    if (!function.isSetter &&
-        (function.positionalParameters.isNotEmpty ||
-            function.namedParameters.isNotEmpty)) {
-      throw new UnsupportedError(
-          'Can only run on functions with no parameters!');
-    }
     var functionName = function.identifier.name;
     builder.declareInLibrary(DeclarationCode.fromParts([
       function.returnType.code,
@@ -161,7 +150,7 @@
     var fields = (await builder.fieldsOf(clazz));
 
     builder.augment(
-      body: _buildFunctionAugmentation(constructor),
+      body: await _buildFunctionAugmentation(constructor, builder),
       initializers: [
         for (var field in fields)
           // TODO: Compare against actual `int` type.
@@ -180,7 +169,7 @@
   @override
   Future<void> buildDefinitionForFunction(
       FunctionDeclaration function, FunctionDefinitionBuilder builder) async {
-    builder.augment(_buildFunctionAugmentation(function));
+    builder.augment(await _buildFunctionAugmentation(function, builder));
   }
 
   @override
@@ -355,47 +344,57 @@
   }
 }
 
-FunctionBodyCode _buildFunctionAugmentation(FunctionDeclaration function) =>
-    FunctionBodyCode.fromParts([
-      '{\n',
-      if (function is MethodDeclaration)
-        "print('definingClass: ${function.definingClass.name}');\n",
-      if (function is ConstructorDeclaration)
-        "print('isFactory: ${function.isFactory}');\n",
-      '''
+Future<FunctionBodyCode> _buildFunctionAugmentation(
+    FunctionDeclaration function, TypeInferrer inferrer) async {
+  Future<List<Object>> typeParts(TypeAnnotation annotation) async {
+    if (annotation is OmittedTypeAnnotation) {
+      var inferred = await inferrer.inferType(annotation);
+      return [inferred.code, ' (inferred)'];
+    }
+    return [annotation.code];
+  }
+
+  return FunctionBodyCode.fromParts([
+    '{\n',
+    if (function is MethodDeclaration)
+      "print('definingClass: ${function.definingClass.name}');\n",
+    if (function is ConstructorDeclaration)
+      "print('isFactory: ${function.isFactory}');\n",
+    '''
       print('isAbstract: ${function.isAbstract}');
       print('isExternal: ${function.isExternal}');
       print('isGetter: ${function.isGetter}');
       print('isSetter: ${function.isSetter}');
       print('returnType: ''',
-      function.returnType.code,
+    function.returnType.code,
+    "');\n",
+    for (var param in function.positionalParameters) ...[
+      "print('positionalParam: ",
+      ...await typeParts(param.type),
+      ' ${param.identifier.name}',
       "');\n",
-      for (var param in function.positionalParameters) ...[
-        "print('positionalParam: ",
-        param.type.code,
-        ' ${param.identifier.name}',
-        "');\n",
-      ],
-      for (var param in function.namedParameters) ...[
-        "print('namedParam: ",
-        param.type.code,
-        ' ${param.identifier.name}',
-        "');\n",
-      ],
-      for (var param in function.typeParameters) ...[
-        "print('typeParam: ${param.identifier.name} ",
-        if (param.bound != null) param.bound!.code,
-        "');\n",
-      ],
-      'return augment super',
-      if (function.isSetter) ...[
-        ' = ',
-        function.positionalParameters.first.identifier,
-      ],
-      if (!function.isGetter && !function.isSetter) '()',
-      ''';
+    ],
+    for (var param in function.namedParameters) ...[
+      "print('namedParam: ",
+      ...await typeParts(param.type),
+      ' ${param.identifier.name}',
+      "');\n",
+    ],
+    for (var param in function.typeParameters) ...[
+      "print('typeParam: ${param.identifier.name} ",
+      if (param.bound != null) param.bound!.code,
+      "');\n",
+    ],
+    'return augment super',
+    if (function.isSetter) ...[
+      ' = ',
+      function.positionalParameters.first.identifier,
+    ],
+    if (!function.isGetter && !function.isSetter) '()',
+    ''';
     }''',
-    ]);
+  ]);
+}
 
 extension _ on String {
   String capitalize() => '${this[0].toUpperCase()}${substring(1)}';
diff --git a/pkg/_fe_analyzer_shared/test/macros/util.dart b/pkg/_fe_analyzer_shared/test/macros/util.dart
index edb0788..6049823 100644
--- a/pkg/_fe_analyzer_shared/test/macros/util.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/util.dart
@@ -111,6 +111,23 @@
       (library == other.library && identifier.name == other.identifier.name);
 }
 
+/// Assumes all omitted types are [TestOmittedTypeAnnotation]s and just returns
+/// the inferred type directly.
+class TestTypeInferrer implements TypeInferrer {
+  @override
+  Future<TypeAnnotation> inferType(
+          TestOmittedTypeAnnotation omittedType) async =>
+      omittedType.inferredType!;
+}
+
+/// Knows its inferred type ahead of time.
+class TestOmittedTypeAnnotation extends OmittedTypeAnnotationImpl {
+  final TypeAnnotation? inferredType;
+
+  TestOmittedTypeAnnotation([this.inferredType])
+      : super(id: RemoteInstance.uniqueId);
+}
+
 /// An identifier that knows the resolved version of itself.
 class TestIdentifier extends IdentifierImpl {
   final ResolvedIdentifier resolved;
@@ -137,6 +154,13 @@
         part.debugString(buffer);
       } else if (part is IdentifierImpl) {
         buffer.write(part.name);
+      } else if (part is TestOmittedTypeAnnotation) {
+        if (part.inferredType != null) {
+          buffer.write('/*inferred*/');
+          part.inferredType!.code.debugString(buffer);
+        } else {
+          buffer.write('/*omitted*/');
+        }
       } else {
         buffer.write(part as String);
       }
@@ -244,6 +268,7 @@
       identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'String'),
       isNullable: false,
       typeArguments: const []);
+  static final inferredStringType = TestOmittedTypeAnnotation(stringType);
   static final voidType = NamedTypeAnnotationImpl(
       id: RemoteInstance.uniqueId,
       identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'void'),
@@ -271,7 +296,7 @@
       isExternal: false,
       isFinal: true,
       isLate: false,
-      type: stringType);
+      type: inferredStringType);
   static final myVariableGetter = FunctionDeclarationImpl(
       id: RemoteInstance.uniqueId,
       identifier:
@@ -349,7 +374,15 @@
       isOperator: false,
       isSetter: false,
       namedParameters: [],
-      positionalParameters: [],
+      positionalParameters: [
+        ParameterDeclarationImpl(
+            id: RemoteInstance.uniqueId,
+            identifier:
+                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myField'),
+            isNamed: false,
+            isRequired: true,
+            type: TestOmittedTypeAnnotation(myField.type))
+      ],
       returnType: myClassType,
       typeParameters: [],
       definingClass: myClassType.identifier,
@@ -435,4 +468,6 @@
   );
   static final testTypeDeclarationResolver =
       TestTypeDeclarationResolver({myClass.identifier: myClass});
+
+  static final testTypeInferrer = TestTypeInferrer();
 }
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index 0c71378..3a8528c 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -1413,14 +1413,17 @@
       flow.logicalBinaryOp_begin();
     }
     var leftType = analyzeExpression(lhs);
+    EqualityInfo<Var, Type>? leftInfo;
     if (isEquals) {
-      flow.equalityOp_rightBegin(lhs, leftType);
+      leftInfo = flow.equalityOperand_end(lhs, leftType);
     } else if (isLogical) {
       flow.logicalBinaryOp_rightBegin(lhs, node, isAnd: isAnd);
     }
     var rightType = analyzeExpression(rhs);
     if (isEquals) {
-      flow.equalityOp_end(node, rhs, rightType, notEqual: isNot);
+      flow.equalityOperation_end(
+          node, leftInfo, flow.equalityOperand_end(rhs, rightType),
+          notEqual: isNot);
     } else if (isLogical) {
       flow.logicalBinaryOp_end(node, rhs, isAnd: isAnd);
     }
diff --git a/pkg/_fe_analyzer_shared/test/util/dependency_walker_test.dart b/pkg/_fe_analyzer_shared/test/util/dependency_walker_test.dart
new file mode 100644
index 0000000..ec5cd44
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/util/dependency_walker_test.dart
@@ -0,0 +1,229 @@
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/util/dependency_walker.dart';
+import 'package:test/test.dart';
+
+main() {
+  late Map<String, TestNode> nodes;
+  setUp(() {
+    nodes = {};
+  });
+
+  TestNode getNode(String name) =>
+      nodes.putIfAbsent(name, () => TestNode(name));
+
+  void makeGraph(Map<String, List<String>> graph) {
+    graph.forEach((name, deps) {
+      var node = getNode(name);
+      for (var dep in deps) {
+        node._dependencies.add(getNode(dep));
+      }
+    });
+  }
+
+  TestWalker walk(String startingNodeName) =>
+      TestWalker()..walk(getNode(startingNodeName));
+
+  void checkGraph(Map<String, List<String>> graph, String startingNodeName,
+      List<List<String>> expectedEvaluations, List<bool> expectedSccFlags) {
+    makeGraph(graph);
+    var walker = walk(startingNodeName);
+    expect(walker._evaluations, expectedEvaluations.map((x) => x.toSet()));
+    expect(walker._sccFlags, expectedSccFlags);
+  }
+
+  test('Complex graph', () {
+    checkGraph(
+        {
+          'a': ['b', 'c'],
+          'b': ['c', 'd'],
+          'c': [],
+          'd': ['c', 'e'],
+          'e': ['b', 'f'],
+          'f': ['c', 'd']
+        },
+        'a',
+        [
+          ['c'],
+          ['b', 'd', 'e', 'f'],
+          ['a']
+        ],
+        [false, true, false]);
+  });
+
+  test('Diamond', () {
+    checkGraph(
+        {
+          'a': ['b', 'c'],
+          'b': ['d'],
+          'c': ['d'],
+          'd': []
+        },
+        'a',
+        [
+          ['d'],
+          ['b'],
+          ['c'],
+          ['a']
+        ],
+        [false, false, false, false]);
+  });
+
+  test('Single node', () {
+    checkGraph(
+        {'a': []},
+        'a',
+        [
+          ['a']
+        ],
+        [false]);
+  });
+
+  test('Single node with trivial cycle', () {
+    checkGraph(
+        {
+          'a': ['a']
+        },
+        'a',
+        [
+          ['a']
+        ],
+        [true]);
+  });
+
+  test('Three nodes with circular dependency', () {
+    checkGraph(
+        {
+          'a': ['b'],
+          'b': ['c'],
+          'c': ['a'],
+        },
+        'a',
+        [
+          ['a', 'b', 'c']
+        ],
+        [true]);
+  });
+
+  group('Two backlinks:', () {
+    test('earlier first', () {
+      // Test a graph A->B->C->D, where D points back to B and then C.
+      checkGraph(
+          {
+            'a': ['b'],
+            'b': ['c'],
+            'c': ['d'],
+            'd': ['b', 'c']
+          },
+          'a',
+          [
+            ['b', 'c', 'd'],
+            ['a']
+          ],
+          [true, false]);
+    });
+
+    test('later first', () {
+      // Test a graph A->B->C->D, where D points back to C and then B.
+      checkGraph(
+          {
+            'a': ['b'],
+            'b': ['c'],
+            'c': ['d'],
+            'd': ['c', 'b']
+          },
+          'a',
+          [
+            ['b', 'c', 'd'],
+            ['a']
+          ],
+          [true, false]);
+    });
+  });
+
+  test('Two nodes with circular dependency', () {
+    checkGraph(
+        {
+          'a': ['b'],
+          'b': ['a']
+        },
+        'a',
+        [
+          ['a', 'b']
+        ],
+        [true]);
+  });
+
+  test('Two nodes with simple dependency', () {
+    checkGraph(
+        {
+          'a': ['b'],
+          'b': []
+        },
+        'a',
+        [
+          ['b'],
+          ['a']
+        ],
+        [false, false]);
+  });
+
+  test('Do not revisit already-evaluated nodes', () {
+    makeGraph({
+      'a': ['b'],
+      'b': []
+    });
+    var walker = TestWalker();
+    var a = getNode('a');
+    walker.walk(a);
+    expect(walker._evaluations, hasLength(2));
+    walker.walk(a);
+    expect(walker._evaluations, hasLength(2));
+  });
+}
+
+class TestNode extends Node<TestNode> {
+  final String _name;
+
+  @override
+  bool isEvaluated = false;
+
+  bool _computeDependenciesCalled = false;
+
+  final _dependencies = <TestNode>[];
+
+  TestNode(this._name);
+
+  @override
+  List<TestNode> computeDependencies() {
+    expect(_computeDependenciesCalled, false);
+    _computeDependenciesCalled = true;
+    return _dependencies;
+  }
+}
+
+class TestWalker extends DependencyWalker<TestNode> {
+  final _evaluations = <Set<String>>[];
+  final _sccFlags = <bool>[];
+
+  @override
+  void evaluate(TestNode v) {
+    v.isEvaluated = true;
+    _evaluations.add({v._name});
+    _sccFlags.add(false);
+  }
+
+  @override
+  void evaluateScc(List<TestNode> scc) {
+    for (var v in scc) {
+      v.isEvaluated = true;
+    }
+    var sccNames = scc.map((node) => node._name).toSet();
+    // Make sure there were no duplicates
+    expect(sccNames.length, scc.length);
+    _evaluations.add(sccNames);
+    _sccFlags.add(true);
+  }
+}
diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart
index 6dab119..26d8396 100644
--- a/pkg/_js_interop_checks/lib/js_interop_checks.dart
+++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart
@@ -14,10 +14,10 @@
         messageJsInteropEnclosingClassJSAnnotationContext,
         messageJsInteropExternalExtensionMemberOnTypeInvalid,
         messageJsInteropExternalMemberNotJSAnnotated,
-        messageJsInteropIndexNotSupported,
         messageJsInteropNamedParameters,
         messageJsInteropNonExternalConstructor,
         messageJsInteropNonExternalMember,
+        messageJsInteropOperatorsNotSupported,
         templateJsInteropDartClassExtendsJSClass,
         templateJsInteropStaticInteropWithInstanceMembers,
         templateJsInteropStaticInteropWithNonStaticSupertype,
@@ -220,10 +220,9 @@
       _checkDisallowedExternal(procedure);
     } else {
       // Check JS interop indexing.
-      if (!procedure.isStatic &&
-          (procedure.name.text == '[]=' || procedure.name.text == '[]')) {
+      if (!procedure.isStatic && procedure.kind == ProcedureKind.Operator) {
         _diagnosticsReporter.report(
-            messageJsInteropIndexNotSupported,
+            messageJsInteropOperatorsNotSupported,
             procedure.fileOffset,
             procedure.name.text.length,
             procedure.fileUri);
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index be632ea..d610cb4 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -109,7 +109,7 @@
 <body>
 <h1>Analysis Server API Specification</h1>
 <h1 style="color:#999999">Version
-  1.32.10
+  1.33.0
 </h1>
 <p>
   This document contains a specification of the API provided by the
@@ -236,6 +236,11 @@
   ignoring the item or treating it with some default/fallback handling.
 </p>
 <h3>Changelog</h3>
+<h4>1.33.0</h4>
+<ul>
+  <li>Requests <tt>getSuggestions2</tt> and <tt>getSuggestionDetails2</tt>
+    are enabled.</li>
+</ul>
 <h4>1.32.10</h4>
 <ul>
   <li>The <tt>MOVE_FILE</tt> refactor now supports moving/renaming folders.</li>
@@ -313,9 +318,11 @@
 </ul>
 
 <p><a href="#domain_completion">Completion</a></p><ul><li><a href="#request_completion.getSuggestions">completion.getSuggestions</a></li>
+<li><a href="#request_completion.getSuggestions2">completion.getSuggestions2</a></li>
 <li><a href="#request_completion.setSubscriptions">completion.setSubscriptions</a></li>
 <li><a class="deprecated" href="#request_completion.registerLibraryPaths">completion.registerLibraryPaths</a></li>
 <li><a href="#request_completion.getSuggestionDetails">completion.getSuggestionDetails</a></li>
+<li><a href="#request_completion.getSuggestionDetails2">completion.getSuggestionDetails2</a></li>
 </ul>
 
 <p><a href="#domain_search">Search</a></p><ul><li><a href="#request_search.findElementReferences">search.findElementReferences</a></li>
@@ -1606,6 +1613,94 @@
           The identifier used to associate results with this
           completion request.
         </p>
+      </dd></dl></dd><dt class="request"><a name="request_completion.getSuggestions2">completion.getSuggestions2</a></dt><dd><div class="box"><pre>request: {
+  "id": String
+  "method": "completion.getSuggestions2"
+  "params": {
+    "<b>file</b>": <a href="#type_FilePath">FilePath</a>
+    "<b>offset</b>": int
+    "<b>maxResults</b>": int
+    "<b>completionCaseMatchingMode</b>": <span style="color:#999999">optional</span> <a href="#type_CompletionCaseMatchingMode">CompletionCaseMatchingMode</a>
+  }
+}</pre><br><pre>response: {
+  "id": String
+  "error": <span style="color:#999999">optional</span> <a href="#type_RequestError">RequestError</a>
+  "result": {
+    "<b>replacementOffset</b>": int
+    "<b>replacementLength</b>": int
+    "<b>suggestions</b>": List&lt;<a href="#type_CompletionSuggestion">CompletionSuggestion</a>&gt;
+    "<b>isIncomplete</b>": bool
+  }
+}</pre></div>
+    <p>
+      Request that completion suggestions for the given offset in the given
+      file be returned. The suggestions will be filtered using fuzzy matching
+      with the already existing prefix.
+    </p>
+    
+    
+  <h4>parameters:</h4><dl><dt class="field"><b>file: <a href="#type_FilePath">FilePath</a></b></dt><dd>
+        
+        <p>
+          The file containing the point at which suggestions are to be made.
+        </p>
+      </dd><dt class="field"><b>offset: int</b></dt><dd>
+        
+        <p>
+          The offset within the file at which suggestions are to be made.
+        </p>
+      </dd><dt class="field"><b>maxResults: int</b></dt><dd>
+        
+        <p>
+          The maximum number of suggestions to return. If the number of
+          suggestions after filtering is greater than the <tt>maxResults</tt>,
+          then <tt>isIncomplete</tt> is set to <tt>true</tt>.
+        </p>
+      </dd><dt class="field"><b>completionCaseMatchingMode: <a href="#type_CompletionCaseMatchingMode">CompletionCaseMatchingMode</a><span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          The mode of code completion being invoked. If no value is provided,
+          <tt>MATCH_FIRST_CHAR</tt> will be assumed.
+        </p>
+      </dd></dl><h4>returns:</h4><dl><dt class="field"><b>replacementOffset: int</b></dt><dd>
+        
+        <p>
+          The offset of the start of the text to be  replaced. This will be
+          different from the offset used  to request the completion suggestions
+          if there was a portion of an identifier before the original offset.
+          In particular, the replacementOffset will be the offset of the
+          beginning of said identifier.
+        </p>
+      </dd><dt class="field"><b>replacementLength: int</b></dt><dd>
+        
+        <p>
+          The length of the text to be replaced if the remainder of the
+          identifier containing the cursor is to be replaced when the
+          suggestion is applied (that is, the number of characters in the
+          existing identifier).
+        </p>
+      </dd><dt class="field"><b>suggestions: List&lt;<a href="#type_CompletionSuggestion">CompletionSuggestion</a>&gt;</b></dt><dd>
+        
+        <p>
+          The completion suggestions being reported. This list is filtered
+          by the already existing prefix, and sorted first by relevance,
+          and (if the same) by the suggestion text. The list will have at
+          most <tt>maxResults</tt> items. If the user types a new keystroke,
+          the client is expected to either do local filtering (when the
+          returned list was complete), or ask the server again (if
+          <tt>isIncomplete</tt> was <tt>true</tt>).
+        </p>
+        <p>
+          This list contains suggestions from both imported, and not yet
+          imported libraries. Items from not yet imported libraries will
+          have <tt>isNotImported</tt> set to <tt>true</tt>.
+        </p>
+      </dd><dt class="field"><b>isIncomplete: bool</b></dt><dd>
+        
+        <p>
+          True if the number of suggestions after filtering was greater than
+          the requested <tt>maxResults</tt>.
+        </p>
       </dd></dl></dd><dt class="request"><a name="request_completion.setSubscriptions">completion.setSubscriptions</a></dt><dd><div class="box"><pre>request: {
   "id": String
   "method": "completion.setSubscriptions"
@@ -1717,6 +1812,73 @@
           the accepted completion suggestion needs to be imported. The field
           will be omitted if there are no additional changes that need to be made.
         </p>
+      </dd></dl></dd><dt class="request"><a name="request_completion.getSuggestionDetails2">completion.getSuggestionDetails2</a></dt><dd><div class="box"><pre>request: {
+  "id": String
+  "method": "completion.getSuggestionDetails2"
+  "params": {
+    "<b>file</b>": <a href="#type_FilePath">FilePath</a>
+    "<b>offset</b>": int
+    "<b>completion</b>": String
+    "<b>libraryUri</b>": String
+  }
+}</pre><br><pre>response: {
+  "id": String
+  "error": <span style="color:#999999">optional</span> <a href="#type_RequestError">RequestError</a>
+  "result": {
+    "<b>completion</b>": String
+    "<b>change</b>": <a href="#type_SourceChange">SourceChange</a>
+  }
+}</pre></div>
+    <p>
+      Clients must make this request when the user has selected a completion
+      suggestion with the <tt>isNotImported</tt> field set to <tt>true</tt>.
+      The server will respond with the text to insert, as well as any
+      <tt>SourceChange</tt> that needs to be applied in case the completion
+      requires an additional import to be  added. The text to insert might be
+      different from the original suggestion to include an import prefix if the
+      library will be imported with a prefix to avoid shadowing
+      conflicts in the file.
+    </p>
+    
+    
+  <h4>parameters:</h4><dl><dt class="field"><b>file: <a href="#type_FilePath">FilePath</a></b></dt><dd>
+        
+        <p>
+          The path of the file into which this completion is being inserted.
+        </p>
+      </dd><dt class="field"><b>offset: int</b></dt><dd>
+        
+        <p>
+          The offset in the file where the completion will be inserted.
+        </p>
+      </dd><dt class="field"><b>completion: String</b></dt><dd>
+        
+        <p>
+          The <tt>completion</tt> from the selected
+          <tt>CompletionSuggestion</tt>.  It could be a name of a class, or a
+          name of a constructor in form "typeName.constructorName()", or an
+          enumeration constant in form "enumName.constantName", etc.
+        </p>
+      </dd><dt class="field"><b>libraryUri: String</b></dt><dd>
+        
+        <p>
+          The URI of the library to import, so that the element referenced
+          in the <tt>completion</tt> becomes accessible.
+        </p>
+      </dd></dl><h4>returns:</h4><dl><dt class="field"><b>completion: String</b></dt><dd>
+        
+        <p>
+          The full text to insert, which possibly includes now an import prefix.
+          The client should insert this text, not the <tt>completion</tt> from
+          the selected <tt>CompletionSuggestion</tt>.
+        </p>
+      </dd><dt class="field"><b>change: <a href="#type_SourceChange">SourceChange</a></b></dt><dd>
+        
+        <p>
+          A change for the client to apply to make the accepted completion
+          suggestion available. In most cases the change is to add a new
+          import directive to the file.
+        </p>
       </dd></dl></dd></dl><h3>Notifications</h3><dl><dt class="notification"><a name="notification_completion.results">completion.results</a></dt><dd><div class="box"><pre>notification: {
   "event": "completion.results"
   "params": {
@@ -2161,6 +2323,7 @@
   
   
   
+  
 <h3>Requests</h3><dl><dt class="request"><a name="request_edit.format">edit.format</a></dt><dd><div class="box"><pre>request: {
   "id": String
   "method": "edit.format"
@@ -3711,6 +3874,37 @@
           The type of the options parameter being suggested. This field is
           omitted if the parameterName field is omitted.
         </p>
+      </dd><dt class="field"><b>libraryUri: String<span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          This field  is omitted if <tt>getSuggestions</tt> was used rather
+          than <tt>getSuggestions2</tt>.
+        </p>
+        <p>
+          This field  is omitted if this suggestion corresponds to a locally
+          declared element.
+        </p>
+        <p>
+          If this suggestion corresponds to an already imported element,
+          then this field is the URI of a library that provides this element,
+          not the URI of the library where the element is declared.
+        </p>
+        <p>
+          If this suggestion corresponds to an element from a not yet
+          imported library, this field is the URI of a library that could be
+          imported to make this suggestion  accessible in the file where
+          completion was requested, such as <tt>package:foo/bar.dart</tt> or
+          <tt>file:///home/me/workspace/foo/test/bar_test.dart</tt>.
+        </p>
+      </dd><dt class="field"><b>isNotImported: bool<span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          True if the suggestion is for an element from a not yet imported
+          library. This field is omitted if the element is declared locally,
+          or is from library is already imported, so that the suggestion can
+          be inserted as is, or if <tt>getSuggestions</tt> was used rather
+          than <tt>getSuggestions2</tt>.
+        </p>
       </dd></dl></dd><dt class="typeDefinition"><a name="type_CompletionSuggestionKind">CompletionSuggestionKind: String</a></dt><dd>
     <p>
       An enumeration of the kinds of elements that can be included in a
@@ -4777,6 +4971,13 @@
       the user edit the variable name after the operation, all occurrences of
       the name could be edited simultaneously.
     </p>
+    <p>
+      Edit groups may have a length of 0 and function as tabstops where there
+      is no default text, for example, an edit that inserts an <tt>if</tt>
+      statement might provide an empty group between parens where a condition
+      should be typed. For this reason, it's also valid for a group to contain
+      only a single position that is not linked to others.
+    </p>
     
   <dl><dt class="field"><b>positions: List&lt;<a href="#type_Position">Position</a>&gt;</b></dt><dd>
         
@@ -6148,7 +6349,7 @@
   TODO: TBD
 </p>
 <h2 class="domain"><a name="index">Index</a></h2>
-<h3>Domains</h3><h4>server (<a href="#domain_server">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_server.getVersion">getVersion</a></li><li><a href="#request_server.shutdown">shutdown</a></li><li><a href="#request_server.setSubscriptions">setSubscriptions</a></li><li><a href="#request_server.cancelRequest">cancelRequest</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_server.connected">connected</a></li><li><a href="#notification_server.error">error</a></li><li><a href="#notification_server.log">log</a></li><li><a href="#notification_server.status">status</a></li></ul></div></div><h4>analysis (<a href="#domain_analysis">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_analysis.getErrors">getErrors</a></li><li><a href="#request_analysis.getHover">getHover</a></li><li><a href="#request_analysis.getLibraryDependencies">getLibraryDependencies</a></li><li><a href="#request_analysis.getNavigation">getNavigation</a></li><li><a href="#request_analysis.getReachableSources">getReachableSources</a></li><li><a href="#request_analysis.reanalyze">reanalyze</a></li><li><a href="#request_analysis.setAnalysisRoots">setAnalysisRoots</a></li><li><a href="#request_analysis.setGeneralSubscriptions">setGeneralSubscriptions</a></li><li><a href="#request_analysis.setPriorityFiles">setPriorityFiles</a></li><li><a href="#request_analysis.setSubscriptions">setSubscriptions</a></li><li><a href="#request_analysis.updateContent">updateContent</a></li><li><a href="#request_analysis.updateOptions">updateOptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_analysis.analyzedFiles">analyzedFiles</a></li><li><a href="#notification_analysis.closingLabels">closingLabels</a></li><li><a href="#notification_analysis.errors">errors</a></li><li><a href="#notification_analysis.flushResults">flushResults</a></li><li><a href="#notification_analysis.folding">folding</a></li><li><a href="#notification_analysis.highlights">highlights</a></li><li><a href="#notification_analysis.implemented">implemented</a></li><li><a href="#notification_analysis.invalidate">invalidate</a></li><li><a href="#notification_analysis.navigation">navigation</a></li><li><a href="#notification_analysis.occurrences">occurrences</a></li><li><a href="#notification_analysis.outline">outline</a></li><li><a href="#notification_analysis.overrides">overrides</a></li></ul></div></div><h4>completion (<a href="#domain_completion">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_completion.getSuggestions">getSuggestions</a></li><li><a href="#request_completion.setSubscriptions">setSubscriptions</a></li><li><a href="#request_completion.registerLibraryPaths">registerLibraryPaths</a></li><li><a href="#request_completion.getSuggestionDetails">getSuggestionDetails</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_completion.results">results</a></li><li><a href="#notification_completion.availableSuggestions">availableSuggestions</a></li><li><a href="#notification_completion.existingImports">existingImports</a></li></ul></div></div><h4>search (<a href="#domain_search">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_search.findElementReferences">findElementReferences</a></li><li><a href="#request_search.findMemberDeclarations">findMemberDeclarations</a></li><li><a href="#request_search.findMemberReferences">findMemberReferences</a></li><li><a href="#request_search.findTopLevelDeclarations">findTopLevelDeclarations</a></li><li><a href="#request_search.getTypeHierarchy">getTypeHierarchy</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_search.results">results</a></li></ul></div></div><h4>edit (<a href="#domain_edit">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_edit.format">format</a></li><li><a href="#request_edit.getAssists">getAssists</a></li><li><a href="#request_edit.getAvailableRefactorings">getAvailableRefactorings</a></li><li><a href="#request_edit.getFixes">getFixes</a></li><li><a href="#request_edit.getPostfixCompletion">getPostfixCompletion</a></li><li><a href="#request_edit.getRefactoring">getRefactoring</a></li><li><a href="#request_edit.sortMembers">sortMembers</a></li><li><a href="#request_edit.organizeDirectives">organizeDirectives</a></li></ul></div><h4>execution (<a href="#domain_execution">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_execution.createContext">createContext</a></li><li><a href="#request_execution.deleteContext">deleteContext</a></li><li><a href="#request_execution.getSuggestions">getSuggestions</a></li><li><a href="#request_execution.mapUri">mapUri</a></li><li><a href="#request_execution.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_execution.launchData">launchData</a></li></ul></div></div><h4>diagnostic (<a href="#domain_diagnostic">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_diagnostic.getDiagnostics">getDiagnostics</a></li><li><a href="#request_diagnostic.getServerPort">getServerPort</a></li></ul></div><h4>flutter (<a href="#domain_flutter">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_flutter.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_flutter.outline">outline</a></li></ul></div></div><h3>Types (<a href="#types">↑</a>)</h3><div class="subindex"><ul><li><a href="#type_AddContentOverlay">AddContentOverlay</a></li><li><a href="#type_AnalysisError">AnalysisError</a></li><li><a href="#type_AnalysisErrorFixes">AnalysisErrorFixes</a></li><li><a href="#type_AnalysisErrorSeverity">AnalysisErrorSeverity</a></li><li><a href="#type_AnalysisErrorType">AnalysisErrorType</a></li><li><a href="#type_AnalysisOptions">AnalysisOptions</a></li><li><a href="#type_AnalysisService">AnalysisService</a></li><li><a href="#type_AnalysisStatus">AnalysisStatus</a></li><li><a href="#type_AvailableSuggestion">AvailableSuggestion</a></li><li><a href="#type_AvailableSuggestionRelevanceTag">AvailableSuggestionRelevanceTag</a></li><li><a href="#type_AvailableSuggestionSet">AvailableSuggestionSet</a></li><li><a href="#type_BulkFix">BulkFix</a></li><li><a href="#type_BulkFixDetail">BulkFixDetail</a></li><li><a href="#type_ChangeContentOverlay">ChangeContentOverlay</a></li><li><a href="#type_ClosingLabel">ClosingLabel</a></li><li><a href="#type_CompletionCaseMatchingMode">CompletionCaseMatchingMode</a></li><li><a href="#type_CompletionId">CompletionId</a></li><li><a href="#type_CompletionMode">CompletionMode</a></li><li><a href="#type_CompletionService">CompletionService</a></li><li><a href="#type_CompletionSuggestion">CompletionSuggestion</a></li><li><a href="#type_CompletionSuggestionKind">CompletionSuggestionKind</a></li><li><a href="#type_ContextData">ContextData</a></li><li><a href="#type_DiagnosticMessage">DiagnosticMessage</a></li><li><a href="#type_Element">Element</a></li><li><a href="#type_ElementDeclaration">ElementDeclaration</a></li><li><a href="#type_ElementKind">ElementKind</a></li><li><a href="#type_ExecutableFile">ExecutableFile</a></li><li><a href="#type_ExecutableKind">ExecutableKind</a></li><li><a href="#type_ExecutionContextId">ExecutionContextId</a></li><li><a href="#type_ExecutionService">ExecutionService</a></li><li><a href="#type_ExistingImport">ExistingImport</a></li><li><a href="#type_ExistingImports">ExistingImports</a></li><li><a href="#type_FileKind">FileKind</a></li><li><a href="#type_FilePath">FilePath</a></li><li><a href="#type_FlutterOutline">FlutterOutline</a></li><li><a href="#type_FlutterOutlineAttribute">FlutterOutlineAttribute</a></li><li><a href="#type_FlutterOutlineKind">FlutterOutlineKind</a></li><li><a href="#type_FlutterService">FlutterService</a></li><li><a href="#type_FlutterWidgetProperty">FlutterWidgetProperty</a></li><li><a href="#type_FlutterWidgetPropertyEditor">FlutterWidgetPropertyEditor</a></li><li><a href="#type_FlutterWidgetPropertyEditorKind">FlutterWidgetPropertyEditorKind</a></li><li><a href="#type_FlutterWidgetPropertyValue">FlutterWidgetPropertyValue</a></li><li><a href="#type_FlutterWidgetPropertyValueEnumItem">FlutterWidgetPropertyValueEnumItem</a></li><li><a href="#type_FoldingKind">FoldingKind</a></li><li><a href="#type_FoldingRegion">FoldingRegion</a></li><li><a href="#type_GeneralAnalysisService">GeneralAnalysisService</a></li><li><a href="#type_HighlightRegion">HighlightRegion</a></li><li><a href="#type_HighlightRegionType">HighlightRegionType</a></li><li><a href="#type_HoverInformation">HoverInformation</a></li><li><a href="#type_ImplementedClass">ImplementedClass</a></li><li><a href="#type_ImplementedMember">ImplementedMember</a></li><li><a href="#type_ImportedElementSet">ImportedElementSet</a></li><li><a href="#type_ImportedElements">ImportedElements</a></li><li><a href="#type_IncludedSuggestionRelevanceTag">IncludedSuggestionRelevanceTag</a></li><li><a href="#type_IncludedSuggestionSet">IncludedSuggestionSet</a></li><li><a href="#type_KytheEntry">KytheEntry</a></li><li><a href="#type_KytheVName">KytheVName</a></li><li><a href="#type_LibraryPathSet">LibraryPathSet</a></li><li><a href="#type_LinkedEditGroup">LinkedEditGroup</a></li><li><a href="#type_LinkedEditSuggestion">LinkedEditSuggestion</a></li><li><a href="#type_LinkedEditSuggestionKind">LinkedEditSuggestionKind</a></li><li><a href="#type_Location">Location</a></li><li><a href="#type_NavigationRegion">NavigationRegion</a></li><li><a href="#type_NavigationTarget">NavigationTarget</a></li><li><a href="#type_Occurrences">Occurrences</a></li><li><a href="#type_Outline">Outline</a></li><li><a href="#type_OverriddenMember">OverriddenMember</a></li><li><a href="#type_Override">Override</a></li><li><a href="#type_Position">Position</a></li><li><a href="#type_PostfixTemplateDescriptor">PostfixTemplateDescriptor</a></li><li><a href="#type_PubStatus">PubStatus</a></li><li><a href="#type_RefactoringFeedback">RefactoringFeedback</a></li><li><a href="#type_RefactoringKind">RefactoringKind</a></li><li><a href="#type_RefactoringMethodParameter">RefactoringMethodParameter</a></li><li><a href="#type_RefactoringMethodParameterKind">RefactoringMethodParameterKind</a></li><li><a href="#type_RefactoringOptions">RefactoringOptions</a></li><li><a href="#type_RefactoringProblem">RefactoringProblem</a></li><li><a href="#type_RefactoringProblemSeverity">RefactoringProblemSeverity</a></li><li><a href="#type_RemoveContentOverlay">RemoveContentOverlay</a></li><li><a href="#type_RequestError">RequestError</a></li><li><a href="#type_RequestErrorCode">RequestErrorCode</a></li><li><a href="#type_RuntimeCompletionExpression">RuntimeCompletionExpression</a></li><li><a href="#type_RuntimeCompletionExpressionType">RuntimeCompletionExpressionType</a></li><li><a href="#type_RuntimeCompletionExpressionTypeKind">RuntimeCompletionExpressionTypeKind</a></li><li><a href="#type_RuntimeCompletionVariable">RuntimeCompletionVariable</a></li><li><a href="#type_SearchId">SearchId</a></li><li><a href="#type_SearchResult">SearchResult</a></li><li><a href="#type_SearchResultKind">SearchResultKind</a></li><li><a href="#type_ServerService">ServerService</a></li><li><a href="#type_SourceChange">SourceChange</a></li><li><a href="#type_SourceEdit">SourceEdit</a></li><li><a href="#type_SourceFileEdit">SourceFileEdit</a></li><li><a href="#type_TypeHierarchyItem">TypeHierarchyItem</a></li></ul></div><h3>Refactorings (<a href="#refactorings">↑</a>)</h3><div class="subindex"><ul><li><a href="#refactoring_CONVERT_GETTER_TO_METHOD">CONVERT_GETTER_TO_METHOD</a></li><li><a href="#refactoring_CONVERT_METHOD_TO_GETTER">CONVERT_METHOD_TO_GETTER</a></li><li><a href="#refactoring_EXTRACT_LOCAL_VARIABLE">EXTRACT_LOCAL_VARIABLE</a></li><li><a href="#refactoring_EXTRACT_METHOD">EXTRACT_METHOD</a></li><li><a href="#refactoring_EXTRACT_WIDGET">EXTRACT_WIDGET</a></li><li><a href="#refactoring_INLINE_LOCAL_VARIABLE">INLINE_LOCAL_VARIABLE</a></li><li><a href="#refactoring_INLINE_METHOD">INLINE_METHOD</a></li><li><a href="#refactoring_MOVE_FILE">MOVE_FILE</a></li><li><a href="#refactoring_RENAME">RENAME</a></li></ul></div>
+<h3>Domains</h3><h4>server (<a href="#domain_server">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_server.getVersion">getVersion</a></li><li><a href="#request_server.shutdown">shutdown</a></li><li><a href="#request_server.setSubscriptions">setSubscriptions</a></li><li><a href="#request_server.cancelRequest">cancelRequest</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_server.connected">connected</a></li><li><a href="#notification_server.error">error</a></li><li><a href="#notification_server.log">log</a></li><li><a href="#notification_server.status">status</a></li></ul></div></div><h4>analysis (<a href="#domain_analysis">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_analysis.getErrors">getErrors</a></li><li><a href="#request_analysis.getHover">getHover</a></li><li><a href="#request_analysis.getLibraryDependencies">getLibraryDependencies</a></li><li><a href="#request_analysis.getNavigation">getNavigation</a></li><li><a href="#request_analysis.getReachableSources">getReachableSources</a></li><li><a href="#request_analysis.reanalyze">reanalyze</a></li><li><a href="#request_analysis.setAnalysisRoots">setAnalysisRoots</a></li><li><a href="#request_analysis.setGeneralSubscriptions">setGeneralSubscriptions</a></li><li><a href="#request_analysis.setPriorityFiles">setPriorityFiles</a></li><li><a href="#request_analysis.setSubscriptions">setSubscriptions</a></li><li><a href="#request_analysis.updateContent">updateContent</a></li><li><a href="#request_analysis.updateOptions">updateOptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_analysis.analyzedFiles">analyzedFiles</a></li><li><a href="#notification_analysis.closingLabels">closingLabels</a></li><li><a href="#notification_analysis.errors">errors</a></li><li><a href="#notification_analysis.flushResults">flushResults</a></li><li><a href="#notification_analysis.folding">folding</a></li><li><a href="#notification_analysis.highlights">highlights</a></li><li><a href="#notification_analysis.implemented">implemented</a></li><li><a href="#notification_analysis.invalidate">invalidate</a></li><li><a href="#notification_analysis.navigation">navigation</a></li><li><a href="#notification_analysis.occurrences">occurrences</a></li><li><a href="#notification_analysis.outline">outline</a></li><li><a href="#notification_analysis.overrides">overrides</a></li></ul></div></div><h4>completion (<a href="#domain_completion">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_completion.getSuggestions">getSuggestions</a></li><li><a href="#request_completion.getSuggestions2">getSuggestions2</a></li><li><a href="#request_completion.setSubscriptions">setSubscriptions</a></li><li><a href="#request_completion.registerLibraryPaths">registerLibraryPaths</a></li><li><a href="#request_completion.getSuggestionDetails">getSuggestionDetails</a></li><li><a href="#request_completion.getSuggestionDetails2">getSuggestionDetails2</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_completion.results">results</a></li><li><a href="#notification_completion.availableSuggestions">availableSuggestions</a></li><li><a href="#notification_completion.existingImports">existingImports</a></li></ul></div></div><h4>search (<a href="#domain_search">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_search.findElementReferences">findElementReferences</a></li><li><a href="#request_search.findMemberDeclarations">findMemberDeclarations</a></li><li><a href="#request_search.findMemberReferences">findMemberReferences</a></li><li><a href="#request_search.findTopLevelDeclarations">findTopLevelDeclarations</a></li><li><a href="#request_search.getTypeHierarchy">getTypeHierarchy</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_search.results">results</a></li></ul></div></div><h4>edit (<a href="#domain_edit">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_edit.format">format</a></li><li><a href="#request_edit.getAssists">getAssists</a></li><li><a href="#request_edit.getAvailableRefactorings">getAvailableRefactorings</a></li><li><a href="#request_edit.getFixes">getFixes</a></li><li><a href="#request_edit.getPostfixCompletion">getPostfixCompletion</a></li><li><a href="#request_edit.getRefactoring">getRefactoring</a></li><li><a href="#request_edit.sortMembers">sortMembers</a></li><li><a href="#request_edit.organizeDirectives">organizeDirectives</a></li></ul></div><h4>execution (<a href="#domain_execution">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_execution.createContext">createContext</a></li><li><a href="#request_execution.deleteContext">deleteContext</a></li><li><a href="#request_execution.getSuggestions">getSuggestions</a></li><li><a href="#request_execution.mapUri">mapUri</a></li><li><a href="#request_execution.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_execution.launchData">launchData</a></li></ul></div></div><h4>diagnostic (<a href="#domain_diagnostic">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_diagnostic.getDiagnostics">getDiagnostics</a></li><li><a href="#request_diagnostic.getServerPort">getServerPort</a></li></ul></div><h4>flutter (<a href="#domain_flutter">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_flutter.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_flutter.outline">outline</a></li></ul></div></div><h3>Types (<a href="#types">↑</a>)</h3><div class="subindex"><ul><li><a href="#type_AddContentOverlay">AddContentOverlay</a></li><li><a href="#type_AnalysisError">AnalysisError</a></li><li><a href="#type_AnalysisErrorFixes">AnalysisErrorFixes</a></li><li><a href="#type_AnalysisErrorSeverity">AnalysisErrorSeverity</a></li><li><a href="#type_AnalysisErrorType">AnalysisErrorType</a></li><li><a href="#type_AnalysisOptions">AnalysisOptions</a></li><li><a href="#type_AnalysisService">AnalysisService</a></li><li><a href="#type_AnalysisStatus">AnalysisStatus</a></li><li><a href="#type_AvailableSuggestion">AvailableSuggestion</a></li><li><a href="#type_AvailableSuggestionRelevanceTag">AvailableSuggestionRelevanceTag</a></li><li><a href="#type_AvailableSuggestionSet">AvailableSuggestionSet</a></li><li><a href="#type_BulkFix">BulkFix</a></li><li><a href="#type_BulkFixDetail">BulkFixDetail</a></li><li><a href="#type_ChangeContentOverlay">ChangeContentOverlay</a></li><li><a href="#type_ClosingLabel">ClosingLabel</a></li><li><a href="#type_CompletionCaseMatchingMode">CompletionCaseMatchingMode</a></li><li><a href="#type_CompletionId">CompletionId</a></li><li><a href="#type_CompletionMode">CompletionMode</a></li><li><a href="#type_CompletionService">CompletionService</a></li><li><a href="#type_CompletionSuggestion">CompletionSuggestion</a></li><li><a href="#type_CompletionSuggestionKind">CompletionSuggestionKind</a></li><li><a href="#type_ContextData">ContextData</a></li><li><a href="#type_DiagnosticMessage">DiagnosticMessage</a></li><li><a href="#type_Element">Element</a></li><li><a href="#type_ElementDeclaration">ElementDeclaration</a></li><li><a href="#type_ElementKind">ElementKind</a></li><li><a href="#type_ExecutableFile">ExecutableFile</a></li><li><a href="#type_ExecutableKind">ExecutableKind</a></li><li><a href="#type_ExecutionContextId">ExecutionContextId</a></li><li><a href="#type_ExecutionService">ExecutionService</a></li><li><a href="#type_ExistingImport">ExistingImport</a></li><li><a href="#type_ExistingImports">ExistingImports</a></li><li><a href="#type_FileKind">FileKind</a></li><li><a href="#type_FilePath">FilePath</a></li><li><a href="#type_FlutterOutline">FlutterOutline</a></li><li><a href="#type_FlutterOutlineAttribute">FlutterOutlineAttribute</a></li><li><a href="#type_FlutterOutlineKind">FlutterOutlineKind</a></li><li><a href="#type_FlutterService">FlutterService</a></li><li><a href="#type_FlutterWidgetProperty">FlutterWidgetProperty</a></li><li><a href="#type_FlutterWidgetPropertyEditor">FlutterWidgetPropertyEditor</a></li><li><a href="#type_FlutterWidgetPropertyEditorKind">FlutterWidgetPropertyEditorKind</a></li><li><a href="#type_FlutterWidgetPropertyValue">FlutterWidgetPropertyValue</a></li><li><a href="#type_FlutterWidgetPropertyValueEnumItem">FlutterWidgetPropertyValueEnumItem</a></li><li><a href="#type_FoldingKind">FoldingKind</a></li><li><a href="#type_FoldingRegion">FoldingRegion</a></li><li><a href="#type_GeneralAnalysisService">GeneralAnalysisService</a></li><li><a href="#type_HighlightRegion">HighlightRegion</a></li><li><a href="#type_HighlightRegionType">HighlightRegionType</a></li><li><a href="#type_HoverInformation">HoverInformation</a></li><li><a href="#type_ImplementedClass">ImplementedClass</a></li><li><a href="#type_ImplementedMember">ImplementedMember</a></li><li><a href="#type_ImportedElementSet">ImportedElementSet</a></li><li><a href="#type_ImportedElements">ImportedElements</a></li><li><a href="#type_IncludedSuggestionRelevanceTag">IncludedSuggestionRelevanceTag</a></li><li><a href="#type_IncludedSuggestionSet">IncludedSuggestionSet</a></li><li><a href="#type_KytheEntry">KytheEntry</a></li><li><a href="#type_KytheVName">KytheVName</a></li><li><a href="#type_LibraryPathSet">LibraryPathSet</a></li><li><a href="#type_LinkedEditGroup">LinkedEditGroup</a></li><li><a href="#type_LinkedEditSuggestion">LinkedEditSuggestion</a></li><li><a href="#type_LinkedEditSuggestionKind">LinkedEditSuggestionKind</a></li><li><a href="#type_Location">Location</a></li><li><a href="#type_NavigationRegion">NavigationRegion</a></li><li><a href="#type_NavigationTarget">NavigationTarget</a></li><li><a href="#type_Occurrences">Occurrences</a></li><li><a href="#type_Outline">Outline</a></li><li><a href="#type_OverriddenMember">OverriddenMember</a></li><li><a href="#type_Override">Override</a></li><li><a href="#type_Position">Position</a></li><li><a href="#type_PostfixTemplateDescriptor">PostfixTemplateDescriptor</a></li><li><a href="#type_PubStatus">PubStatus</a></li><li><a href="#type_RefactoringFeedback">RefactoringFeedback</a></li><li><a href="#type_RefactoringKind">RefactoringKind</a></li><li><a href="#type_RefactoringMethodParameter">RefactoringMethodParameter</a></li><li><a href="#type_RefactoringMethodParameterKind">RefactoringMethodParameterKind</a></li><li><a href="#type_RefactoringOptions">RefactoringOptions</a></li><li><a href="#type_RefactoringProblem">RefactoringProblem</a></li><li><a href="#type_RefactoringProblemSeverity">RefactoringProblemSeverity</a></li><li><a href="#type_RemoveContentOverlay">RemoveContentOverlay</a></li><li><a href="#type_RequestError">RequestError</a></li><li><a href="#type_RequestErrorCode">RequestErrorCode</a></li><li><a href="#type_RuntimeCompletionExpression">RuntimeCompletionExpression</a></li><li><a href="#type_RuntimeCompletionExpressionType">RuntimeCompletionExpressionType</a></li><li><a href="#type_RuntimeCompletionExpressionTypeKind">RuntimeCompletionExpressionTypeKind</a></li><li><a href="#type_RuntimeCompletionVariable">RuntimeCompletionVariable</a></li><li><a href="#type_SearchId">SearchId</a></li><li><a href="#type_SearchResult">SearchResult</a></li><li><a href="#type_SearchResultKind">SearchResultKind</a></li><li><a href="#type_ServerService">ServerService</a></li><li><a href="#type_SourceChange">SourceChange</a></li><li><a href="#type_SourceEdit">SourceEdit</a></li><li><a href="#type_SourceFileEdit">SourceFileEdit</a></li><li><a href="#type_TypeHierarchyItem">TypeHierarchyItem</a></li></ul></div><h3>Refactorings (<a href="#refactorings">↑</a>)</h3><div class="subindex"><ul><li><a href="#refactoring_CONVERT_GETTER_TO_METHOD">CONVERT_GETTER_TO_METHOD</a></li><li><a href="#refactoring_CONVERT_METHOD_TO_GETTER">CONVERT_METHOD_TO_GETTER</a></li><li><a href="#refactoring_EXTRACT_LOCAL_VARIABLE">EXTRACT_LOCAL_VARIABLE</a></li><li><a href="#refactoring_EXTRACT_METHOD">EXTRACT_METHOD</a></li><li><a href="#refactoring_EXTRACT_WIDGET">EXTRACT_WIDGET</a></li><li><a href="#refactoring_INLINE_LOCAL_VARIABLE">INLINE_LOCAL_VARIABLE</a></li><li><a href="#refactoring_INLINE_METHOD">INLINE_METHOD</a></li><li><a href="#refactoring_MOVE_FILE">MOVE_FILE</a></li><li><a href="#refactoring_RENAME">RENAME</a></li></ul></div>
 
 
 </body></html>
\ No newline at end of file
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
index 7d095d7..8475456 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
@@ -1605,3 +1605,81 @@
   @override
   String toString() => jsonEncoder.convert(toJson());
 }
+
+class ValidateRefactorResult implements ToJsonable {
+  static const jsonHandler = LspJsonHandler(
+      ValidateRefactorResult.canParse, ValidateRefactorResult.fromJson);
+
+  ValidateRefactorResult({required this.valid, this.message});
+  static ValidateRefactorResult fromJson(Map<String, Object?> json) {
+    final validJson = json['valid'];
+    final valid = validJson as bool;
+    final messageJson = json['message'];
+    final message = messageJson as String?;
+    return ValidateRefactorResult(valid: valid, message: message);
+  }
+
+  final String? message;
+  final bool valid;
+
+  Map<String, Object?> toJson() {
+    var __result = <String, Object?>{};
+    __result['valid'] = valid;
+    if (message != null) {
+      __result['message'] = message;
+    }
+    return __result;
+  }
+
+  static bool canParse(Object? obj, LspJsonReporter reporter) {
+    if (obj is Map<String, Object?>) {
+      reporter.push('valid');
+      try {
+        if (!obj.containsKey('valid')) {
+          reporter.reportError('must not be undefined');
+          return false;
+        }
+        final valid = obj['valid'];
+        if (valid == null) {
+          reporter.reportError('must not be null');
+          return false;
+        }
+        if (!(valid is bool)) {
+          reporter.reportError('must be of type bool');
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('message');
+      try {
+        final message = obj['message'];
+        if (message != null && !(message is String)) {
+          reporter.reportError('must be of type String');
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError('must be of type ValidateRefactorResult');
+      return false;
+    }
+  }
+
+  @override
+  bool operator ==(Object other) {
+    if (other is ValidateRefactorResult &&
+        other.runtimeType == ValidateRefactorResult) {
+      return valid == other.valid && message == other.message && true;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode => Object.hash(valid, message);
+
+  @override
+  String toString() => jsonEncoder.convert(toJson());
+}
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
index 65c9513..6d5b0d8 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
@@ -65,10 +65,10 @@
   final T1? _t1;
   final T2? _t2;
 
-  Either2.t1(T1 this._t1)
+  const Either2.t1(T1 this._t1)
       : _t2 = null,
         _which = 1;
-  Either2.t2(T2 this._t2)
+  const Either2.t2(T2 this._t2)
       : _t1 = null,
         _which = 2;
 
diff --git a/pkg/analysis_server/lib/protocol/protocol.dart b/pkg/analysis_server/lib/protocol/protocol.dart
index 3481a4c..c1d6da7 100644
--- a/pkg/analysis_server/lib/protocol/protocol.dart
+++ b/pkg/analysis_server/lib/protocol/protocol.dart
@@ -310,6 +310,13 @@
   /// then the response will represent an error condition.
   Response(this.id, {this.result, this.error});
 
+  /// Initialize a newly created instance to represent the CONTENT_MODIFIED
+  /// error condition.
+  Response.contentModified(Request request)
+      : this(request.id,
+            error: RequestError(RequestErrorCode.CONTENT_MODIFIED,
+                'File was modified before the operation completed.'));
+
   /// Create and return the `DEBUG_PORT_COULD_NOT_BE_OPENED` error response.
   Response.debugPortCouldNotBeOpened(Request request, dynamic error)
       : this(request.id,
diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart
index e8a4e7e..24ad02a 100644
--- a/pkg/analysis_server/lib/protocol/protocol_constants.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart
@@ -6,7 +6,7 @@
 // To regenerate the file, use the script
 // "pkg/analysis_server/tool/spec/generate_files".
 
-const String PROTOCOL_VERSION = '1.32.10';
+const String PROTOCOL_VERSION = '1.33.0';
 
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
@@ -194,6 +194,8 @@
 const String EDIT_REQUEST_BULK_FIXES_IN_TEST_MODE = 'inTestMode';
 const String EDIT_REQUEST_FORMAT = 'edit.format';
 const String EDIT_REQUEST_FORMAT_FILE = 'file';
+const String EDIT_REQUEST_FORMAT_IF_ENABLED = 'edit.formatIfEnabled';
+const String EDIT_REQUEST_FORMAT_IF_ENABLED_DIRECTORIES = 'directories';
 const String EDIT_REQUEST_FORMAT_LINE_LENGTH = 'lineLength';
 const String EDIT_REQUEST_FORMAT_SELECTION_LENGTH = 'selectionLength';
 const String EDIT_REQUEST_FORMAT_SELECTION_OFFSET = 'selectionOffset';
@@ -242,6 +244,7 @@
 const String EDIT_RESPONSE_BULK_FIXES_DETAILS = 'details';
 const String EDIT_RESPONSE_BULK_FIXES_EDITS = 'edits';
 const String EDIT_RESPONSE_FORMAT_EDITS = 'edits';
+const String EDIT_RESPONSE_FORMAT_IF_ENABLED_EDITS = 'edits';
 const String EDIT_RESPONSE_FORMAT_SELECTION_LENGTH = 'selectionLength';
 const String EDIT_RESPONSE_FORMAT_SELECTION_OFFSET = 'selectionOffset';
 const String EDIT_RESPONSE_GET_ASSISTS_ASSISTS = 'assists';
diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart
index dc2c12a..37339ee 100644
--- a/pkg/analysis_server/lib/protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart
@@ -6258,6 +6258,140 @@
       );
 }
 
+/// edit.formatIfEnabled params
+///
+/// {
+///   "directories": List<FilePath>
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class EditFormatIfEnabledParams implements RequestParams {
+  /// The paths of the directories containing the code to be formatted.
+  List<String> directories;
+
+  EditFormatIfEnabledParams(this.directories);
+
+  factory EditFormatIfEnabledParams.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+    json ??= {};
+    if (json is Map) {
+      List<String> directories;
+      if (json.containsKey('directories')) {
+        directories = jsonDecoder.decodeList(jsonPath + '.directories',
+            json['directories'], jsonDecoder.decodeString);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, 'directories');
+      }
+      return EditFormatIfEnabledParams(directories);
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, 'edit.formatIfEnabled params', json);
+    }
+  }
+
+  factory EditFormatIfEnabledParams.fromRequest(Request request) {
+    return EditFormatIfEnabledParams.fromJson(
+        RequestDecoder(request), 'params', request.params);
+  }
+
+  @override
+  Map<String, Object> toJson() {
+    var result = <String, Object>{};
+    result['directories'] = directories;
+    return result;
+  }
+
+  @override
+  Request toRequest(String id) {
+    return Request(id, 'edit.formatIfEnabled', toJson());
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is EditFormatIfEnabledParams) {
+      return listEqual(
+          directories, other.directories, (String a, String b) => a == b);
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode => directories.hashCode;
+}
+
+/// edit.formatIfEnabled result
+///
+/// {
+///   "edits": List<SourceFileEdit>
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class EditFormatIfEnabledResult implements ResponseResult {
+  /// The edit(s) to be applied in order to format the code. The list will be
+  /// empty if none of the files were formatted, whether because they were not
+  /// eligible to be formatted or because they were already formatted.
+  List<SourceFileEdit> edits;
+
+  EditFormatIfEnabledResult(this.edits);
+
+  factory EditFormatIfEnabledResult.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+    json ??= {};
+    if (json is Map) {
+      List<SourceFileEdit> edits;
+      if (json.containsKey('edits')) {
+        edits = jsonDecoder.decodeList(
+            jsonPath + '.edits',
+            json['edits'],
+            (String jsonPath, Object? json) =>
+                SourceFileEdit.fromJson(jsonDecoder, jsonPath, json));
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, 'edits');
+      }
+      return EditFormatIfEnabledResult(edits);
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, 'edit.formatIfEnabled result', json);
+    }
+  }
+
+  factory EditFormatIfEnabledResult.fromResponse(Response response) {
+    return EditFormatIfEnabledResult.fromJson(
+        ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)),
+        'result',
+        response.result);
+  }
+
+  @override
+  Map<String, Object> toJson() {
+    var result = <String, Object>{};
+    result['edits'] =
+        edits.map((SourceFileEdit value) => value.toJson()).toList();
+    return result;
+  }
+
+  @override
+  Response toResponse(String id) {
+    return Response(id, result: toJson());
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is EditFormatIfEnabledResult) {
+      return listEqual(
+          edits, other.edits, (SourceFileEdit a, SourceFileEdit b) => a == b);
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode => edits.hashCode;
+}
+
 /// edit.format params
 ///
 /// {
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 12ae415..2aec83b 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -44,9 +44,11 @@
 import 'package:analysis_server/src/utilities/progress.dart';
 import 'package:analysis_server/src/utilities/request_statistics.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/exception/exception.dart';
 import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/overlay_file_system.dart';
 import 'package:analyzer/instrumentation/instrumentation.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart' as analysis;
 import 'package:analyzer/src/dart/analysis/status.dart' as analysis;
@@ -123,6 +125,34 @@
   final StreamController _onAnalysisSetChangedController =
       StreamController.broadcast(sync: true);
 
+  /// Key: a file path for which removing of the overlay was requested.
+  /// Value: a timer that will remove the overlay, or cancelled.
+  ///
+  /// This helps for analysis server running remotely, with slow remote file
+  /// systems, in the following scenario:
+  /// 1. User edits file, IDE sends "add overlay".
+  /// 2. User saves file, IDE saves file locally, sends "remove overlay".
+  /// 3. The remove server reads the file on "remove overlay". But the content
+  ///    of the file on the remove machine is not the same as it is locally,
+  ///    and not what the user looks in the IDE. So, analysis results, such
+  ///    as semantic highlighting, are inconsistent with the local file content.
+  /// 4. (after a few seconds) The file is synced to the remove machine,
+  ///    the watch event happens, server reads the file, sends analysis
+  ///    results that are consistent with the local file content.
+  ///
+  /// We try to prevent the inconsistency between moments (3) and (4).
+  /// It is not wrong, we are still in the eventual consistency, but we
+  /// want to keep the inconsistency time shorter.
+  ///
+  /// To do this we keep the last overlay content on "remove overlay",
+  /// and wait for the next watch event in (4). But there might be race
+  /// condition, and when it happens, we still want to get to the eventual
+  /// consistency, so on timer we remove the overlay anyway.
+  final Map<String, Timer> _pendingFilesToRemoveOverlay = {};
+
+  @visibleForTesting
+  Duration pendingFilesRemoveOverlayDelay = const Duration(seconds: 10);
+
   final DetachableFileSystemManager? detachableFileSystemManager;
 
   /// The broadcast stream of requests that were discarded because there
@@ -231,6 +261,12 @@
     cancellationTokens[id]?.cancel();
   }
 
+  Future<void> dispose() async {
+    for (var timer in _pendingFilesToRemoveOverlay.values) {
+      timer.cancel();
+    }
+  }
+
   /// The socket from which requests are being read has been closed.
   void done() {}
 
@@ -266,6 +302,9 @@
             sendResponse(response);
             return;
           }
+        } on InconsistentAnalysisException {
+          sendResponse(Response.contentModified(request));
+          return;
         } on RequestFailure catch (exception) {
           sendResponse(exception.response);
           return;
@@ -511,7 +550,7 @@
       } catch (_) {}
 
       // Prepare the new contents.
-      String? newContents;
+      String newContents;
       if (change is AddContentOverlay) {
         newContents = change.content;
       } else if (change is ChangeContentOverlay) {
@@ -530,25 +569,29 @@
                   'Invalid overlay change')));
         }
       } else if (change is RemoveContentOverlay) {
-        newContents = null;
+        _pendingFilesToRemoveOverlay.remove(file)?.cancel();
+        _pendingFilesToRemoveOverlay[file] = Timer(
+          pendingFilesRemoveOverlayDelay,
+          () {
+            _pendingFilesToRemoveOverlay.remove(file);
+            resourceProvider.removeOverlay(file);
+            _changeFileInDrivers(file);
+          },
+        );
+        return;
       } else {
         // Protocol parsing should have ensured that we never get here.
         throw AnalysisException('Illegal change type');
       }
 
-      if (newContents != null) {
-        resourceProvider.setOverlay(
-          file,
-          content: newContents,
-          modificationStamp: overlayModificationStamp++,
-        );
-      } else {
-        resourceProvider.removeOverlay(file);
-      }
+      _pendingFilesToRemoveOverlay.remove(file)?.cancel();
+      resourceProvider.setOverlay(
+        file,
+        content: newContents,
+        modificationStamp: overlayModificationStamp++,
+      );
 
-      driverMap.values.forEach((driver) {
-        driver.changeFile(file);
-      });
+      _changeFileInDrivers(file);
 
       // If the file did not exist, and is "overlay only", it still should be
       // analyzed. Add it to driver to which it should have been added.
@@ -586,6 +629,12 @@
 //    });
   }
 
+  void _changeFileInDrivers(String path) {
+    for (var driver in driverMap.values) {
+      driver.changeFile(path);
+    }
+  }
+
   /// Returns `true` if there is a subscription for the given [service] and
   /// [file].
   bool _hasAnalysisServiceSubscription(AnalysisService service, String file) {
@@ -671,7 +720,7 @@
   final AnalysisServer analysisServer;
 
   /// The [ResourceProvider] by which paths are converted into [Resource]s.
-  final ResourceProvider resourceProvider;
+  final OverlayResourceProvider resourceProvider;
 
   /// The set of files for which notifications were sent.
   final Set<String> filesToFlush = {};
@@ -698,6 +747,15 @@
 
   @override
   void afterWatchEvent(WatchEvent event) {
+    var path = event.path;
+
+    var pendingTimer = analysisServer._pendingFilesToRemoveOverlay.remove(path);
+    if (pendingTimer != null) {
+      pendingTimer.cancel();
+      resourceProvider.removeOverlay(path);
+      analysisServer._changeFileInDrivers(path);
+    }
+
     analysisServer._onAnalysisSetChangedController.add(null);
   }
 
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index b60182d..893de44 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -229,8 +229,13 @@
   }
 
   /// The list of current analysis sessions in all contexts.
-  List<AnalysisSession> get currentSessions {
-    return driverMap.values.map((driver) => driver.currentSession).toList();
+  Future<List<AnalysisSession>> get currentSessions async {
+    var sessions = <AnalysisSession>[];
+    for (var driver in driverMap.values) {
+      await driver.applyPendingFileChanges();
+      sessions.add(driver.currentSession);
+    }
+    return sessions;
   }
 
   /// A table mapping [Folder]s to the [AnalysisDriver]s associated with them.
@@ -297,6 +302,18 @@
     return null;
   }
 
+  /// Return an [AnalysisSession] in which the file with the given [path]
+  /// should be analyzed, preferring the one in which it is analyzed, then
+  /// the one where it is referenced, then the first, otherwise `null`.
+  Future<AnalysisSession?> getAnalysisSession(String path) async {
+    var analysisDriver = getAnalysisDriver(path);
+    if (analysisDriver != null) {
+      await analysisDriver.applyPendingFileChanges();
+      return analysisDriver.currentSession;
+    }
+    return null;
+  }
+
   DartdocDirectiveInfo getDartdocDirectiveInfoFor(ResolvedUnitResult result) {
     return getDartdocDirectiveInfoForSession(result.session);
   }
@@ -390,13 +407,20 @@
   }
 
   /// Return the unresolved unit for the file with the given [path].
-  ParsedUnitResult? getParsedUnit(String path) {
+  ///
+  /// Callers should handle [InconsistentAnalysisException] exceptions that may
+  /// occur if a file is modified during this operation.
+  Future<ParsedUnitResult?> getParsedUnit(String path) async {
     if (!file_paths.isDart(resourceProvider.pathContext, path)) {
       return null;
     }
 
-    var session = getAnalysisDriver(path)?.currentSession;
-    var result = session?.getParsedUnit(path);
+    var session = await getAnalysisSession(path);
+    if (session == null) {
+      return null;
+    }
+
+    var result = session.getParsedUnit(path);
     return result is ParsedUnitResult ? result : null;
   }
 
@@ -463,11 +487,11 @@
     await contextManager.refresh();
   }
 
-  ResolvedForCompletionResultImpl? resolveForCompletion({
+  Future<ResolvedForCompletionResultImpl?> resolveForCompletion({
     required String path,
     required int offset,
     required OperationPerformanceImpl performance,
-  }) {
+  }) async {
     if (!file_paths.isDart(resourceProvider.pathContext, path)) {
       return null;
     }
@@ -478,6 +502,7 @@
     }
 
     try {
+      await driver.applyPendingFileChanges();
       return driver.resolveForCompletion(
         path: path,
         offset: offset,
diff --git a/pkg/analysis_server/lib/src/cider/assists.dart b/pkg/analysis_server/lib/src/cider/assists.dart
index ba46cce..6b51f05 100644
--- a/pkg/analysis_server/lib/src/cider/assists.dart
+++ b/pkg/analysis_server/lib/src/cider/assists.dart
@@ -21,7 +21,7 @@
   Future<List<Assist>> compute(
       String path, int lineNumber, int colNumber, int length) async {
     var result = <Assist>[];
-    var resolvedUnit = _fileResolver.resolve(path: path);
+    var resolvedUnit = await _fileResolver.resolve2(path: path);
     var lineInfo = resolvedUnit.lineInfo;
     var offset = lineInfo.getOffsetOfLine(lineNumber) + colNumber;
 
diff --git a/pkg/analysis_server/lib/src/cider/completion.dart b/pkg/analysis_server/lib/src/cider/completion.dart
index 25a8bf9..86e6d59 100644
--- a/pkg/analysis_server/lib/src/cider/completion.dart
+++ b/pkg/analysis_server/lib/src/cider/completion.dart
@@ -54,14 +54,17 @@
     @visibleForTesting void Function(ResolvedUnitResult)? testResolvedUnit,
   }) async {
     return _performanceRoot.runAsync('completion', (performance) async {
-      var resolvedUnit = performance.run('resolution', (performance) {
-        return _fileResolver.resolve(
-          completionLine: line,
-          completionColumn: column,
-          path: path,
-          performance: performance,
-        );
-      });
+      var resolvedUnit = await performance.runAsync(
+        'resolution',
+        (performance) async {
+          return _fileResolver.resolve2(
+            completionLine: line,
+            completionColumn: column,
+            path: path,
+            performance: performance,
+          );
+        },
+      );
 
       if (testResolvedUnit != null) {
         testResolvedUnit(resolvedUnit);
diff --git a/pkg/analysis_server/lib/src/cider/document_symbols.dart b/pkg/analysis_server/lib/src/cider/document_symbols.dart
new file mode 100644
index 0000000..f8c7775
--- /dev/null
+++ b/pkg/analysis_server/lib/src/cider/document_symbols.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/src/computer/computer_outline.dart';
+import 'package:analysis_server/src/lsp/client_capabilities.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/dart/micro/resolve_file.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+
+class CiderDocumentSymbolsComputer {
+  final FileResolver _fileResolver;
+
+  CiderDocumentSymbolsComputer(this._fileResolver);
+
+  Future<List<DocumentSymbol>> compute2(String filePath) async {
+    var result = <DocumentSymbol>[];
+    var resolvedUnit = await _fileResolver.resolve2(path: filePath);
+
+    final computer = DartUnitOutlineComputer(resolvedUnit);
+    final outline = computer.compute();
+
+    final children = outline.children;
+    if (children == null) {
+      return result;
+    }
+
+    result.addAll(children.map((child) => _asDocumentSymbol(
+        LspClientCapabilities.defaultSupportedSymbolKinds,
+        resolvedUnit.lineInfo,
+        child)));
+
+    return result;
+  }
+
+  DocumentSymbol _asDocumentSymbol(
+    Set<SymbolKind> supportedKinds,
+    LineInfo lineInfo,
+    Outline outline,
+  ) {
+    final codeRange = toRange(lineInfo, outline.codeOffset, outline.codeLength);
+    final nameLocation = outline.element.location;
+    final nameRange = nameLocation != null
+        ? toRange(lineInfo, nameLocation.offset, nameLocation.length)
+        : null;
+    return DocumentSymbol(
+      name: toElementName(outline.element),
+      detail: outline.element.parameters,
+      kind: elementKindToSymbolKind(supportedKinds, outline.element.kind),
+      deprecated: outline.element.isDeprecated,
+      range: codeRange,
+      selectionRange: nameRange ?? codeRange,
+      children: outline.children
+          ?.map((child) => _asDocumentSymbol(supportedKinds, lineInfo, child))
+          .toList(),
+    );
+  }
+}
diff --git a/pkg/analysis_server/lib/src/cider/fixes.dart b/pkg/analysis_server/lib/src/cider/fixes.dart
index 6b65f24..fd9efd7 100644
--- a/pkg/analysis_server/lib/src/cider/fixes.dart
+++ b/pkg/analysis_server/lib/src/cider/fixes.dart
@@ -40,7 +40,7 @@
   /// Compute quick fixes for errors on the line with the [offset].
   Future<List<CiderErrorFixes>> compute(String path, int lineNumber) async {
     var result = <CiderErrorFixes>[];
-    var resolvedUnit = _fileResolver.resolve(path: path);
+    var resolvedUnit = await _fileResolver.resolve2(path: path);
 
     var lineInfo = resolvedUnit.lineInfo;
 
@@ -89,7 +89,7 @@
     var files = _fileResolver.getFilesWithTopLevelDeclarations(name);
     for (var file in files) {
       if (file.partOfLibrary == null) {
-        var libraryElement = _fileResolver.getLibraryByUri(
+        var libraryElement = await _fileResolver.getLibraryByUri2(
           uriStr: file.uriStr,
         );
         TopLevelDeclarations.addElement(result, libraryElement, name);
diff --git a/pkg/analysis_server/lib/src/cider/rename.dart b/pkg/analysis_server/lib/src/cider/rename.dart
index 44eadad..84ef2e3 100644
--- a/pkg/analysis_server/lib/src/cider/rename.dart
+++ b/pkg/analysis_server/lib/src/cider/rename.dart
@@ -39,6 +39,8 @@
       status = validateFunctionName(name);
     } else if (element is FieldElement) {
       status = validateFieldName(name);
+    } else if (element is MethodElement) {
+      status = validateMethodName(name);
     } else if (element is TypeAliasElement) {
       status = validateTypeAliasName(name);
     } else if (element is ClassElement) {
@@ -81,7 +83,7 @@
 
   String get oldName => canRename.refactoringElement.element.displayName;
 
-  RenameResponse? computeRenameRanges() {
+  Future<RenameResponse?> computeRenameRanges2() async {
     var elements = <Element>[];
     var element = canRename.refactoringElement.element;
     if (element is PropertyInducingElement && element.isSynthetic) {
@@ -93,14 +95,15 @@
     } else {
       elements.add(element);
     }
+    var fileResolver = canRename._fileResolver;
     var matches = <CiderSearchMatch>[];
     for (var element in elements) {
-      matches.addAll(canRename._fileResolver.findReferences(element));
+      matches.addAll(await fileResolver.findReferences2(element));
     }
     FlutterWidgetRename? flutterRename;
     if (canRename._flutterWidgetState != null) {
       var stateWidget = canRename._flutterWidgetState!;
-      var match = canRename._fileResolver.findReferences(stateWidget.state);
+      var match = await fileResolver.findReferences2(stateWidget.state);
       flutterRename = FlutterWidgetRename(stateWidget.newName, match);
     }
     return RenameResponse(matches, this, flutterWidgetRename: flutterRename);
@@ -114,8 +117,9 @@
 
   /// Check if the identifier at the [line], [column] for the file at the
   /// [filePath] can be renamed.
-  CanRenameResponse? canRename(String filePath, int line, int column) {
-    var resolvedUnit = _fileResolver.resolve(path: filePath);
+  Future<CanRenameResponse?> canRename2(
+      String filePath, int line, int column) async {
+    var resolvedUnit = await _fileResolver.resolve2(path: filePath);
     var lineInfo = resolvedUnit.lineInfo;
     var offset = lineInfo.getOffsetOfLine(line) + column;
 
diff --git a/pkg/analysis_server/lib/src/cider/signature_help.dart b/pkg/analysis_server/lib/src/cider/signature_help.dart
index 6b3616a..107060e 100644
--- a/pkg/analysis_server/lib/src/cider/signature_help.dart
+++ b/pkg/analysis_server/lib/src/cider/signature_help.dart
@@ -15,8 +15,9 @@
 
   CiderSignatureHelpComputer(this._fileResolver);
 
-  SignatureHelpResponse? compute(String filePath, int line, int column) {
-    var resolvedUnit = _fileResolver.resolve(path: filePath);
+  Future<SignatureHelpResponse?> compute2(
+      String filePath, int line, int column) async {
+    var resolvedUnit = await _fileResolver.resolve2(path: filePath);
     var lineInfo = resolvedUnit.lineInfo;
     var offset = lineInfo.getOffsetOfLine(line) + column;
     final formats = <MarkupKind>{MarkupKind.Markdown};
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
index 303ffc6..299f79a 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -681,6 +681,7 @@
         node.externalKeyword, HighlightRegionType.BUILT_IN);
     computer._addRegion_token(
         node.factoryKeyword, HighlightRegionType.BUILT_IN);
+    computer._addRegion_token(node.constKeyword, HighlightRegionType.KEYWORD);
     super.visitConstructorDeclaration(node);
   }
 
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 6dc9295..a7ea8fd 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -330,10 +330,14 @@
     var convertedErrors = const <protocol.AnalysisError>[];
     try {
       var file = resourceProvider.getFile(path);
-      var packageName = file.parent2.parent2.shortName;
+      var packageName = file.parent.parent.shortName;
       var content = _readFile(path);
       var errorListener = RecordingErrorListener();
-      var errorReporter = ErrorReporter(errorListener, file.createSource());
+      var errorReporter = ErrorReporter(
+        errorListener,
+        file.createSource(),
+        isNonNullableByDefault: false,
+      );
       var parser = TransformSetParser(errorReporter, packageName);
       parser.parse(content);
       var converter = AnalyzerConverter();
@@ -480,7 +484,9 @@
           }
 
           var optionsFile = analysisContext.contextRoot.optionsFile;
-          if (optionsFile != null) {
+
+          if (optionsFile != null &&
+              analysisContext.contextRoot.isAnalyzed(optionsFile.path)) {
             _analyzeAnalysisOptionsYaml(driver, optionsFile.path);
           }
 
@@ -493,7 +499,8 @@
 
           var pubspecFile =
               rootFolder.getChildAssumingFile(file_paths.pubspecYaml);
-          if (pubspecFile.exists) {
+          if (pubspecFile.exists &&
+              analysisContext.contextRoot.isAnalyzed(pubspecFile.path)) {
             _analyzePubspecYaml(driver, pubspecFile.path);
           }
         }
@@ -509,7 +516,6 @@
         return file_paths.isDart(pathContext, path) ||
             file_paths.isAnalysisOptionsYaml(pathContext, path) ||
             file_paths.isPubspecYaml(pathContext, path) ||
-            file_paths.isDotPackages(pathContext, path) ||
             file_paths.isPackageConfigJson(pathContext, path);
       }
 
@@ -671,7 +677,6 @@
     final isPubspec = file_paths.isPubspecYaml(pathContext, path);
     if (file_paths.isAnalysisOptionsYaml(pathContext, path) ||
         file_paths.isBazelBuild(pathContext, path) ||
-        file_paths.isDotPackages(pathContext, path) ||
         file_paths.isPackageConfigJson(pathContext, path) ||
         isPubspec ||
         false) {
diff --git a/pkg/analysis_server/lib/src/domain_analysis.dart b/pkg/analysis_server/lib/src/domain_analysis.dart
index af16242..cfd19e2 100644
--- a/pkg/analysis_server/lib/src/domain_analysis.dart
+++ b/pkg/analysis_server/lib/src/domain_analysis.dart
@@ -4,21 +4,22 @@
 
 import 'package:analysis_server/protocol/protocol_constants.dart';
 import 'package:analysis_server/src/analysis_server.dart';
-import 'package:analysis_server/src/computer/computer_hover.dart';
-import 'package:analysis_server/src/computer/computer_signature.dart';
-import 'package:analysis_server/src/computer/imported_elements_computer.dart';
 import 'package:analysis_server/src/domain_abstract.dart';
-import 'package:analysis_server/src/domain_analysis_flags.dart';
+import 'package:analysis_server/src/handler/legacy/analysis_get_errors.dart';
+import 'package:analysis_server/src/handler/legacy/analysis_get_hover.dart';
+import 'package:analysis_server/src/handler/legacy/analysis_get_imported_elements.dart';
+import 'package:analysis_server/src/handler/legacy/analysis_get_navigation.dart';
+import 'package:analysis_server/src/handler/legacy/analysis_get_signature.dart';
+import 'package:analysis_server/src/handler/legacy/analysis_reanalyze.dart';
+import 'package:analysis_server/src/handler/legacy/analysis_set_analysis_roots.dart';
+import 'package:analysis_server/src/handler/legacy/analysis_set_general_subscriptions.dart';
+import 'package:analysis_server/src/handler/legacy/analysis_set_priority_files.dart';
+import 'package:analysis_server/src/handler/legacy/analysis_set_subscriptions.dart';
+import 'package:analysis_server/src/handler/legacy/unsupported_request.dart';
 import 'package:analysis_server/src/plugin/request_converter.dart';
-import 'package:analysis_server/src/plugin/result_merger.dart';
-import 'package:analysis_server/src/protocol/protocol_internal.dart';
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/utilities/progress.dart';
-import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/src/generated/engine.dart' as engine;
-import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
-import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
-import 'package:analyzer_plugin/utilities/navigation/navigation_dart.dart';
 
 /// Instances of the class [AnalysisDomainHandler] implement a [RequestHandler]
 /// that handles requests in the `analysis` domain.
@@ -27,272 +28,62 @@
   /// [server].
   AnalysisDomainHandler(AnalysisServer server) : super(server);
 
-  /// Implement the `analysis.getErrors` request.
-  Future<void> getErrors(Request request) async {
-    var file = AnalysisGetErrorsParams.fromRequest(request).file;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    var result = await server.getResolvedUnit(file);
-
-    if (result == null) {
-      server.sendResponse(Response.getErrorsInvalidFile(request));
-      return;
-    }
-
-    var protocolErrors = doAnalysisError_listFromEngine(result);
-    server.sendResponse(
-        AnalysisGetErrorsResult(protocolErrors).toResponse(request.id));
-  }
-
-  /// Implement the `analysis.getHover` request.
-  Future<void> getHover(Request request) async {
-    var params = AnalysisGetHoverParams.fromRequest(request);
-    var file = params.file;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    // Prepare the resolved units.
-    var result = await server.getResolvedUnit(file);
-    if (result is! ResolvedUnitResult) {
-      server.sendResponse(Response.fileNotAnalyzed(request, file));
-      return;
-    }
-    var unit = result.unit;
-
-    // Prepare the hovers.
-    var hovers = <HoverInformation>[];
-    var computer = DartUnitHoverComputer(
-        server.getDartdocDirectiveInfoFor(result), unit, params.offset);
-    var hoverInformation = computer.compute();
-    if (hoverInformation != null) {
-      hovers.add(hoverInformation);
-    }
-
-    // Send the response.
-    server.sendResponse(AnalysisGetHoverResult(hovers).toResponse(request.id));
-  }
-
-  /// Implement the `analysis.getImportedElements` request.
-  Future<void> getImportedElements(Request request) async {
-    var params = AnalysisGetImportedElementsParams.fromRequest(request);
-    var file = params.file;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    //
-    // Prepare the resolved unit.
-    //
-    var result = await server.getResolvedUnit(file);
-    if (result == null) {
-      server.sendResponse(Response.getImportedElementsInvalidFile(request));
-      return;
-    }
-
-    List<ImportedElements> elements;
-
-    //
-    // Compute the list of imported elements.
-    //
-    if (disableManageImportsOnPaste) {
-      elements = <ImportedElements>[];
-    } else {
-      elements =
-          ImportedElementsComputer(result.unit, params.offset, params.length)
-              .compute();
-    }
-
-    //
-    // Send the response.
-    //
-    server.sendResponse(
-        AnalysisGetImportedElementsResult(elements).toResponse(request.id));
-  }
-
-  /// Implement the `analysis.getLibraryDependencies` request.
-  Response getLibraryDependencies(Request request) {
-    return Response.unsupportedFeature(request.id,
-        'Please contact the Dart analyzer team if you need this request.');
-//    server.onAnalysisComplete.then((_) {
-//      LibraryDependencyCollector collector =
-//          new LibraryDependencyCollector(server.analysisContexts);
-//      Set<String> libraries = collector.collectLibraryDependencies();
-//      Map<String, Map<String, List<String>>> packageMap =
-//          collector.calculatePackageMap(server.folderMap);
-//      server.sendResponse(new AnalysisGetLibraryDependenciesResult(
-//              libraries.toList(growable: false), packageMap)
-//          .toResponse(request.id));
-//    }).catchError((error, st) {
-//      server.sendResponse(new Response.serverError(request, error, st));
-//    });
-//    // delay response
-//    return Response.DELAYED_RESPONSE;
-  }
-
-  /// Implement the `analysis.getNavigation` request.
-  Future<void> getNavigation(Request request) async {
-    var params = AnalysisGetNavigationParams.fromRequest(request);
-    var file = params.file;
-    var offset = params.offset;
-    var length = params.length;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    var driver = server.getAnalysisDriver(file);
-    if (driver == null) {
-      server.sendResponse(Response.getNavigationInvalidFile(request));
-    } else {
-      //
-      // Allow plugins to start computing navigation data.
-      //
-      var requestParams =
-          plugin.AnalysisGetNavigationParams(file, offset, length);
-      var pluginFutures = server.pluginManager.broadcastRequest(
-        requestParams,
-        contextRoot: driver.analysisContext!.contextRoot,
-      );
-      //
-      // Compute navigation data generated by server.
-      //
-      var allResults = <AnalysisNavigationParams>[];
-      var result = await server.getResolvedUnit(file);
-      if (result != null) {
-        var unit = result.unit;
-        var collector = NavigationCollectorImpl();
-        computeDartNavigation(
-            server.resourceProvider, collector, unit, offset, length);
-        collector.createRegions();
-        allResults.add(AnalysisNavigationParams(
-            file, collector.regions, collector.targets, collector.files));
-      }
-      //
-      // Add the navigation data produced by plugins to the server-generated
-      // navigation data.
-      //
-      var responses = await waitForResponses(pluginFutures,
-          requestParameters: requestParams);
-      for (var response in responses) {
-        var result = plugin.AnalysisGetNavigationResult.fromResponse(response);
-        allResults.add(AnalysisNavigationParams(
-            file, result.regions, result.targets, result.files));
-      }
-      //
-      // Return the result.
-      //
-      var merger = ResultMerger();
-      var mergedResults = merger.mergeNavigation(allResults);
-      if (mergedResults == null) {
-        server.sendResponse(AnalysisGetNavigationResult(
-                <String>[], <NavigationTarget>[], <NavigationRegion>[])
-            .toResponse(request.id));
-      } else {
-        server.sendResponse(AnalysisGetNavigationResult(mergedResults.files,
-                mergedResults.targets, mergedResults.regions)
-            .toResponse(request.id));
-      }
-    }
-  }
-
-  /// Implement the `analysis.getReachableSources` request.
-  Response getReachableSources(Request request) {
-    return Response.unsupportedFeature(request.id,
-        'Please contact the Dart analyzer team if you need this request.');
-//    AnalysisGetReachableSourcesParams params =
-//        new AnalysisGetReachableSourcesParams.fromRequest(request);
-//    ContextSourcePair pair = server.getContextSourcePair(params.file);
-//    if (pair.context == null || pair.source == null) {
-//      return new Response.getReachableSourcesInvalidFile(request);
-//    }
-//    Map<String, List<String>> sources =
-//        new ReachableSourceCollector(pair.source, pair.context)
-//            .collectSources();
-//    return new AnalysisGetReachableSourcesResult(sources)
-//        .toResponse(request.id);
-  }
-
-  /// Implement the `analysis.getSignature` request.
-  Future<void> getSignature(Request request) async {
-    var params = AnalysisGetSignatureParams.fromRequest(request);
-    var file = params.file;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    // Prepare the resolved units.
-    var result = await server.getResolvedUnit(file);
-
-    if (result == null || !result.exists) {
-      server.sendResponse(Response.getSignatureInvalidFile(request));
-      return;
-    }
-
-    // Ensure the offset provided is a valid location in the file.
-    final unit = result.unit;
-    final computer = DartUnitSignatureComputer(
-        server.getDartdocDirectiveInfoFor(result), unit, params.offset);
-    if (!computer.offsetIsValid) {
-      server.sendResponse(Response.getSignatureInvalidOffset(request));
-      return;
-    }
-
-    // Try to get a signature.
-    final signature = computer.compute();
-    if (signature == null) {
-      server.sendResponse(Response.getSignatureUnknownFunction(request));
-      return;
-    }
-
-    server.sendResponse(signature.toResponse(request.id));
-  }
-
   @override
   Response? handleRequest(
       Request request, CancellationToken cancellationToken) {
     try {
       var requestName = request.method;
       if (requestName == ANALYSIS_REQUEST_GET_ERRORS) {
-        getErrors(request);
+        AnalysisGetErrorsHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_GET_HOVER) {
-        getHover(request);
+        AnalysisGetHoverHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_GET_IMPORTED_ELEMENTS) {
-        getImportedElements(request);
+        AnalysisGetImportedElementsHandler(server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_GET_LIBRARY_DEPENDENCIES) {
-        return getLibraryDependencies(request);
+        UnsupportedRequestHandler(server, request, cancellationToken).handle();
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_GET_NAVIGATION) {
-        getNavigation(request);
+        AnalysisGetNavigationHandler(server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_GET_REACHABLE_SOURCES) {
-        return getReachableSources(request);
+        UnsupportedRequestHandler(server, request, cancellationToken).handle();
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_GET_SIGNATURE) {
-        getSignature(request);
+        AnalysisGetSignatureHandler(server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_REANALYZE) {
-        reanalyze(request);
+        AnalysisReanalyzeHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_SET_ANALYSIS_ROOTS) {
-        setAnalysisRoots(request);
+        AnalysisSetAnalysisRootsHandler(server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_SET_GENERAL_SUBSCRIPTIONS) {
-        return setGeneralSubscriptions(request);
+        AnalysisSetGeneralSubscriptionsHandler(
+                server, request, cancellationToken)
+            .handle();
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_SET_PRIORITY_FILES) {
-        return setPriorityFiles(request);
+        AnalysisSetPriorityFilesHandler(server, request, cancellationToken)
+            .handle();
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_SET_SUBSCRIPTIONS) {
-        return setSubscriptions(request);
+        AnalysisSetSubscriptionsHandler(server, request, cancellationToken)
+            .handle();
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == ANALYSIS_REQUEST_UPDATE_CONTENT) {
+        // TODO(brianwilkerson) Converting this to a handler currently causes a
+        //  test to timeout.
         return updateContent(request);
       } else if (requestName == ANALYSIS_REQUEST_UPDATE_OPTIONS) {
+        // TODO(brianwilkerson) Converting this to a handler currently causes a
+        //  test to timeout.
         return updateOptions(request);
       }
     } on RequestFailure catch (exception) {
@@ -301,120 +92,6 @@
     return null;
   }
 
-  /// Implement the 'analysis.reanalyze' request.
-  void reanalyze(Request request) async {
-    server.options.analytics?.sendEvent('analysis', 'reanalyze');
-
-    await server.reanalyze();
-
-    //
-    // Restart all of the plugins. This is an async operation that will happen
-    // in the background.
-    //
-    server.pluginManager.restartPlugins();
-    //
-    // Send the response.
-    //
-    server.sendResponse(AnalysisReanalyzeResult().toResponse(request.id));
-  }
-
-  /// Implement the 'analysis.setAnalysisRoots' request.
-  void setAnalysisRoots(Request request) async {
-    var params = AnalysisSetAnalysisRootsParams.fromRequest(request);
-    var includedPathList = params.included;
-    var excludedPathList = params.excluded;
-
-    server.options.analytics?.sendEvent('analysis', 'setAnalysisRoots',
-        value: includedPathList.length);
-
-    // validate
-    for (var path in includedPathList) {
-      if (!server.isValidFilePath(path)) {
-        server.sendResponse(Response.invalidFilePathFormat(request, path));
-        return;
-      }
-    }
-    for (var path in excludedPathList) {
-      if (!server.isValidFilePath(path)) {
-        server.sendResponse(Response.invalidFilePathFormat(request, path));
-        return;
-      }
-    }
-
-    var detachableFileSystemManager = server.detachableFileSystemManager;
-    if (detachableFileSystemManager != null) {
-      // TODO(scheglov) remove the last argument
-      detachableFileSystemManager
-          .setAnalysisRoots(request.id, includedPathList, excludedPathList, {});
-    } else {
-      await server.setAnalysisRoots(
-          request.id, includedPathList, excludedPathList);
-    }
-    return server
-        .sendResponse(AnalysisSetAnalysisRootsResult().toResponse(request.id));
-  }
-
-  /// Implement the 'analysis.setGeneralSubscriptions' request.
-  Response setGeneralSubscriptions(Request request) {
-    var params = AnalysisSetGeneralSubscriptionsParams.fromRequest(request);
-    server.setGeneralAnalysisSubscriptions(params.subscriptions);
-    return AnalysisSetGeneralSubscriptionsResult().toResponse(request.id);
-  }
-
-  /// Implement the 'analysis.setPriorityFiles' request.
-  Response setPriorityFiles(Request request) {
-    var params = AnalysisSetPriorityFilesParams.fromRequest(request);
-
-    for (var file in params.files) {
-      if (!server.isAbsoluteAndNormalized(file)) {
-        return Response.invalidFilePathFormat(request, file);
-      }
-    }
-
-    server.setPriorityFiles(request.id, params.files);
-    //
-    // Forward the request to the plugins.
-    //
-    var converter = RequestConverter();
-    server.pluginManager.setAnalysisSetPriorityFilesParams(
-        converter.convertAnalysisSetPriorityFilesParams(params));
-    //
-    // Send the response.
-    //
-    return AnalysisSetPriorityFilesResult().toResponse(request.id);
-  }
-
-  /// Implement the 'analysis.setSubscriptions' request.
-  Response setSubscriptions(Request request) {
-    var params = AnalysisSetSubscriptionsParams.fromRequest(request);
-
-    for (var fileList in params.subscriptions.values) {
-      for (var file in fileList) {
-        if (!server.isAbsoluteAndNormalized(file)) {
-          return Response.invalidFilePathFormat(request, file);
-        }
-      }
-    }
-
-    // parse subscriptions
-    var subMap =
-        mapMap<AnalysisService, List<String>, AnalysisService, Set<String>>(
-            params.subscriptions,
-            valueCallback: (List<String> subscriptions) =>
-                subscriptions.toSet());
-    server.setAnalysisSubscriptions(subMap);
-    //
-    // Forward the request to the plugins.
-    //
-    var converter = RequestConverter();
-    server.pluginManager.setAnalysisSetSubscriptionsParams(
-        converter.convertAnalysisSetSubscriptionsParams(params));
-    //
-    // Send the response.
-    //
-    return AnalysisSetSubscriptionsResult().toResponse(request.id);
-  }
-
   /// Implement the 'analysis.updateContent' request.
   Response updateContent(Request request) {
     var params = AnalysisUpdateContentParams.fromRequest(request);
diff --git a/pkg/analysis_server/lib/src/domain_analytics.dart b/pkg/analysis_server/lib/src/domain_analytics.dart
index cec9fa8..1460410 100644
--- a/pkg/analysis_server/lib/src/domain_analytics.dart
+++ b/pkg/analysis_server/lib/src/domain_analytics.dart
@@ -4,10 +4,12 @@
 
 import 'package:analysis_server/protocol/protocol.dart';
 import 'package:analysis_server/protocol/protocol_constants.dart';
-import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/analytics_enable.dart';
+import 'package:analysis_server/src/handler/legacy/analytics_is_enabled.dart';
+import 'package:analysis_server/src/handler/legacy/analytics_send_event.dart';
+import 'package:analysis_server/src/handler/legacy/analytics_send_timing.dart';
 import 'package:analysis_server/src/utilities/progress.dart';
-import 'package:telemetry/telemetry.dart';
 
 /// Instances of the class [AnalyticsDomainHandler] implement a [RequestHandler]
 /// that handles requests in the `analytics` domain.
@@ -16,61 +18,24 @@
 
   AnalyticsDomainHandler(this.server);
 
-  Analytics? get analytics => server.analytics;
-
-  String get _clientId => server.options.clientId ?? 'client';
-
-  Response handleEnable(Request request) {
-    var params = AnalyticsEnableParams.fromRequest(request);
-    final analytics = this.analytics;
-    if (analytics != null) {
-      analytics.enabled = params.value;
-    }
-    return AnalyticsEnableResult().toResponse(request.id);
-  }
-
-  Response handleIsEnabled(Request request) {
-    return AnalyticsIsEnabledResult(analytics?.enabled ?? false)
-        .toResponse(request.id);
-  }
-
   @override
   Response? handleRequest(
       Request request, CancellationToken cancellationToken) {
     var requestName = request.method;
 
     if (requestName == ANALYTICS_REQUEST_IS_ENABLED) {
-      return handleIsEnabled(request);
+      AnalyticsIsEnabledHandler(server, request, cancellationToken).handle();
+      return Response.DELAYED_RESPONSE;
     } else if (requestName == ANALYTICS_REQUEST_ENABLE) {
-      return handleEnable(request);
+      AnalyticsEnableHandler(server, request, cancellationToken).handle();
+      return Response.DELAYED_RESPONSE;
     } else if (requestName == ANALYTICS_REQUEST_SEND_EVENT) {
-      return handleSendEvent(request);
+      AnalyticsSendEventHandler(server, request, cancellationToken).handle();
+      return Response.DELAYED_RESPONSE;
     } else if (requestName == ANALYTICS_REQUEST_SEND_TIMING) {
-      return handleSendTiming(request);
+      AnalyticsSendTimingHandler(server, request, cancellationToken).handle();
+      return Response.DELAYED_RESPONSE;
     }
-
     return null;
   }
-
-  Response handleSendEvent(Request request) {
-    final analytics = this.analytics;
-    if (analytics == null) {
-      return AnalyticsSendEventResult().toResponse(request.id);
-    }
-
-    var params = AnalyticsSendEventParams.fromRequest(request);
-    analytics.sendEvent(_clientId, params.action);
-    return AnalyticsSendEventResult().toResponse(request.id);
-  }
-
-  Response handleSendTiming(Request request) {
-    final analytics = this.analytics;
-    if (analytics == null) {
-      return AnalyticsSendTimingResult().toResponse(request.id);
-    }
-
-    var params = AnalyticsSendTimingParams.fromRequest(request);
-    analytics.sendTiming(params.event, params.millis, category: _clientId);
-    return AnalyticsSendTimingResult().toResponse(request.id);
-  }
 }
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 9f3043e..3db8862 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -11,6 +11,8 @@
 import 'package:analysis_server/src/collections.dart';
 import 'package:analysis_server/src/domain_abstract.dart';
 import 'package:analysis_server/src/domains/completion/available_suggestions.dart';
+import 'package:analysis_server/src/handler/legacy/completion_get_suggestion_details.dart';
+import 'package:analysis_server/src/handler/legacy/completion_get_suggestion_details2.dart';
 import 'package:analysis_server/src/plugin/plugin_manager.dart';
 import 'package:analysis_server/src/provisional/completion/completion_core.dart';
 import 'package:analysis_server/src/services/completion/completion_performance.dart';
@@ -22,7 +24,6 @@
 import 'package:analysis_server/src/services/completion/yaml/pubspec_generator.dart';
 import 'package:analysis_server/src/services/completion/yaml/yaml_completion_generator.dart';
 import 'package:analysis_server/src/utilities/progress.dart';
-import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/exception/exception.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/util/file_paths.dart' as file_paths;
@@ -30,7 +31,6 @@
 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
-import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:collection/collection.dart';
 
 /// Instances of the class [CompletionDomainHandler] implement a
@@ -43,6 +43,10 @@
   Duration budgetDuration = CompletionBudget.defaultDuration;
 
   /// The completion services that the client is currently subscribed.
+  // TODO(brianwilkerson) This needs to be moved to some location where multiple
+  //  [LegacyHandler]s can access it, and the tests need to be cleaned up so
+  //  that they no longer depend on creating a new [CompletionDomainHandler] to
+  //  clear subscriptions from previous tests.
   final Set<CompletionService> subscriptions = <CompletionService>{};
 
   /// The next completion response id.
@@ -56,10 +60,6 @@
   /// The current request being processed or `null` if none.
   DartCompletionRequest? _currentRequest;
 
-  /// The identifiers of the latest `getSuggestionDetails` request.
-  /// We use it to abort previous requests.
-  int _latestGetSuggestionDetailsId = 0;
-
   /// Initialize a new request handler for the given [server].
   CompletionDomainHandler(AnalysisServer server) : super(server);
 
@@ -134,137 +134,6 @@
     return const YamlCompletionResults.empty();
   }
 
-  /// Process a `completion.getSuggestionDetails` request.
-  void getSuggestionDetails(Request request) async {
-    var params = CompletionGetSuggestionDetailsParams.fromRequest(request);
-
-    var file = params.file;
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    var libraryId = params.id;
-    var declarationsTracker = server.declarationsTracker;
-    if (declarationsTracker == null) {
-      server.sendResponse(Response.unsupportedFeature(
-          request.id, 'Completion is not enabled.'));
-      return;
-    }
-    var library = declarationsTracker.getLibrary(libraryId);
-    if (library == null) {
-      server.sendResponse(Response.invalidParameter(
-        request,
-        'libraryId',
-        'No such library: $libraryId',
-      ));
-      return;
-    }
-
-    // The label might be `MyEnum.myValue`, but we import only `MyEnum`.
-    var requestedName = params.label;
-    if (requestedName.contains('.')) {
-      requestedName = requestedName.substring(
-        0,
-        requestedName.indexOf('.'),
-      );
-    }
-
-    const timeout = Duration(milliseconds: 1000);
-    var timer = Stopwatch()..start();
-    var id = ++_latestGetSuggestionDetailsId;
-    while (id == _latestGetSuggestionDetailsId && timer.elapsed < timeout) {
-      try {
-        var analysisDriver = server.getAnalysisDriver(file);
-        if (analysisDriver == null) {
-          server.sendResponse(Response.fileNotAnalyzed(request, 'libraryId'));
-          return;
-        }
-        var session = analysisDriver.currentSession;
-
-        var completion = params.label;
-        var builder = ChangeBuilder(session: session);
-        await builder.addDartFileEdit(file, (builder) {
-          var result = builder.importLibraryElement(library.uri);
-          if (result.prefix != null) {
-            completion = '${result.prefix}.$completion';
-          }
-        });
-
-        server.sendResponse(
-          CompletionGetSuggestionDetailsResult(
-            completion,
-            change: builder.sourceChange,
-          ).toResponse(request.id),
-        );
-        return;
-      } on InconsistentAnalysisException {
-        // Loop around to try again.
-      }
-    }
-
-    // Timeout or abort, send the empty response.
-    server.sendResponse(
-      CompletionGetSuggestionDetailsResult('').toResponse(request.id),
-    );
-  }
-
-  /// Process a `completion.getSuggestionDetails2` request.
-  void getSuggestionDetails2(Request request) async {
-    var params = CompletionGetSuggestionDetails2Params.fromRequest(request);
-
-    var file = params.file;
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    var libraryUri = Uri.tryParse(params.libraryUri);
-    if (libraryUri == null) {
-      server.sendResponse(
-        Response.invalidParameter(request, 'libraryUri', 'Cannot parse'),
-      );
-      return;
-    }
-
-    var budget = CompletionBudget(
-      const Duration(milliseconds: 1000),
-    );
-    var id = ++_latestGetSuggestionDetailsId;
-    while (id == _latestGetSuggestionDetailsId && !budget.isEmpty) {
-      try {
-        var analysisDriver = server.getAnalysisDriver(file);
-        if (analysisDriver == null) {
-          server.sendResponse(Response.fileNotAnalyzed(request, file));
-          return;
-        }
-        var session = analysisDriver.currentSession;
-
-        var completion = params.completion;
-        var builder = ChangeBuilder(session: session);
-        await builder.addDartFileEdit(file, (builder) {
-          var result = builder.importLibraryElement(libraryUri);
-          if (result.prefix != null) {
-            completion = '${result.prefix}.$completion';
-          }
-        });
-
-        server.sendResponse(
-          CompletionGetSuggestionDetails2Result(
-            completion,
-            builder.sourceChange,
-          ).toResponse(request.id),
-        );
-        return;
-      } on InconsistentAnalysisException {
-        // Loop around to try again.
-      }
-    }
-
-    // Timeout or abort, send the empty response.
-    server.sendResponse(
-      CompletionGetSuggestionDetailsResult('').toResponse(request.id),
-    );
-  }
-
   /// Implement the 'completion.getSuggestions2' request.
   void getSuggestions2(Request request) async {
     var params = CompletionGetSuggestions2Params.fromRequest(request);
@@ -306,7 +175,7 @@
     performance.runAsync(
       'request',
       (performance) async {
-        var resolvedUnit = performance.run(
+        var resolvedUnit = await performance.runAsync(
           'resolveForCompletion',
           (performance) {
             return server.resolveForCompletion(
@@ -387,7 +256,10 @@
 
         var lengthRestricted =
             suggestionBuilders.take(params.maxResults).toList();
-        completionPerformance.suggestionCount = lengthRestricted.length;
+        completionPerformance.computedSuggestionCount =
+            suggestionBuilders.length;
+        completionPerformance.transmittedSuggestionCount =
+            lengthRestricted.length;
 
         var suggestions = lengthRestricted.map((e) => e.build()).toList();
 
@@ -423,10 +295,14 @@
       var requestName = request.method;
 
       if (requestName == COMPLETION_REQUEST_GET_SUGGESTION_DETAILS) {
-        getSuggestionDetails(request);
+        CompletionGetSuggestionDetailsHandler(
+                server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2) {
-        getSuggestionDetails2(request);
+        CompletionGetSuggestionDetails2Handler(
+                server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == COMPLETION_REQUEST_GET_SUGGESTIONS) {
         processRequest(request);
@@ -604,7 +480,10 @@
             );
           });
 
-          completionPerformance.suggestionCount = suggestionBuilders.length;
+          completionPerformance.computedSuggestionCount =
+              suggestionBuilders.length;
+          completionPerformance.transmittedSuggestionCount =
+              suggestionBuilders.length;
         } finally {
           ifMatchesRequestClear(completionRequest);
         }
diff --git a/pkg/analysis_server/lib/src/domain_diagnostic.dart b/pkg/analysis_server/lib/src/domain_diagnostic.dart
index 117bec4..35a41d6 100644
--- a/pkg/analysis_server/lib/src/domain_diagnostic.dart
+++ b/pkg/analysis_server/lib/src/domain_diagnostic.dart
@@ -4,10 +4,10 @@
 
 import 'package:analysis_server/protocol/protocol.dart';
 import 'package:analysis_server/protocol/protocol_constants.dart';
-import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/diagnostic_get_diagnostics.dart';
+import 'package:analysis_server/src/handler/legacy/diagnostic_get_server_port.dart';
 import 'package:analysis_server/src/utilities/progress.dart';
-import 'package:analyzer/src/dart/analysis/driver.dart';
 
 /// Instances of the class [DiagnosticDomainHandler] implement a
 /// [RequestHandler] that handles requests in the `diagnostic` domain.
@@ -19,41 +19,18 @@
   /// [server].
   DiagnosticDomainHandler(this.server);
 
-  /// Answer the `diagnostic.getDiagnostics` request.
-  Response computeDiagnostics(Request request) {
-    var contexts = server.driverMap.values.map(extractDataFromDriver).toList();
-    return DiagnosticGetDiagnosticsResult(contexts).toResponse(request.id);
-  }
-
-  /// Extract context data from the given [driver].
-  ContextData extractDataFromDriver(AnalysisDriver driver) {
-    var explicitFileCount = driver.addedFiles.length;
-    var knownFileCount = driver.knownFiles.length;
-    return ContextData(driver.name, explicitFileCount,
-        knownFileCount - explicitFileCount, driver.numberOfFilesToAnalyze, []);
-  }
-
-  /// Answer the `diagnostic.getServerPort` request.
-  Future handleGetServerPort(Request request) async {
-    try {
-      // Open a port (or return the existing one).
-      var port = await server.diagnosticServer!.getServerPort();
-      server.sendResponse(
-          DiagnosticGetServerPortResult(port).toResponse(request.id));
-    } catch (error) {
-      server.sendResponse(Response.debugPortCouldNotBeOpened(request, error));
-    }
-  }
-
   @override
   Response? handleRequest(
       Request request, CancellationToken cancellationToken) {
     try {
       var requestName = request.method;
       if (requestName == DIAGNOSTIC_REQUEST_GET_DIAGNOSTICS) {
-        return computeDiagnostics(request);
+        DiagnosticGetDiagnosticsHandler(server, request, cancellationToken)
+            .handle();
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == DIAGNOSTIC_REQUEST_GET_SERVER_PORT) {
-        handleGetServerPort(request);
+        DiagnosticGetServerPortHandler(server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       }
     } on RequestFailure catch (exception) {
diff --git a/pkg/analysis_server/lib/src/domain_server.dart b/pkg/analysis_server/lib/src/domain_server.dart
index 54efa9a..a5b7b83 100644
--- a/pkg/analysis_server/lib/src/domain_server.dart
+++ b/pkg/analysis_server/lib/src/domain_server.dart
@@ -4,8 +4,11 @@
 
 import 'package:analysis_server/protocol/protocol.dart';
 import 'package:analysis_server/protocol/protocol_constants.dart';
-import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/server_cancel_request.dart';
+import 'package:analysis_server/src/handler/legacy/server_get_version.dart';
+import 'package:analysis_server/src/handler/legacy/server_set_subscriptions.dart';
+import 'package:analysis_server/src/handler/legacy/server_shutdown.dart';
 import 'package:analysis_server/src/utilities/progress.dart';
 
 /// Instances of the class [ServerDomainHandler] implement a [RequestHandler]
@@ -18,58 +21,28 @@
   /// [server].
   ServerDomainHandler(this.server);
 
-  Response cancelRequest(Request request) {
-    final id = ServerCancelRequestParams.fromRequest(request).id;
-    server.cancelRequest(id);
-
-    return ServerCancelRequestResult().toResponse(request.id);
-  }
-
-  /// Return the version number of the analysis server.
-  Response getVersion(Request request) {
-    return ServerGetVersionResult(
-      server.options.reportProtocolVersion ?? PROTOCOL_VERSION,
-    ).toResponse(request.id);
-  }
-
   @override
   Response? handleRequest(
       Request request, CancellationToken cancellationToken) {
     try {
       var requestName = request.method;
       if (requestName == SERVER_REQUEST_GET_VERSION) {
-        return getVersion(request);
+        ServerGetVersionHandler(server, request, cancellationToken).handle();
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == SERVER_REQUEST_SET_SUBSCRIPTIONS) {
-        return setSubscriptions(request);
+        ServerSetSubscriptionsHandler(server, request, cancellationToken)
+            .handle();
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == SERVER_REQUEST_SHUTDOWN) {
-        shutdown(request);
+        ServerShutdownHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == SERVER_REQUEST_CANCEL_REQUEST) {
-        return cancelRequest(request);
+        ServerCancelRequestHandler(server, request, cancellationToken).handle();
+        return Response.DELAYED_RESPONSE;
       }
     } on RequestFailure catch (exception) {
       return exception.response;
     }
     return null;
   }
-
-  /// Subscribe for services.
-  ///
-  /// All previous subscriptions are replaced by the given set of subscriptions.
-  Response setSubscriptions(Request request) {
-    server.serverServices =
-        ServerSetSubscriptionsParams.fromRequest(request).subscriptions.toSet();
-
-    server.requestStatistics?.isNotificationSubscribed =
-        server.serverServices.contains(ServerService.LOG);
-
-    return ServerSetSubscriptionsResult().toResponse(request.id);
-  }
-
-  /// Cleanly shutdown the analysis server.
-  Future<void> shutdown(Request request) async {
-    await server.shutdown();
-    var response = ServerShutdownResult().toResponse(request.id);
-    server.sendResponse(response);
-  }
 }
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index eeda336..3b7e4a3 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -4,57 +4,31 @@
 
 import 'dart:async';
 
-import 'package:analysis_server/plugin/edit/assist/assist_core.dart';
-import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
 import 'package:analysis_server/protocol/protocol_constants.dart';
 import 'package:analysis_server/src/analysis_server.dart';
 import 'package:analysis_server/src/collections.dart';
-import 'package:analysis_server/src/computer/import_elements_computer.dart';
 import 'package:analysis_server/src/domain_abstract.dart';
-import 'package:analysis_server/src/plugin/plugin_manager.dart';
-import 'package:analysis_server/src/plugin/result_converter.dart';
+import 'package:analysis_server/src/handler/legacy/edit_bulk_fixes.dart';
+import 'package:analysis_server/src/handler/legacy/edit_format.dart';
+import 'package:analysis_server/src/handler/legacy/edit_format_if_enabled.dart';
+import 'package:analysis_server/src/handler/legacy/edit_get_assists.dart';
+import 'package:analysis_server/src/handler/legacy/edit_get_fixes.dart';
+import 'package:analysis_server/src/handler/legacy/edit_get_postfix_completion.dart';
+import 'package:analysis_server/src/handler/legacy/edit_get_statement_completion.dart';
+import 'package:analysis_server/src/handler/legacy/edit_import_elements.dart';
+import 'package:analysis_server/src/handler/legacy/edit_is_postfix_completion_applicable.dart';
+import 'package:analysis_server/src/handler/legacy/edit_list_postfix_completion_templates.dart';
+import 'package:analysis_server/src/handler/legacy/edit_organize_directives.dart';
+import 'package:analysis_server/src/handler/legacy/edit_sort_members.dart';
 import 'package:analysis_server/src/protocol_server.dart'
     hide AnalysisError, Element;
-import 'package:analysis_server/src/services/completion/postfix/postfix_completion.dart';
-import 'package:analysis_server/src/services/completion/statement/statement_completion.dart';
-import 'package:analysis_server/src/services/correction/assist.dart';
-import 'package:analysis_server/src/services/correction/assist_internal.dart';
-import 'package:analysis_server/src/services/correction/bulk_fix_processor.dart';
-import 'package:analysis_server/src/services/correction/change_workspace.dart';
-import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analysis_server/src/services/correction/fix/analysis_options/fix_generator.dart';
-import 'package:analysis_server/src/services/correction/fix/manifest/fix_generator.dart';
-import 'package:analysis_server/src/services/correction/fix/pubspec/fix_generator.dart';
-import 'package:analysis_server/src/services/correction/fix_internal.dart';
-import 'package:analysis_server/src/services/correction/organize_imports.dart';
-import 'package:analysis_server/src/services/correction/sort_members.dart';
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analysis_server/src/services/search/search_engine.dart';
 import 'package:analysis_server/src/utilities/progress.dart';
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/error/error.dart' as engine;
-import 'package:analyzer/exception/exception.dart';
-import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
-import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
-import 'package:analyzer/src/dart/analysis/results.dart' as engine;
 import 'package:analyzer/src/dart/ast/utilities.dart';
-import 'package:analyzer/src/dart/scanner/scanner.dart' as engine;
-import 'package:analyzer/src/exception/exception.dart';
-import 'package:analyzer/src/generated/parser.dart' as engine;
-import 'package:analyzer/src/generated/source.dart';
-import 'package:analyzer/src/manifest/manifest_validator.dart';
-import 'package:analyzer/src/manifest/manifest_values.dart';
-import 'package:analyzer/src/pubspec/pubspec_validator.dart';
-import 'package:analyzer/src/task/options.dart';
-import 'package:analyzer/src/util/file_paths.dart' as file_paths;
-import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
-import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
-import 'package:dart_style/dart_style.dart';
-import 'package:html/parser.dart';
-import 'package:yaml/yaml.dart';
 
 int test_resetCount = 0;
 
@@ -83,301 +57,60 @@
     _newRefactoringManager();
   }
 
-  Future bulkFixes(Request request) async {
-    //
-    // Compute bulk fixes
-    //
-    try {
-      var params = EditBulkFixesParams.fromRequest(request);
-      for (var file in params.included) {
-        if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-          return;
-        }
-      }
-
-      var collection = AnalysisContextCollectionImpl(
-        includedPaths: params.included,
-        resourceProvider: server.resourceProvider,
-        sdkPath: server.sdkPath,
-      );
-      var workspace = DartChangeWorkspace(
-          collection.contexts.map((c) => c.currentSession).toList());
-      var processor = BulkFixProcessor(server.instrumentationService, workspace,
-          useConfigFiles: params.inTestMode ?? false);
-
-      var changeBuilder = await processor.fixErrors(collection.contexts);
-
-      var response = EditBulkFixesResult(
-              changeBuilder.sourceChange.edits, processor.fixDetails)
-          .toResponse(request.id);
-      server.sendResponse(response);
-    } catch (exception, stackTrace) {
-      server.sendServerErrorNotification('Exception while getting bulk fixes',
-          CaughtException(exception, stackTrace), stackTrace);
-    }
-  }
-
-  Response format(Request request) {
-    server.options.analytics?.sendEvent('edit', 'format');
-
-    var params = EditFormatParams.fromRequest(request);
-    var file = params.file;
-
-    String unformattedCode;
-    try {
-      var resource = server.resourceProvider.getFile(file);
-      unformattedCode = resource.readAsStringSync();
-    } catch (e) {
-      return Response.formatInvalidFile(request);
-    }
-
-    int? start = params.selectionOffset;
-    int? length = params.selectionLength;
-
-    // No need to preserve 0,0 selection
-    if (start == 0 && length == 0) {
-      start = null;
-      length = null;
-    }
-
-    var code = SourceCode(unformattedCode,
-        uri: null,
-        isCompilationUnit: true,
-        selectionStart: start,
-        selectionLength: length);
-    var formatter = DartFormatter(pageWidth: params.lineLength);
-    SourceCode formattedResult;
-    try {
-      formattedResult = formatter.formatSource(code);
-    } on FormatterException {
-      return Response.formatWithErrors(request);
-    }
-    var formattedSource = formattedResult.text;
-
-    var edits = <SourceEdit>[];
-
-    if (formattedSource != unformattedCode) {
-      //TODO: replace full replacements with smaller, more targeted edits
-      var edit = SourceEdit(0, unformattedCode.length, formattedSource);
-      edits.add(edit);
-    }
-
-    var newStart = formattedResult.selectionStart;
-    var newLength = formattedResult.selectionLength;
-
-    // Sending null start/length values would violate protocol, so convert back
-    // to 0.
-    newStart ??= 0;
-    newLength ??= 0;
-
-    return EditFormatResult(edits, newStart, newLength).toResponse(request.id);
-  }
-
-  Future getAssists(Request request) async {
-    var params = EditGetAssistsParams.fromRequest(request);
-    var file = params.file;
-    var offset = params.offset;
-    var length = params.length;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    //
-    // Allow plugins to start computing assists.
-    //
-    Map<PluginInfo, Future<plugin.Response>> pluginFutures;
-    var requestParams = plugin.EditGetAssistsParams(file, offset, length);
-    var driver = server.getAnalysisDriver(file);
-    if (driver == null) {
-      pluginFutures = <PluginInfo, Future<plugin.Response>>{};
-    } else {
-      pluginFutures = server.pluginManager.broadcastRequest(
-        requestParams,
-        contextRoot: driver.analysisContext!.contextRoot,
-      );
-    }
-
-    //
-    // Compute fixes associated with server-generated errors.
-    //
-    var changes = await _computeServerAssists(request, file, offset, length);
-
-    //
-    // Add the fixes produced by plugins to the server-generated fixes.
-    //
-    var responses =
-        await waitForResponses(pluginFutures, requestParameters: requestParams);
-    server.requestStatistics?.addItemTimeNow(request, 'pluginResponses');
-    var converter = ResultConverter();
-    var pluginChanges = <plugin.PrioritizedSourceChange>[];
-    for (var response in responses) {
-      var result = plugin.EditGetAssistsResult.fromResponse(response);
-      pluginChanges.addAll(result.assists);
-    }
-    pluginChanges
-        .sort((first, second) => first.priority.compareTo(second.priority));
-    changes.addAll(pluginChanges.map(converter.convertPrioritizedSourceChange));
-
-    //
-    // Send the response.
-    //
-    server.sendResponse(EditGetAssistsResult(changes).toResponse(request.id));
-  }
-
-  Future<void> getFixes(Request request) async {
-    var params = EditGetFixesParams.fromRequest(request);
-    var file = params.file;
-    var offset = params.offset;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    if (!server.isAnalyzed(file)) {
-      server.sendResponse(Response.getFixesInvalidFile(request));
-      return;
-    }
-
-    //
-    // Allow plugins to start computing fixes.
-    //
-    Map<PluginInfo, Future<plugin.Response>> pluginFutures;
-    var requestParams = plugin.EditGetFixesParams(file, offset);
-    var driver = server.getAnalysisDriver(file);
-    if (driver == null) {
-      pluginFutures = <PluginInfo, Future<plugin.Response>>{};
-    } else {
-      pluginFutures = server.pluginManager.broadcastRequest(
-        requestParams,
-        contextRoot: driver.analysisContext!.contextRoot,
-      );
-    }
-    //
-    // Compute fixes associated with server-generated errors.
-    //
-    List<AnalysisErrorFixes>? errorFixesList;
-    while (errorFixesList == null) {
-      try {
-        errorFixesList = await _computeServerErrorFixes(request, file, offset);
-      } on InconsistentAnalysisException {
-        // Loop around to try again to compute the fixes.
-      }
-    }
-    //
-    // Add the fixes produced by plugins to the server-generated fixes.
-    //
-    var responses =
-        await waitForResponses(pluginFutures, requestParameters: requestParams);
-    server.requestStatistics?.addItemTimeNow(request, 'pluginResponses');
-    var converter = ResultConverter();
-    for (var response in responses) {
-      var result = plugin.EditGetFixesResult.fromResponse(response);
-      errorFixesList
-          .addAll(result.fixes.map(converter.convertAnalysisErrorFixes));
-    }
-    //
-    // Send the response.
-    //
-    server.sendResponse(
-        EditGetFixesResult(errorFixesList).toResponse(request.id));
-  }
-
-  Future getPostfixCompletion(Request request) async {
-    server.options.analytics?.sendEvent('edit', 'getPostfixCompletion');
-
-    var params = EditGetPostfixCompletionParams.fromRequest(request);
-    var file = params.file;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    SourceChange? change;
-
-    var result = await server.getResolvedUnit(file);
-    if (result != null) {
-      var context = PostfixCompletionContext(
-        result,
-        params.offset,
-        params.key,
-      );
-      var processor = PostfixCompletionProcessor(context);
-      var completion = await processor.compute();
-      change = completion.change;
-    }
-    change ??= SourceChange('', edits: []);
-
-    var response =
-        EditGetPostfixCompletionResult(change).toResponse(request.id);
-    server.sendResponse(response);
-  }
-
-  Future getStatementCompletion(Request request) async {
-    var params = EditGetStatementCompletionParams.fromRequest(request);
-    var file = params.file;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    SourceChange? change;
-
-    var result = await server.getResolvedUnit(file);
-    if (result != null) {
-      var context = StatementCompletionContext(result, params.offset);
-      var processor = StatementCompletionProcessor(context);
-      var completion = await processor.compute();
-      change = completion.change;
-    }
-    change ??= SourceChange('', edits: []);
-
-    var response =
-        EditGetStatementCompletionResult(change, false).toResponse(request.id);
-    server.sendResponse(response);
-  }
-
   @override
   Response? handleRequest(
       Request request, CancellationToken cancellationToken) {
     try {
       var requestName = request.method;
       if (requestName == EDIT_REQUEST_FORMAT) {
-        return format(request);
+        EditFormatHandler(server, request, cancellationToken).handle();
+        return Response.DELAYED_RESPONSE;
+      } else if (requestName == EDIT_REQUEST_FORMAT_IF_ENABLED) {
+        EditFormatIfEnabledHandler(server, request, cancellationToken).handle();
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_GET_ASSISTS) {
-        getAssists(request);
+        EditGetAssistsHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS) {
-        return _getAvailableRefactorings(request);
+        _getAvailableRefactorings(request);
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_BULK_FIXES) {
-        bulkFixes(request);
+        EditBulkFixes(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_GET_FIXES) {
-        getFixes(request);
+        EditGetFixesHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_GET_REFACTORING) {
         return _getRefactoring(request, cancellationToken);
       } else if (requestName == EDIT_REQUEST_IMPORT_ELEMENTS) {
-        importElements(request);
+        EditImportElementsHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_ORGANIZE_DIRECTIVES) {
-        organizeDirectives(request);
+        EditOrganizeDirectivesHandler(server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_SORT_MEMBERS) {
-        sortMembers(request);
+        EditSortMembersHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_GET_STATEMENT_COMPLETION) {
-        getStatementCompletion(request);
+        EditGetStatementCompletionHandler(server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_IS_POSTFIX_COMPLETION_APPLICABLE) {
-        isPostfixCompletionApplicable(request);
+        EditIsPostfixCompletionApplicableHandler(
+                server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_GET_POSTFIX_COMPLETION) {
-        getPostfixCompletion(request);
+        EditGetPostfixCompletionHandler(server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName ==
           EDIT_REQUEST_LIST_POSTFIX_COMPLETION_TEMPLATES) {
-        return listPostfixCompletionTemplates(request);
+        EditListPostfixCompletionTemplatesHandler(
+                server, request, cancellationToken)
+            .handle();
+        return Response.DELAYED_RESPONSE;
       }
     } on RequestFailure catch (exception) {
       return exception.response;
@@ -385,412 +118,7 @@
     return null;
   }
 
-  /// Implement the `edit.importElements` request.
-  Future<void> importElements(Request request) async {
-    var params = EditImportElementsParams.fromRequest(request);
-    var file = params.file;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    //
-    // Prepare the resolved unit.
-    //
-    var result = await server.getResolvedUnit(file);
-    if (result == null) {
-      server.sendResponse(Response.importElementsInvalidFile(request));
-      return;
-    }
-    var libraryUnit = result.libraryElement.definingCompilationUnit;
-    if (libraryUnit != result.unit.declaredElement) {
-      // The file in the request is a part of a library. We need to pass the
-      // defining compilation unit to the computer, not the part.
-      result = await server.getResolvedUnit(libraryUnit.source.fullName);
-      if (result == null) {
-        server.sendResponse(Response.importElementsInvalidFile(request));
-        return;
-      }
-    }
-    //
-    // Compute the edits required to import the required elements.
-    //
-    var computer = ImportElementsComputer(server.resourceProvider, result);
-    var change = await computer.createEdits(params.elements);
-    var edits = change.edits;
-    var edit = edits.isEmpty ? null : edits[0];
-    //
-    // Send the response.
-    //
-    server.sendResponse(
-        EditImportElementsResult(edit: edit).toResponse(request.id));
-  }
-
-  Future isPostfixCompletionApplicable(Request request) async {
-    var params = EditGetPostfixCompletionParams.fromRequest(request);
-    var file = params.file;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    var value = false;
-
-    var result = await server.getResolvedUnit(file);
-    if (result != null) {
-      var context = PostfixCompletionContext(
-        result,
-        params.offset,
-        params.key,
-      );
-      var processor = PostfixCompletionProcessor(context);
-      value = await processor.isApplicable();
-    }
-
-    var response =
-        EditIsPostfixCompletionApplicableResult(value).toResponse(request.id);
-    server.sendResponse(response);
-  }
-
-  Response listPostfixCompletionTemplates(Request request) {
-    var templates = DartPostfixCompletion.ALL_TEMPLATES
-        .map((PostfixCompletionKind kind) =>
-            PostfixTemplateDescriptor(kind.name, kind.key, kind.example))
-        .toList();
-
-    return EditListPostfixCompletionTemplatesResult(templates)
-        .toResponse(request.id);
-  }
-
-  Future<void> organizeDirectives(Request request) async {
-    server.options.analytics?.sendEvent('edit', 'organizeDirectives');
-
-    var params = EditOrganizeDirectivesParams.fromRequest(request);
-    var file = params.file;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    var pathContext = server.resourceProvider.pathContext;
-    if (!file_paths.isDart(pathContext, file)) {
-      server.sendResponse(Response.fileNotAnalyzed(request, file));
-      return;
-    }
-
-    // Prepare the file information.
-    var result = await server.getResolvedUnit(file);
-    if (result == null) {
-      server.sendResponse(Response.fileNotAnalyzed(request, file));
-      return;
-    }
-    var fileStamp = -1;
-    var code = result.content;
-    var unit = result.unit;
-    var errors = result.errors;
-    // check if there are scan/parse errors in the file
-    var numScanParseErrors = _getNumberOfScanParseErrors(errors);
-    if (numScanParseErrors != 0) {
-      server.sendResponse(Response.organizeDirectivesError(
-          request, 'File has $numScanParseErrors scan/parse errors.'));
-      return;
-    }
-    // do organize
-    var sorter = ImportOrganizer(code, unit, errors);
-    var edits = sorter.organize();
-    var fileEdit = SourceFileEdit(file, fileStamp, edits: edits);
-    server.sendResponse(
-        EditOrganizeDirectivesResult(fileEdit).toResponse(request.id));
-  }
-
-  Future<void> sortMembers(Request request) async {
-    var params = EditSortMembersParams.fromRequest(request);
-    var file = params.file;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    var pathContext = server.resourceProvider.pathContext;
-    if (!file_paths.isDart(pathContext, file)) {
-      server.sendResponse(Response.sortMembersInvalidFile(request));
-      return;
-    }
-
-    // Prepare the file information.
-    var result = server.getParsedUnit(file);
-    if (result == null) {
-      server.sendResponse(Response.fileNotAnalyzed(request, file));
-      return;
-    }
-
-    var fileStamp = -1;
-    var code = result.content;
-    var unit = result.unit;
-    var errors = result.errors;
-    // Check if there are scan/parse errors in the file.
-    var numScanParseErrors = _getNumberOfScanParseErrors(errors);
-    if (numScanParseErrors != 0) {
-      server.sendResponse(
-          Response.sortMembersParseErrors(request, numScanParseErrors));
-      return;
-    }
-    // Do sort.
-    var sorter = MemberSorter(code, unit, result.lineInfo);
-    var edits = sorter.sort();
-    var fileEdit = SourceFileEdit(file, fileStamp, edits: edits);
-    server.sendResponse(EditSortMembersResult(fileEdit).toResponse(request.id));
-  }
-
-  /// Compute and return the fixes associated with server-generated errors in
-  /// analysis options files.
-  Future<List<AnalysisErrorFixes>> _computeAnalysisOptionsFixes(
-      String file, int offset) async {
-    var errorFixesList = <AnalysisErrorFixes>[];
-    var resourceProvider = server.resourceProvider;
-    var optionsFile = resourceProvider.getFile(file);
-    var content = _safelyRead(optionsFile);
-    if (content == null) {
-      return errorFixesList;
-    }
-    var driver = server.getAnalysisDriver(file);
-    if (driver == null) {
-      return errorFixesList;
-    }
-    var session = driver.currentSession;
-    var sourceFactory = driver.sourceFactory;
-    var errors = analyzeAnalysisOptions(
-      optionsFile.createSource(),
-      content,
-      sourceFactory,
-      driver.currentSession.analysisContext.contextRoot.root.path,
-    );
-    var options = _getOptions(sourceFactory, content);
-    if (options == null) {
-      return errorFixesList;
-    }
-    for (var error in errors) {
-      var generator = AnalysisOptionsFixGenerator(
-          resourceProvider, error, content, options);
-      var fixes = await generator.computeFixes();
-      if (fixes.isNotEmpty) {
-        fixes.sort(Fix.SORT_BY_RELEVANCE);
-        var lineInfo = LineInfo.fromContent(content);
-        var result = engine.ErrorsResultImpl(
-            session, file, Uri.file(file), lineInfo, false, errors);
-        var serverError = newAnalysisError_fromEngine(result, error);
-        var errorFixes = AnalysisErrorFixes(serverError);
-        errorFixesList.add(errorFixes);
-        fixes.forEach((fix) {
-          errorFixes.fixes.add(fix.change);
-        });
-      }
-    }
-    return errorFixesList;
-  }
-
-  /// Compute and return the fixes associated with server-generated errors in
-  /// Dart files.
-  Future<List<AnalysisErrorFixes>> _computeDartFixes(
-      Request request, String file, int offset) async {
-    var errorFixesList = <AnalysisErrorFixes>[];
-    var result = await server.getResolvedUnit(file);
-    server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
-    if (result != null) {
-      var lineInfo = result.lineInfo;
-      var requestLine = lineInfo.getLocation(offset).lineNumber;
-      for (var error in result.errors) {
-        var errorLine = lineInfo.getLocation(error.offset).lineNumber;
-        if (errorLine == requestLine) {
-          var workspace = DartChangeWorkspace(server.currentSessions);
-          var context = DartFixContextImpl(
-              server.instrumentationService, workspace, result, error);
-
-          List<Fix> fixes;
-          try {
-            fixes = await DartFixContributor().computeFixes(context);
-          } on InconsistentAnalysisException {
-            fixes = [];
-          } catch (exception, stackTrace) {
-            var parametersFile = '''
-offset: $offset
-error: $error
-error.errorCode: ${error.errorCode}
-''';
-            throw CaughtExceptionWithFiles(exception, stackTrace, {
-              file: result.content,
-              'parameters': parametersFile,
-            });
-          }
-
-          if (fixes.isNotEmpty) {
-            fixes.sort(Fix.SORT_BY_RELEVANCE);
-            var serverError = newAnalysisError_fromEngine(result, error);
-            var errorFixes = AnalysisErrorFixes(serverError);
-            errorFixesList.add(errorFixes);
-            fixes.forEach((fix) {
-              errorFixes.fixes.add(fix.change);
-            });
-          }
-        }
-      }
-    }
-    server.requestStatistics?.addItemTimeNow(request, 'computedFixes');
-    return errorFixesList;
-  }
-
-  /// Compute and return the fixes associated with server-generated errors in
-  /// Android manifest files.
-  Future<List<AnalysisErrorFixes>> _computeManifestFixes(
-      String file, int offset) async {
-    var errorFixesList = <AnalysisErrorFixes>[];
-    var manifestFile = server.resourceProvider.getFile(file);
-    var content = _safelyRead(manifestFile);
-    if (content == null) {
-      return errorFixesList;
-    }
-    var document =
-        parseFragment(content, container: MANIFEST_TAG, generateSpans: true);
-    var validator = ManifestValidator(manifestFile.createSource());
-    var driver = server.getAnalysisDriver(file);
-    if (driver == null) {
-      return errorFixesList;
-    }
-    var session = driver.currentSession;
-    var errors = validator.validate(content, true);
-    for (var error in errors) {
-      var generator = ManifestFixGenerator(error, content, document);
-      var fixes = await generator.computeFixes();
-      if (fixes.isNotEmpty) {
-        fixes.sort(Fix.SORT_BY_RELEVANCE);
-        var lineInfo = LineInfo.fromContent(content);
-        var result = engine.ErrorsResultImpl(
-            session, file, Uri.file(file), lineInfo, false, errors);
-        var serverError = newAnalysisError_fromEngine(result, error);
-        var errorFixes = AnalysisErrorFixes(serverError);
-        errorFixesList.add(errorFixes);
-        fixes.forEach((fix) {
-          errorFixes.fixes.add(fix.change);
-        });
-      }
-    }
-    return errorFixesList;
-  }
-
-  /// Compute and return the fixes associated with server-generated errors in
-  /// pubspec.yaml files.
-  Future<List<AnalysisErrorFixes>> _computePubspecFixes(
-      String file, int offset) async {
-    var errorFixesList = <AnalysisErrorFixes>[];
-    var resourceProvider = server.resourceProvider;
-    var pubspecFile = resourceProvider.getFile(file);
-    var content = _safelyRead(pubspecFile);
-    if (content == null) {
-      return errorFixesList;
-    }
-    var driver = server.getAnalysisDriver(file);
-    if (driver == null) {
-      return errorFixesList;
-    }
-    YamlDocument document;
-    try {
-      document = loadYamlDocument(content);
-    } catch (exception) {
-      return errorFixesList;
-    }
-    var yamlContent = document.contents;
-    if (yamlContent is! YamlMap) {
-      yamlContent = YamlMap();
-    }
-    var validator =
-        PubspecValidator(resourceProvider, pubspecFile.createSource());
-    var session = driver.currentSession;
-    var errors = validator.validate(yamlContent.nodes);
-    for (var error in errors) {
-      var generator =
-          PubspecFixGenerator(resourceProvider, error, content, document);
-      var fixes = await generator.computeFixes();
-      if (fixes.isNotEmpty) {
-        fixes.sort(Fix.SORT_BY_RELEVANCE);
-        var lineInfo = LineInfo.fromContent(content);
-        var result = engine.ErrorsResultImpl(
-            session, file, Uri.file(file), lineInfo, false, errors);
-        var serverError = newAnalysisError_fromEngine(result, error);
-        var errorFixes = AnalysisErrorFixes(serverError);
-        errorFixesList.add(errorFixes);
-        fixes.forEach((fix) {
-          errorFixes.fixes.add(fix.change);
-        });
-      }
-    }
-    return errorFixesList;
-  }
-
-  Future<List<SourceChange>> _computeServerAssists(
-      Request request, String file, int offset, int length) async {
-    var changes = <SourceChange>[];
-
-    var result = await server.getResolvedUnit(file);
-    server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
-
-    if (result != null) {
-      var context = DartAssistContextImpl(
-        server.instrumentationService,
-        DartChangeWorkspace(server.currentSessions),
-        result,
-        offset,
-        length,
-      );
-
-      try {
-        var processor = AssistProcessor(context);
-        var assists = await processor.compute();
-        assists.sort(Assist.SORT_BY_RELEVANCE);
-        for (var assist in assists) {
-          changes.add(assist.change);
-        }
-      } on InconsistentAnalysisException {
-        // ignore
-      } catch (exception, stackTrace) {
-        var parametersFile = '''
-offset: $offset
-length: $length
-      ''';
-        throw CaughtExceptionWithFiles(exception, stackTrace, {
-          file: result.content,
-          'parameters': parametersFile,
-        });
-      }
-
-      server.requestStatistics?.addItemTimeNow(request, 'computedAssists');
-    }
-
-    return changes;
-  }
-
-  /// Compute and return the fixes associated with server-generated errors.
-  Future<List<AnalysisErrorFixes>> _computeServerErrorFixes(
-      Request request, String file, int offset) async {
-    var pathContext = server.resourceProvider.pathContext;
-    if (file_paths.isDart(pathContext, file)) {
-      return _computeDartFixes(request, file, offset);
-    } else if (file_paths.isAnalysisOptionsYaml(pathContext, file)) {
-      return _computeAnalysisOptionsFixes(file, offset);
-    } else if (file_paths.isPubspecYaml(pathContext, file)) {
-      return _computePubspecFixes(file, offset);
-    } else if (file_paths.isAndroidManifestXml(pathContext, file)) {
-      // TODO(brianwilkerson) Do we need to check more than the file name?
-      return _computeManifestFixes(file, offset);
-    }
-    return <AnalysisErrorFixes>[];
-  }
-
-  Response _getAvailableRefactorings(Request request) {
-    _getAvailableRefactoringsImpl(request);
-    return Response.DELAYED_RESPONSE;
-  }
-
-  Future _getAvailableRefactoringsImpl(Request request) async {
+  Future<void> _getAvailableRefactorings(Request request) async {
     var params = EditGetAvailableRefactoringsParams.fromRequest(request);
     var file = params.file;
     var offset = params.offset;
@@ -857,15 +185,6 @@
     server.sendResponse(result.toResponse(request.id));
   }
 
-  YamlMap? _getOptions(SourceFactory sourceFactory, String content) {
-    var optionsProvider = AnalysisOptionsProvider(sourceFactory);
-    try {
-      return optionsProvider.getOptionsFromString(content);
-    } on OptionsFormatException {
-      return null;
-    }
-  }
-
   Response _getRefactoring(
       Request request, CancellationToken cancellationToken) {
     final refactoringManager = this.refactoringManager;
@@ -887,27 +206,6 @@
       refactoringManager = _RefactoringManager(server, refactoringWorkspace);
     }
   }
-
-  /// Return the contents of the [file], or `null` if the file does not exist or
-  /// cannot be read.
-  String? _safelyRead(File file) {
-    try {
-      return file.readAsStringSync();
-    } on FileSystemException {
-      return null;
-    }
-  }
-
-  static int _getNumberOfScanParseErrors(List<engine.AnalysisError> errors) {
-    var numScanParseErrors = 0;
-    for (var error in errors) {
-      if (error.errorCode is engine.ScannerErrorCode ||
-          error.errorCode is engine.ParserErrorCode) {
-        numScanParseErrors++;
-      }
-    }
-    return numScanParseErrors;
-  }
 }
 
 /// An object managing a single [Refactoring] instance.
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_get_errors.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_get_errors.dart
new file mode 100644
index 0000000..ffad95d
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_get_errors.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analysis.getErrors` request.
+class AnalysisGetErrorsHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalysisGetErrorsHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var file = AnalysisGetErrorsParams.fromRequest(request).file;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    var result = await server.getResolvedUnit(file);
+
+    if (result == null) {
+      sendResponse(Response.getErrorsInvalidFile(request));
+      return;
+    }
+
+    var protocolErrors = doAnalysisError_listFromEngine(result);
+    sendResult(AnalysisGetErrorsResult(protocolErrors));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_get_hover.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_get_hover.dart
new file mode 100644
index 0000000..b3a4cc2
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_get_hover.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/computer/computer_hover.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+
+/// The handler for the `analysis.getHover` request.
+class AnalysisGetHoverHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalysisGetHoverHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = AnalysisGetHoverParams.fromRequest(request);
+    var file = params.file;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    // Prepare the resolved units.
+    var result = await server.getResolvedUnit(file);
+    if (result is! ResolvedUnitResult) {
+      sendResponse(Response.fileNotAnalyzed(request, file));
+      return;
+    }
+    var unit = result.unit;
+
+    // Prepare the hovers.
+    var hovers = <HoverInformation>[];
+    var computer = DartUnitHoverComputer(
+        server.getDartdocDirectiveInfoFor(result), unit, params.offset);
+    var hoverInformation = computer.compute();
+    if (hoverInformation != null) {
+      hovers.add(hoverInformation);
+    }
+
+    // Send the response.
+    sendResult(AnalysisGetHoverResult(hovers));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_get_imported_elements.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_get_imported_elements.dart
new file mode 100644
index 0000000..f7b299a
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_get_imported_elements.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/computer/imported_elements_computer.dart';
+import 'package:analysis_server/src/domain_analysis_flags.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analysis.getImportedElements` request.
+class AnalysisGetImportedElementsHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalysisGetImportedElementsHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = AnalysisGetImportedElementsParams.fromRequest(request);
+    var file = params.file;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    //
+    // Prepare the resolved unit.
+    //
+    var result = await server.getResolvedUnit(file);
+    if (result == null) {
+      sendResponse(Response.getImportedElementsInvalidFile(request));
+      return;
+    }
+
+    List<ImportedElements> elements;
+
+    //
+    // Compute the list of imported elements.
+    //
+    if (disableManageImportsOnPaste) {
+      elements = <ImportedElements>[];
+    } else {
+      elements =
+          ImportedElementsComputer(result.unit, params.offset, params.length)
+              .compute();
+    }
+
+    sendResult(AnalysisGetImportedElementsResult(elements));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_get_navigation.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_get_navigation.dart
new file mode 100644
index 0000000..060bc16
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_get_navigation.dart
@@ -0,0 +1,91 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/domain_abstract.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/plugin/result_merger.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
+import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
+import 'package:analyzer_plugin/utilities/navigation/navigation_dart.dart';
+
+/// The handler for the `analysis.getNavigation` request.
+class AnalysisGetNavigationHandler extends LegacyHandler
+    with RequestHandlerMixin<AnalysisServer> {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalysisGetNavigationHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = AnalysisGetNavigationParams.fromRequest(request);
+    var file = params.file;
+    var offset = params.offset;
+    var length = params.length;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    var driver = server.getAnalysisDriver(file);
+    if (driver == null) {
+      sendResponse(Response.getNavigationInvalidFile(request));
+    } else {
+      //
+      // Allow plugins to start computing navigation data.
+      //
+      var requestParams =
+          plugin.AnalysisGetNavigationParams(file, offset, length);
+      var pluginFutures = server.pluginManager.broadcastRequest(
+        requestParams,
+        contextRoot: driver.analysisContext!.contextRoot,
+      );
+      //
+      // Compute navigation data generated by server.
+      //
+      var allResults = <AnalysisNavigationParams>[];
+      var result = await server.getResolvedUnit(file);
+      if (result != null) {
+        var unit = result.unit;
+        var collector = NavigationCollectorImpl();
+        computeDartNavigation(
+            server.resourceProvider, collector, unit, offset, length);
+        collector.createRegions();
+        allResults.add(AnalysisNavigationParams(
+            file, collector.regions, collector.targets, collector.files));
+      }
+      //
+      // Add the navigation data produced by plugins to the server-generated
+      // navigation data.
+      //
+      var responses = await waitForResponses(pluginFutures,
+          requestParameters: requestParams);
+      for (var response in responses) {
+        var result = plugin.AnalysisGetNavigationResult.fromResponse(response);
+        allResults.add(AnalysisNavigationParams(
+            file, result.regions, result.targets, result.files));
+      }
+      //
+      // Return the result.
+      //
+      var merger = ResultMerger();
+      var mergedResults = merger.mergeNavigation(allResults);
+      if (mergedResults == null) {
+        sendResult(AnalysisGetNavigationResult(
+            <String>[], <NavigationTarget>[], <NavigationRegion>[]));
+      } else {
+        sendResult(AnalysisGetNavigationResult(
+            mergedResults.files, mergedResults.targets, mergedResults.regions));
+      }
+    }
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_get_signature.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_get_signature.dart
new file mode 100644
index 0000000..4b093bb
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_get_signature.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/computer/computer_signature.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analysis.getSignature` request.
+class AnalysisGetSignatureHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalysisGetSignatureHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = AnalysisGetSignatureParams.fromRequest(request);
+    var file = params.file;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    // Prepare the resolved units.
+    var result = await server.getResolvedUnit(file);
+
+    if (result == null || !result.exists) {
+      sendResponse(Response.getSignatureInvalidFile(request));
+      return;
+    }
+
+    // Ensure the offset provided is a valid location in the file.
+    final unit = result.unit;
+    final computer = DartUnitSignatureComputer(
+        server.getDartdocDirectiveInfoFor(result), unit, params.offset);
+    if (!computer.offsetIsValid) {
+      sendResponse(Response.getSignatureInvalidOffset(request));
+      return;
+    }
+
+    // Try to get a signature.
+    final signature = computer.compute();
+    if (signature == null) {
+      sendResponse(Response.getSignatureUnknownFunction(request));
+      return;
+    }
+
+    sendResult(signature);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart
new file mode 100644
index 0000000..0473c34
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analysis.reanalyze` request.
+class AnalysisReanalyzeHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalysisReanalyzeHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    server.options.analytics?.sendEvent('analysis', 'reanalyze');
+
+    await server.reanalyze();
+    //
+    // Restart all of the plugins. This is an async operation that will happen
+    // in the background.
+    //
+    server.pluginManager.restartPlugins();
+
+    sendResult(AnalysisReanalyzeResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart
new file mode 100644
index 0000000..5eafdf0
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analysis.setAnalysisRoots` request.
+class AnalysisSetAnalysisRootsHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalysisSetAnalysisRootsHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = AnalysisSetAnalysisRootsParams.fromRequest(request);
+    var includedPathList = params.included;
+    var excludedPathList = params.excluded;
+
+    server.options.analytics?.sendEvent('analysis', 'setAnalysisRoots',
+        value: includedPathList.length);
+
+    // validate
+    for (var path in includedPathList) {
+      if (!server.isValidFilePath(path)) {
+        sendResponse(Response.invalidFilePathFormat(request, path));
+        return;
+      }
+    }
+    for (var path in excludedPathList) {
+      if (!server.isValidFilePath(path)) {
+        sendResponse(Response.invalidFilePathFormat(request, path));
+        return;
+      }
+    }
+
+    var detachableFileSystemManager = server.detachableFileSystemManager;
+    if (detachableFileSystemManager != null) {
+      // TODO(scheglov) remove the last argument
+      detachableFileSystemManager
+          .setAnalysisRoots(request.id, includedPathList, excludedPathList, {});
+    } else {
+      await server.setAnalysisRoots(
+          request.id, includedPathList, excludedPathList);
+    }
+    sendResult(AnalysisSetAnalysisRootsResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_set_general_subscriptions.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_general_subscriptions.dart
new file mode 100644
index 0000000..1d02f73
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_general_subscriptions.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analysis.setGeneralSubscriptions` request.
+class AnalysisSetGeneralSubscriptionsHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalysisSetGeneralSubscriptionsHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = AnalysisSetGeneralSubscriptionsParams.fromRequest(request);
+    server.setGeneralAnalysisSubscriptions(params.subscriptions);
+    sendResult(AnalysisSetGeneralSubscriptionsResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_set_priority_files.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_priority_files.dart
new file mode 100644
index 0000000..9cbe6bd
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_priority_files.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/plugin/request_converter.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analysis.setPriorityFiles` request.
+class AnalysisSetPriorityFilesHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalysisSetPriorityFilesHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = AnalysisSetPriorityFilesParams.fromRequest(request);
+
+    for (var file in params.files) {
+      if (!server.isAbsoluteAndNormalized(file)) {
+        sendResponse(Response.invalidFilePathFormat(request, file));
+      }
+    }
+
+    server.setPriorityFiles(request.id, params.files);
+    //
+    // Forward the request to the plugins.
+    //
+    var converter = RequestConverter();
+    server.pluginManager.setAnalysisSetPriorityFilesParams(
+        converter.convertAnalysisSetPriorityFilesParams(params));
+    //
+    // Send the response.
+    //
+    sendResult(AnalysisSetPriorityFilesResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_set_subscriptions.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_subscriptions.dart
new file mode 100644
index 0000000..bce48cf
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_subscriptions.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/plugin/request_converter.dart';
+import 'package:analysis_server/src/protocol/protocol_internal.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analysis.setSubscriptions` request.
+class AnalysisSetSubscriptionsHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalysisSetSubscriptionsHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = AnalysisSetSubscriptionsParams.fromRequest(request);
+
+    for (var fileList in params.subscriptions.values) {
+      for (var file in fileList) {
+        if (!server.isAbsoluteAndNormalized(file)) {
+          sendResponse(Response.invalidFilePathFormat(request, file));
+        }
+      }
+    }
+
+    // parse subscriptions
+    var subMap =
+        mapMap<AnalysisService, List<String>, AnalysisService, Set<String>>(
+            params.subscriptions,
+            valueCallback: (List<String> subscriptions) =>
+                subscriptions.toSet());
+    server.setAnalysisSubscriptions(subMap);
+    //
+    // Forward the request to the plugins.
+    //
+    var converter = RequestConverter();
+    server.pluginManager.setAnalysisSetSubscriptionsParams(
+        converter.convertAnalysisSetSubscriptionsParams(params));
+    //
+    // Send the response.
+    //
+    sendResult(AnalysisSetSubscriptionsResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analytics_enable.dart b/pkg/analysis_server/lib/src/handler/legacy/analytics_enable.dart
new file mode 100644
index 0000000..5b5a927
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analytics_enable.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analytics.enable` request.
+class AnalyticsEnableHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalyticsEnableHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = AnalyticsEnableParams.fromRequest(request);
+    final analytics = server.analytics;
+    if (analytics != null) {
+      analytics.enabled = params.value;
+    }
+    sendResult(AnalyticsEnableResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analytics_is_enabled.dart b/pkg/analysis_server/lib/src/handler/legacy/analytics_is_enabled.dart
new file mode 100644
index 0000000..a4d3bfb
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analytics_is_enabled.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analytics.isEnabled` request.
+class AnalyticsIsEnabledHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalyticsIsEnabledHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    sendResult(AnalyticsIsEnabledResult(server.analytics?.enabled ?? false));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart
new file mode 100644
index 0000000..ea1aee4
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analytics.sendEvent` request.
+class AnalyticsSendEventHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalyticsSendEventHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  String get _clientId => server.options.clientId ?? 'client';
+
+  @override
+  Future<void> handle() async {
+    final analytics = server.analytics;
+    if (analytics == null) {
+      sendResult(AnalyticsSendEventResult());
+      return;
+    }
+
+    var params = AnalyticsSendEventParams.fromRequest(request);
+    analytics.sendEvent(_clientId, params.action);
+    sendResult(AnalyticsSendEventResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart
new file mode 100644
index 0000000..bb9961e
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `analytics.sendTiming` request.
+class AnalyticsSendTimingHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  AnalyticsSendTimingHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  String get _clientId => server.options.clientId ?? 'client';
+
+  @override
+  Future<void> handle() async {
+    final analytics = server.analytics;
+    if (analytics == null) {
+      sendResult(AnalyticsSendTimingResult());
+      return;
+    }
+
+    var params = AnalyticsSendTimingParams.fromRequest(request);
+    analytics.sendTiming(params.event, params.millis, category: _clientId);
+    sendResult(AnalyticsSendTimingResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestion_details.dart b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestion_details.dart
new file mode 100644
index 0000000..e531347
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestion_details.dart
@@ -0,0 +1,93 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// The handler for the `completion.getSuggestionDetails` request.
+class CompletionGetSuggestionDetailsHandler extends LegacyHandler {
+  /// The identifiers of the latest `getSuggestionDetails` request.
+  /// We use it to abort previous requests.
+  int _latestGetSuggestionDetailsId = 0;
+
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  CompletionGetSuggestionDetailsHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = CompletionGetSuggestionDetailsParams.fromRequest(request);
+
+    var file = params.file;
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    var libraryId = params.id;
+    var declarationsTracker = server.declarationsTracker;
+    if (declarationsTracker == null) {
+      sendResponse(Response.unsupportedFeature(
+          request.id, 'Completion is not enabled.'));
+      return;
+    }
+    var library = declarationsTracker.getLibrary(libraryId);
+    if (library == null) {
+      sendResponse(Response.invalidParameter(
+        request,
+        'libraryId',
+        'No such library: $libraryId',
+      ));
+      return;
+    }
+
+    // The label might be `MyEnum.myValue`, but we import only `MyEnum`.
+    var requestedName = params.label;
+    if (requestedName.contains('.')) {
+      requestedName = requestedName.substring(
+        0,
+        requestedName.indexOf('.'),
+      );
+    }
+
+    const timeout = Duration(milliseconds: 1000);
+    var timer = Stopwatch()..start();
+    var id = ++_latestGetSuggestionDetailsId;
+    while (id == _latestGetSuggestionDetailsId && timer.elapsed < timeout) {
+      try {
+        var session = await server.getAnalysisSession(file);
+        if (session == null) {
+          sendResponse(Response.fileNotAnalyzed(request, 'libraryId'));
+          return;
+        }
+
+        var completion = params.label;
+        var builder = ChangeBuilder(session: session);
+        await builder.addDartFileEdit(file, (builder) {
+          var result = builder.importLibraryElement(library.uri);
+          if (result.prefix != null) {
+            completion = '${result.prefix}.$completion';
+          }
+        });
+
+        sendResult(CompletionGetSuggestionDetailsResult(completion,
+            change: builder.sourceChange));
+        return;
+      } on InconsistentAnalysisException {
+        // Loop around to try again.
+      }
+    }
+
+    // Timeout or abort, send the empty response.
+    sendResult(CompletionGetSuggestionDetailsResult(''));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestion_details2.dart b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestion_details2.dart
new file mode 100644
index 0000000..2cdf656
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestion_details2.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// The handler for the `completion.getSuggestionDetails2` request.
+class CompletionGetSuggestionDetails2Handler extends LegacyHandler {
+  /// The identifiers of the latest `getSuggestionDetails2` request.
+  /// We use it to abort previous requests.
+  int _latestGetSuggestionDetailsId = 0;
+
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  CompletionGetSuggestionDetails2Handler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = CompletionGetSuggestionDetails2Params.fromRequest(request);
+
+    var file = params.file;
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    var libraryUri = Uri.tryParse(params.libraryUri);
+    if (libraryUri == null) {
+      sendResponse(
+        Response.invalidParameter(request, 'libraryUri', 'Cannot parse'),
+      );
+      return;
+    }
+
+    var budget = CompletionBudget(
+      const Duration(milliseconds: 1000),
+    );
+    var id = ++_latestGetSuggestionDetailsId;
+    while (id == _latestGetSuggestionDetailsId && !budget.isEmpty) {
+      try {
+        var session = await server.getAnalysisSession(file);
+        if (session == null) {
+          sendResponse(Response.fileNotAnalyzed(request, file));
+          return;
+        }
+
+        var completion = params.completion;
+        var builder = ChangeBuilder(session: session);
+        await builder.addDartFileEdit(file, (builder) {
+          var result = builder.importLibraryElement(libraryUri);
+          if (result.prefix != null) {
+            completion = '${result.prefix}.$completion';
+          }
+        });
+
+        sendResult(CompletionGetSuggestionDetails2Result(
+          completion,
+          builder.sourceChange,
+        ));
+        return;
+      } on InconsistentAnalysisException {
+        // Loop around to try again.
+      }
+    }
+
+    // Timeout or abort, send the empty response.
+    sendResult(CompletionGetSuggestionDetailsResult(''));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/diagnostic_get_diagnostics.dart b/pkg/analysis_server/lib/src/handler/legacy/diagnostic_get_diagnostics.dart
new file mode 100644
index 0000000..678db0f
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/diagnostic_get_diagnostics.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
+
+/// The handler for the `diagnostic.getDiagnostics` request.
+class DiagnosticGetDiagnosticsHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  DiagnosticGetDiagnosticsHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var contexts = server.driverMap.values.map(_extractDataFromDriver).toList();
+    sendResult(DiagnosticGetDiagnosticsResult(contexts));
+  }
+
+  /// Extract context data from the given [driver].
+  ContextData _extractDataFromDriver(AnalysisDriver driver) {
+    var explicitFileCount = driver.addedFiles.length;
+    var knownFileCount = driver.knownFiles.length;
+    return ContextData(driver.name, explicitFileCount,
+        knownFileCount - explicitFileCount, driver.numberOfFilesToAnalyze, []);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/diagnostic_get_server_port.dart b/pkg/analysis_server/lib/src/handler/legacy/diagnostic_get_server_port.dart
new file mode 100644
index 0000000..e76abac
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/diagnostic_get_server_port.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `diagnostic.getServerPort` request.
+class DiagnosticGetServerPortHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  DiagnosticGetServerPortHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    try {
+      // Open a port (or return the existing one).
+      var port = await server.diagnosticServer!.getServerPort();
+      sendResult(DiagnosticGetServerPortResult(port));
+    } catch (error) {
+      sendResponse(Response.debugPortCouldNotBeOpened(request, error));
+    }
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_bulk_fixes.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_bulk_fixes.dart
new file mode 100644
index 0000000..6e2a361
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_bulk_fixes.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/services/correction/bulk_fix_processor.dart';
+import 'package:analysis_server/src/services/correction/change_workspace.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/exception/exception.dart';
+import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
+
+/// The handler for the `edit.bulkFixes` request.
+class EditBulkFixes extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditBulkFixes(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    //
+    // Compute bulk fixes
+    //
+    try {
+      var params = EditBulkFixesParams.fromRequest(request);
+      for (var file in params.included) {
+        if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+          return;
+        }
+      }
+
+      var collection = AnalysisContextCollectionImpl(
+        includedPaths: params.included,
+        resourceProvider: server.resourceProvider,
+        sdkPath: server.sdkPath,
+      );
+      var workspace = DartChangeWorkspace(
+          collection.contexts.map((c) => c.currentSession).toList());
+      var processor = BulkFixProcessor(server.instrumentationService, workspace,
+          useConfigFiles: params.inTestMode ?? false);
+
+      var changeBuilder = await processor.fixErrors(collection.contexts);
+
+      sendResult(EditBulkFixesResult(
+          changeBuilder.sourceChange.edits, processor.fixDetails));
+    } catch (exception, stackTrace) {
+      // TODO(brianwilkerson) Move exception handling outside [handle].
+      server.sendServerErrorNotification('Exception while getting bulk fixes',
+          CaughtException(exception, stackTrace), stackTrace);
+    }
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
new file mode 100644
index 0000000..ca6d691
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:dart_style/src/dart_formatter.dart';
+import 'package:dart_style/src/exceptions.dart';
+import 'package:dart_style/src/source_code.dart';
+
+/// The handler for the `edit.format` request.
+class EditFormatHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditFormatHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    server.options.analytics?.sendEvent('edit', 'format');
+
+    var params = EditFormatParams.fromRequest(request);
+    var file = params.file;
+//
+    String unformattedCode;
+    try {
+      var resource = server.resourceProvider.getFile(file);
+      unformattedCode = resource.readAsStringSync();
+    } catch (e) {
+      sendResponse(Response.formatInvalidFile(request));
+      return;
+    }
+
+    int? start = params.selectionOffset;
+    int? length = params.selectionLength;
+
+    // No need to preserve 0,0 selection
+    if (start == 0 && length == 0) {
+      start = null;
+      length = null;
+    }
+
+    var code = SourceCode(unformattedCode,
+        uri: null,
+        isCompilationUnit: true,
+        selectionStart: start,
+        selectionLength: length);
+    var formatter = DartFormatter(pageWidth: params.lineLength);
+    SourceCode formattedResult;
+    try {
+      formattedResult = formatter.formatSource(code);
+    } on FormatterException {
+      sendResponse(Response.formatWithErrors(request));
+      return;
+    }
+    var formattedSource = formattedResult.text;
+
+    var edits = <SourceEdit>[];
+
+    if (formattedSource != unformattedCode) {
+      //TODO: replace full replacements with smaller, more targeted edits
+      var edit = SourceEdit(0, unformattedCode.length, formattedSource);
+      edits.add(edit);
+    }
+
+    var newStart = formattedResult.selectionStart;
+    var newLength = formattedResult.selectionLength;
+
+    // Sending null start/length values would violate protocol, so convert back
+    // to 0.
+    newStart ??= 0;
+    newLength ??= 0;
+
+    sendResult(EditFormatResult(edits, newStart, newLength));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_format_if_enabled.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_format_if_enabled.dart
new file mode 100644
index 0000000..5e404d4
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_format_if_enabled.dart
@@ -0,0 +1,87 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
+import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:dart_style/src/dart_formatter.dart';
+import 'package:dart_style/src/exceptions.dart';
+import 'package:dart_style/src/source_code.dart';
+
+/// The handler for the `edit.formatIfEnabled` request.
+class EditFormatIfEnabledHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditFormatIfEnabledHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  /// Format the file at the given [filePath].
+  ///
+  /// Throws a [FileSystemException] if the file doesn't exist or can't be read.
+  /// Throws a [FormatterException] if the code could not be formatted.
+  List<SourceEdit> formatFile(String filePath) {
+    // TODO(brianwilkerson) Move this to a superclass when `edit.format` is
+    //  implemented by a handler class so the code can be shared.
+    var resource = server.resourceProvider.getFile(filePath);
+    var originalContent = resource.readAsStringSync();
+    var code = SourceCode(originalContent, uri: null, isCompilationUnit: true);
+
+    var formatter = DartFormatter();
+    var formatResult = formatter.formatSource(code);
+    var formattedContent = formatResult.text;
+
+    var edits = <SourceEdit>[];
+    if (formattedContent != originalContent) {
+      // TODO(brianwilkerson) Replace full replacements with smaller, more
+      //  targeted edits.
+      var edit = SourceEdit(0, originalContent.length, formattedContent);
+      edits.add(edit);
+    }
+    return edits;
+  }
+
+  @override
+  Future<void> handle() async {
+    var params = EditFormatIfEnabledParams.fromRequest(request);
+    var collection = AnalysisContextCollectionImpl(
+      includedPaths: params.directories,
+      resourceProvider: server.resourceProvider,
+      sdkPath: server.sdkPath,
+    );
+    var sourceFileEdits = <SourceFileEdit>[];
+    for (var context in collection.contexts) {
+      if (context.analysisOptions.codeStyleOptions.useFormatter) {
+        _formatInContext(context, sourceFileEdits);
+      }
+    }
+    sendResult(EditFormatIfEnabledResult(sourceFileEdits));
+  }
+
+  /// Format all of the files in the given [context], adding the edits to the
+  /// list of [sourceFileEdits].
+  void _formatInContext(DriverBasedAnalysisContext context,
+      List<SourceFileEdit> sourceFileEdits) {
+    for (var filePath in context.contextRoot.analyzedFiles()) {
+      var pathContext = context.resourceProvider.pathContext;
+      if (file_paths.isDart(pathContext, filePath)) {
+        try {
+          var sourceEdits = formatFile(filePath);
+          if (sourceEdits.isNotEmpty) {
+            sourceFileEdits
+                .add(SourceFileEdit(filePath, 0, edits: sourceEdits));
+          }
+        } catch (exception) {
+          // Ignore files that can't be formatted.
+        }
+      }
+    }
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_assists.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_assists.dart
new file mode 100644
index 0000000..5d76278
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_assists.dart
@@ -0,0 +1,129 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/plugin/edit/assist/assist_core.dart';
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/domain_abstract.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/plugin/plugin_manager.dart';
+import 'package:analysis_server/src/plugin/result_converter.dart';
+import 'package:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/assist_internal.dart';
+import 'package:analysis_server/src/services/correction/change_workspace.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer/src/exception/exception.dart';
+import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
+
+/// The handler for the `edit.getAssists` request.
+class EditGetAssistsHandler extends LegacyHandler
+    with RequestHandlerMixin<AnalysisServer> {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditGetAssistsHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = EditGetAssistsParams.fromRequest(request);
+    var file = params.file;
+    var offset = params.offset;
+    var length = params.length;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+    //
+    // Allow plugins to start computing assists.
+    //
+    Map<PluginInfo, Future<plugin.Response>> pluginFutures;
+    var requestParams = plugin.EditGetAssistsParams(file, offset, length);
+    var driver = server.getAnalysisDriver(file);
+    if (driver == null) {
+      pluginFutures = <PluginInfo, Future<plugin.Response>>{};
+    } else {
+      pluginFutures = server.pluginManager.broadcastRequest(
+        requestParams,
+        contextRoot: driver.analysisContext!.contextRoot,
+      );
+    }
+
+    //
+    // Compute fixes associated with server-generated errors.
+    //
+    var changes = await _computeServerAssists(request, file, offset, length);
+
+    //
+    // Add the fixes produced by plugins to the server-generated fixes.
+    //
+    var responses =
+        await waitForResponses(pluginFutures, requestParameters: requestParams);
+    server.requestStatistics?.addItemTimeNow(request, 'pluginResponses');
+    var converter = ResultConverter();
+    var pluginChanges = <plugin.PrioritizedSourceChange>[];
+    for (var response in responses) {
+      var result = plugin.EditGetAssistsResult.fromResponse(response);
+      pluginChanges.addAll(result.assists);
+    }
+    pluginChanges
+        .sort((first, second) => first.priority.compareTo(second.priority));
+    changes.addAll(pluginChanges.map(converter.convertPrioritizedSourceChange));
+
+    //
+    // Send the response.
+    //
+    sendResult(EditGetAssistsResult(changes));
+  }
+
+  Future<List<SourceChange>> _computeServerAssists(
+      Request request, String file, int offset, int length) async {
+    var changes = <SourceChange>[];
+
+    var result = await server.getResolvedUnit(file);
+    server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
+
+    if (result != null) {
+      var context = DartAssistContextImpl(
+        server.instrumentationService,
+        DartChangeWorkspace(
+          await server.currentSessions,
+        ),
+        result,
+        offset,
+        length,
+      );
+
+      try {
+        var processor = AssistProcessor(context);
+        var assists = await processor.compute();
+        assists.sort(Assist.SORT_BY_RELEVANCE);
+        for (var assist in assists) {
+          changes.add(assist.change);
+        }
+      } on InconsistentAnalysisException {
+        // ignore
+      } catch (exception, stackTrace) {
+        var parametersFile = '''
+offset: $offset
+length: $length
+      ''';
+        throw CaughtExceptionWithFiles(exception, stackTrace, {
+          file: result.content,
+          'parameters': parametersFile,
+        });
+      }
+
+      server.requestStatistics?.addItemTimeNow(request, 'computedAssists');
+    }
+
+    return changes;
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_fixes.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_fixes.dart
new file mode 100644
index 0000000..50595a1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_fixes.dart
@@ -0,0 +1,324 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/domain_abstract.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/plugin/plugin_manager.dart';
+import 'package:analysis_server/src/plugin/result_converter.dart';
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/correction/change_workspace.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/correction/fix/analysis_options/fix_generator.dart';
+import 'package:analysis_server/src/services/correction/fix/manifest/fix_generator.dart';
+import 'package:analysis_server/src/services/correction/fix/pubspec/fix_generator.dart';
+import 'package:analysis_server/src/services/correction/fix_internal.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
+import 'package:analyzer/src/dart/analysis/results.dart' as engine;
+import 'package:analyzer/src/exception/exception.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/manifest/manifest_validator.dart';
+import 'package:analyzer/src/manifest/manifest_values.dart';
+import 'package:analyzer/src/pubspec/pubspec_validator.dart';
+import 'package:analyzer/src/task/options.dart';
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
+import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
+import 'package:html/parser.dart';
+import 'package:yaml/yaml.dart';
+
+/// The handler for the `edit.getFixes` request.
+class EditGetFixesHandler extends LegacyHandler
+    with RequestHandlerMixin<AnalysisServer> {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditGetFixesHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = EditGetFixesParams.fromRequest(request);
+    var file = params.file;
+    var offset = params.offset;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    if (!server.isAnalyzed(file)) {
+      server.sendResponse(Response.getFixesInvalidFile(request));
+      return;
+    }
+
+    //
+    // Allow plugins to start computing fixes.
+    //
+    Map<PluginInfo, Future<plugin.Response>> pluginFutures;
+    var requestParams = plugin.EditGetFixesParams(file, offset);
+    var driver = server.getAnalysisDriver(file);
+    if (driver == null) {
+      pluginFutures = <PluginInfo, Future<plugin.Response>>{};
+    } else {
+      pluginFutures = server.pluginManager.broadcastRequest(
+        requestParams,
+        contextRoot: driver.analysisContext!.contextRoot,
+      );
+    }
+    //
+    // Compute fixes associated with server-generated errors.
+    //
+    List<AnalysisErrorFixes>? errorFixesList;
+    while (errorFixesList == null) {
+      try {
+        errorFixesList = await _computeServerErrorFixes(request, file, offset);
+      } on InconsistentAnalysisException {
+        // Loop around to try again to compute the fixes.
+      }
+    }
+    //
+    // Add the fixes produced by plugins to the server-generated fixes.
+    //
+    var responses =
+        await waitForResponses(pluginFutures, requestParameters: requestParams);
+    server.requestStatistics?.addItemTimeNow(request, 'pluginResponses');
+    var converter = ResultConverter();
+    for (var response in responses) {
+      var result = plugin.EditGetFixesResult.fromResponse(response);
+      errorFixesList
+          .addAll(result.fixes.map(converter.convertAnalysisErrorFixes));
+    }
+    //
+    // Send the response.
+    //
+    sendResult(EditGetFixesResult(errorFixesList));
+  }
+
+  /// Compute and return the fixes associated with server-generated errors in
+  /// analysis options files.
+  Future<List<AnalysisErrorFixes>> _computeAnalysisOptionsFixes(
+      String file, int offset) async {
+    var errorFixesList = <AnalysisErrorFixes>[];
+    var resourceProvider = server.resourceProvider;
+    var optionsFile = resourceProvider.getFile(file);
+    var content = _safelyRead(optionsFile);
+    if (content == null) {
+      return errorFixesList;
+    }
+    var driver = server.getAnalysisDriver(file);
+    if (driver == null) {
+      return errorFixesList;
+    }
+    await driver.applyPendingFileChanges();
+    var session = driver.currentSession;
+    var sourceFactory = driver.sourceFactory;
+    var errors = analyzeAnalysisOptions(
+      optionsFile.createSource(),
+      content,
+      sourceFactory,
+      session.analysisContext.contextRoot.root.path,
+    );
+    var options = _getOptions(sourceFactory, content);
+    if (options == null) {
+      return errorFixesList;
+    }
+    for (var error in errors) {
+      var generator = AnalysisOptionsFixGenerator(
+          resourceProvider, error, content, options);
+      var fixes = await generator.computeFixes();
+      if (fixes.isNotEmpty) {
+        fixes.sort(Fix.SORT_BY_RELEVANCE);
+        var lineInfo = LineInfo.fromContent(content);
+        var result = engine.ErrorsResultImpl(
+            session, file, Uri.file(file), lineInfo, false, errors);
+        var serverError = newAnalysisError_fromEngine(result, error);
+        var errorFixes = AnalysisErrorFixes(serverError);
+        errorFixesList.add(errorFixes);
+        fixes.forEach((fix) {
+          errorFixes.fixes.add(fix.change);
+        });
+      }
+    }
+    return errorFixesList;
+  }
+
+  /// Compute and return the fixes associated with server-generated errors in
+  /// Dart files.
+  Future<List<AnalysisErrorFixes>> _computeDartFixes(
+      Request request, String file, int offset) async {
+    var errorFixesList = <AnalysisErrorFixes>[];
+    var result = await server.getResolvedUnit(file);
+    server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
+    if (result != null) {
+      var lineInfo = result.lineInfo;
+      var requestLine = lineInfo.getLocation(offset).lineNumber;
+      for (var error in result.errors) {
+        var errorLine = lineInfo.getLocation(error.offset).lineNumber;
+        if (errorLine == requestLine) {
+          var workspace = DartChangeWorkspace(
+            await server.currentSessions,
+          );
+          var context = DartFixContextImpl(
+              server.instrumentationService, workspace, result, error);
+
+          List<Fix> fixes;
+          try {
+            fixes = await DartFixContributor().computeFixes(context);
+          } on InconsistentAnalysisException {
+            fixes = [];
+          } catch (exception, stackTrace) {
+            var parametersFile = '''
+offset: $offset
+error: $error
+error.errorCode: ${error.errorCode}
+''';
+            throw CaughtExceptionWithFiles(exception, stackTrace, {
+              file: result.content,
+              'parameters': parametersFile,
+            });
+          }
+
+          if (fixes.isNotEmpty) {
+            fixes.sort(Fix.SORT_BY_RELEVANCE);
+            var serverError = newAnalysisError_fromEngine(result, error);
+            var errorFixes = AnalysisErrorFixes(serverError);
+            errorFixesList.add(errorFixes);
+            fixes.forEach((fix) {
+              errorFixes.fixes.add(fix.change);
+            });
+          }
+        }
+      }
+    }
+    server.requestStatistics?.addItemTimeNow(request, 'computedFixes');
+    return errorFixesList;
+  }
+
+  /// Compute and return the fixes associated with server-generated errors in
+  /// Android manifest files.
+  Future<List<AnalysisErrorFixes>> _computeManifestFixes(
+      String file, int offset) async {
+    var errorFixesList = <AnalysisErrorFixes>[];
+    var manifestFile = server.resourceProvider.getFile(file);
+    var content = _safelyRead(manifestFile);
+    if (content == null) {
+      return errorFixesList;
+    }
+    var document =
+        parseFragment(content, container: MANIFEST_TAG, generateSpans: true);
+    var validator = ManifestValidator(manifestFile.createSource());
+    var session = await server.getAnalysisSession(file);
+    if (session == null) {
+      return errorFixesList;
+    }
+    var errors = validator.validate(content, true);
+    for (var error in errors) {
+      var generator = ManifestFixGenerator(error, content, document);
+      var fixes = await generator.computeFixes();
+      if (fixes.isNotEmpty) {
+        fixes.sort(Fix.SORT_BY_RELEVANCE);
+        var lineInfo = LineInfo.fromContent(content);
+        var result = engine.ErrorsResultImpl(
+            session, file, Uri.file(file), lineInfo, false, errors);
+        var serverError = newAnalysisError_fromEngine(result, error);
+        var errorFixes = AnalysisErrorFixes(serverError);
+        errorFixesList.add(errorFixes);
+        fixes.forEach((fix) {
+          errorFixes.fixes.add(fix.change);
+        });
+      }
+    }
+    return errorFixesList;
+  }
+
+  /// Compute and return the fixes associated with server-generated errors in
+  /// pubspec.yaml files.
+  Future<List<AnalysisErrorFixes>> _computePubspecFixes(
+      String file, int offset) async {
+    var errorFixesList = <AnalysisErrorFixes>[];
+    var resourceProvider = server.resourceProvider;
+    var pubspecFile = resourceProvider.getFile(file);
+    var content = _safelyRead(pubspecFile);
+    if (content == null) {
+      return errorFixesList;
+    }
+    var session = await server.getAnalysisSession(file);
+    if (session == null) {
+      return errorFixesList;
+    }
+    YamlDocument document;
+    try {
+      document = loadYamlDocument(content);
+    } catch (exception) {
+      return errorFixesList;
+    }
+    var yamlContent = document.contents;
+    if (yamlContent is! YamlMap) {
+      yamlContent = YamlMap();
+    }
+    var validator =
+        PubspecValidator(resourceProvider, pubspecFile.createSource());
+    var errors = validator.validate(yamlContent.nodes);
+    for (var error in errors) {
+      var generator =
+          PubspecFixGenerator(resourceProvider, error, content, document);
+      var fixes = await generator.computeFixes();
+      if (fixes.isNotEmpty) {
+        fixes.sort(Fix.SORT_BY_RELEVANCE);
+        var lineInfo = LineInfo.fromContent(content);
+        var result = engine.ErrorsResultImpl(
+            session, file, Uri.file(file), lineInfo, false, errors);
+        var serverError = newAnalysisError_fromEngine(result, error);
+        var errorFixes = AnalysisErrorFixes(serverError);
+        errorFixesList.add(errorFixes);
+        fixes.forEach((fix) {
+          errorFixes.fixes.add(fix.change);
+        });
+      }
+    }
+    return errorFixesList;
+  }
+
+  /// Compute and return the fixes associated with server-generated errors.
+  Future<List<AnalysisErrorFixes>> _computeServerErrorFixes(
+      Request request, String file, int offset) async {
+    var pathContext = server.resourceProvider.pathContext;
+    if (file_paths.isDart(pathContext, file)) {
+      return _computeDartFixes(request, file, offset);
+    } else if (file_paths.isAnalysisOptionsYaml(pathContext, file)) {
+      return _computeAnalysisOptionsFixes(file, offset);
+    } else if (file_paths.isPubspecYaml(pathContext, file)) {
+      return _computePubspecFixes(file, offset);
+    } else if (file_paths.isAndroidManifestXml(pathContext, file)) {
+      // TODO(brianwilkerson) Do we need to check more than the file name?
+      return _computeManifestFixes(file, offset);
+    }
+    return <AnalysisErrorFixes>[];
+  }
+
+  YamlMap? _getOptions(SourceFactory sourceFactory, String content) {
+    var optionsProvider = AnalysisOptionsProvider(sourceFactory);
+    try {
+      return optionsProvider.getOptionsFromString(content);
+    } on OptionsFormatException {
+      return null;
+    }
+  }
+
+  /// Return the contents of the [file], or `null` if the file does not exist or
+  /// cannot be read.
+  String? _safelyRead(File file) {
+    try {
+      return file.readAsStringSync();
+    } on FileSystemException {
+      return null;
+    }
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart
new file mode 100644
index 0000000..c8d6bfb
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/services/completion/postfix/postfix_completion.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+
+/// The handler for the `edit.getPostfixCompletion` request.
+class EditGetPostfixCompletionHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditGetPostfixCompletionHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    server.options.analytics?.sendEvent('edit', 'getPostfixCompletion');
+
+    var params = EditGetPostfixCompletionParams.fromRequest(request);
+    var file = params.file;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    SourceChange? change;
+
+    var result = await server.getResolvedUnit(file);
+    if (result != null) {
+      var context = PostfixCompletionContext(
+        result,
+        params.offset,
+        params.key,
+      );
+      var processor = PostfixCompletionProcessor(context);
+      var completion = await processor.compute();
+      change = completion.change;
+    }
+    change ??= SourceChange('', edits: []);
+
+    sendResult(EditGetPostfixCompletionResult(change));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_statement_completion.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_statement_completion.dart
new file mode 100644
index 0000000..50ace41
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_statement_completion.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/services/completion/statement/statement_completion.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+
+/// The handler for the `edit.getStatementCompletion` request.
+class EditGetStatementCompletionHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditGetStatementCompletionHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = EditGetStatementCompletionParams.fromRequest(request);
+    var file = params.file;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    SourceChange? change;
+
+    var result = await server.getResolvedUnit(file);
+    if (result != null) {
+      var context = StatementCompletionContext(result, params.offset);
+      var processor = StatementCompletionProcessor(context);
+      var completion = await processor.compute();
+      change = completion.change;
+    }
+    change ??= SourceChange('', edits: []);
+
+    sendResult(EditGetStatementCompletionResult(change, false));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_import_elements.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_import_elements.dart
new file mode 100644
index 0000000..1dd1079
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_import_elements.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/computer/import_elements_computer.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `edit.importElements` request.
+class EditImportElementsHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditImportElementsHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = EditImportElementsParams.fromRequest(request);
+    var file = params.file;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    //
+    // Prepare the resolved unit.
+    //
+    var result = await server.getResolvedUnit(file);
+    if (result == null) {
+      sendResponse(Response.importElementsInvalidFile(request));
+      return;
+    }
+    var libraryUnit = result.libraryElement.definingCompilationUnit;
+    if (libraryUnit != result.unit.declaredElement) {
+      // The file in the request is a part of a library. We need to pass the
+      // defining compilation unit to the computer, not the part.
+      result = await server.getResolvedUnit(libraryUnit.source.fullName);
+      if (result == null) {
+        sendResponse(Response.importElementsInvalidFile(request));
+        return;
+      }
+    }
+    //
+    // Compute the edits required to import the required elements.
+    //
+    var computer = ImportElementsComputer(server.resourceProvider, result);
+    var change = await computer.createEdits(params.elements);
+    var edits = change.edits;
+    var edit = edits.isEmpty ? null : edits[0];
+    //
+    // Send the response.
+    //
+    sendResult(EditImportElementsResult(edit: edit));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_is_postfix_completion_applicable.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_is_postfix_completion_applicable.dart
new file mode 100644
index 0000000..7c9fd50
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_is_postfix_completion_applicable.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/services/completion/postfix/postfix_completion.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `edit.isPostfixCompletionApplicable` request.
+class EditIsPostfixCompletionApplicableHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditIsPostfixCompletionApplicableHandler(AnalysisServer server,
+      Request request, CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = EditGetPostfixCompletionParams.fromRequest(request);
+    var file = params.file;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    var value = false;
+
+    var result = await server.getResolvedUnit(file);
+    if (result != null) {
+      var context = PostfixCompletionContext(
+        result,
+        params.offset,
+        params.key,
+      );
+      var processor = PostfixCompletionProcessor(context);
+      value = await processor.isApplicable();
+    }
+
+    sendResult(EditIsPostfixCompletionApplicableResult(value));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_list_postfix_completion_templates.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_list_postfix_completion_templates.dart
new file mode 100644
index 0000000..b990eea
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_list_postfix_completion_templates.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/services/completion/postfix/postfix_completion.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `edit.listPostfixCompletionTemplates` request.
+class EditListPostfixCompletionTemplatesHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditListPostfixCompletionTemplatesHandler(AnalysisServer server,
+      Request request, CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var templates = DartPostfixCompletion.ALL_TEMPLATES
+        .map((PostfixCompletionKind kind) =>
+            PostfixTemplateDescriptor(kind.name, kind.key, kind.example))
+        .toList();
+    sendResult(EditListPostfixCompletionTemplatesResult(templates));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart
new file mode 100644
index 0000000..9a8dbc1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/services/correction/organize_imports.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+
+/// The handler for the `edit.organizeDirectives` request.
+class EditOrganizeDirectivesHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditOrganizeDirectivesHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    // TODO(brianwilkerson) Move analytics tracking out of [handleRequest].
+    server.options.analytics?.sendEvent('edit', 'organizeDirectives');
+
+    var params = EditOrganizeDirectivesParams.fromRequest(request);
+    var file = params.file;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    var pathContext = server.resourceProvider.pathContext;
+    if (!file_paths.isDart(pathContext, file)) {
+      sendResponse(Response.fileNotAnalyzed(request, file));
+      return;
+    }
+
+    // Prepare the file information.
+    var result = await server.getResolvedUnit(file);
+    if (result == null) {
+      sendResponse(Response.fileNotAnalyzed(request, file));
+      return;
+    }
+    var fileStamp = -1;
+    var code = result.content;
+    var unit = result.unit;
+    var errors = result.errors;
+    // check if there are scan/parse errors in the file
+    var numScanParseErrors = numberOfSyntacticErrors(errors);
+    if (numScanParseErrors != 0) {
+      sendResponse(Response.organizeDirectivesError(
+          request, 'File has $numScanParseErrors scan/parse errors.'));
+      return;
+    }
+    // do organize
+    var sorter = ImportOrganizer(code, unit, errors);
+    var edits = sorter.organize();
+    var fileEdit = SourceFileEdit(file, fileStamp, edits: edits);
+    sendResult(EditOrganizeDirectivesResult(fileEdit));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_sort_members.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_sort_members.dart
new file mode 100644
index 0000000..2aa4aa6
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_sort_members.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/services/correction/sort_members.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+
+/// The handler for the `edit.sortMembers` request.
+class EditSortMembersHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditSortMembersHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = EditSortMembersParams.fromRequest(request);
+    var file = params.file;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    var pathContext = server.resourceProvider.pathContext;
+    if (!file_paths.isDart(pathContext, file)) {
+      sendResponse(Response.sortMembersInvalidFile(request));
+      return;
+    }
+
+    // Prepare the file information.
+    var result = await server.getParsedUnit(file);
+    if (result == null) {
+      sendResponse(Response.fileNotAnalyzed(request, file));
+      return;
+    }
+
+    var fileStamp = -1;
+    var code = result.content;
+    var unit = result.unit;
+    var errors = result.errors;
+    // Check if there are scan/parse errors in the file.
+    var numScanParseErrors = numberOfSyntacticErrors(errors);
+    if (numScanParseErrors != 0) {
+      sendResponse(
+          Response.sortMembersParseErrors(request, numScanParseErrors));
+      return;
+    }
+    // Do sort.
+    var sorter = MemberSorter(code, unit, result.lineInfo);
+    var edits = sorter.sort();
+    var fileEdit = SourceFileEdit(file, fileStamp, edits: edits);
+    sendResult(EditSortMembersResult(fileEdit));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart b/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
new file mode 100644
index 0000000..7e9094e
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:_fe_analyzer_shared/src/scanner/errors.dart';
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/protocol/protocol_internal.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/dart/error/syntactic_errors.g.dart';
+
+/// A request handler for the legacy protocol.
+abstract class LegacyHandler {
+  /// The analysis server that is using this handler to process a request.
+  final AnalysisServer server;
+
+  /// The request being handled.
+  final Request request;
+
+  /// The token used in order to allow the request to be cancelled. Not all
+  /// handlers support cancelling a request.
+  final CancellationToken cancellationToken;
+
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  LegacyHandler(this.server, this.request, this.cancellationToken);
+
+  /// Handle the [request].
+  Future<void> handle();
+
+  /// Return the number of syntactic errors in the list of [errors].
+  int numberOfSyntacticErrors(List<AnalysisError> errors) {
+    var numScanParseErrors = 0;
+    for (var error in errors) {
+      if (error.errorCode is ScannerErrorCode ||
+          error.errorCode is ParserErrorCode) {
+        numScanParseErrors++;
+      }
+    }
+    return numScanParseErrors;
+  }
+
+  /// Send the [response] to the client.
+  void sendResponse(Response response) {
+    server.sendResponse(response);
+  }
+
+  /// Send a response to the client that is associated with the given [request]
+  /// and whose body if the given [result].
+  void sendResult(ResponseResult result) {
+    sendResponse(result.toResponse(request.id));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/server_cancel_request.dart b/pkg/analysis_server/lib/src/handler/legacy/server_cancel_request.dart
new file mode 100644
index 0000000..5702aba
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/server_cancel_request.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `server.cancelRequest` request.
+class ServerCancelRequestHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  ServerCancelRequestHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    final id = ServerCancelRequestParams.fromRequest(request).id;
+    server.cancelRequest(id);
+    sendResult(ServerCancelRequestResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/server_get_version.dart b/pkg/analysis_server/lib/src/handler/legacy/server_get_version.dart
new file mode 100644
index 0000000..21eb232
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/server_get_version.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_constants.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `server.getVersion` request.
+class ServerGetVersionHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  ServerGetVersionHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    sendResult(ServerGetVersionResult(
+      server.options.reportProtocolVersion ?? PROTOCOL_VERSION,
+    ));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/server_set_subscriptions.dart b/pkg/analysis_server/lib/src/handler/legacy/server_set_subscriptions.dart
new file mode 100644
index 0000000..67633bf
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/server_set_subscriptions.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `server.setSubscriptions` request.
+class ServerSetSubscriptionsHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  ServerSetSubscriptionsHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    try {
+      server.serverServices = ServerSetSubscriptionsParams.fromRequest(request)
+          .subscriptions
+          .toSet();
+      server.requestStatistics?.isNotificationSubscribed =
+          server.serverServices.contains(ServerService.LOG);
+    } on RequestFailure catch (exception) {
+      sendResponse(exception.response);
+      return;
+    }
+    sendResult(ServerSetSubscriptionsResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/server_shutdown.dart b/pkg/analysis_server/lib/src/handler/legacy/server_shutdown.dart
new file mode 100644
index 0000000..93689a6
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/server_shutdown.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler for the `server.shutdown` request.
+class ServerShutdownHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  ServerShutdownHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    await server.shutdown();
+    sendResult(ServerShutdownResult());
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/unsupported_request.dart b/pkg/analysis_server/lib/src/handler/legacy/unsupported_request.dart
new file mode 100644
index 0000000..03dc5a5
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/unsupported_request.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/utilities/progress.dart';
+
+/// The handler used for the request that are no longer supported.
+class UnsupportedRequestHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  UnsupportedRequestHandler(AnalysisServer server, Request request,
+      CancellationToken cancellationToken)
+      : super(server, request, cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    sendResponse(Response.unsupportedFeature(request.id,
+        'Please contact the Dart analyzer team if you need this request.'));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/client_capabilities.dart b/pkg/analysis_server/lib/src/lsp/client_capabilities.dart
index bac248e..9a50687 100644
--- a/pkg/analysis_server/lib/src/lsp/client_capabilities.dart
+++ b/pkg/analysis_server/lib/src/lsp/client_capabilities.dart
@@ -68,6 +68,7 @@
   final bool literalCodeActions;
   final bool insertReplaceCompletionRanges;
   final bool definitionLocationLink;
+  final bool typeDefinitionLocationLink;
   final bool hierarchicalSymbols;
   final bool diagnosticCodeDescription;
   final Set<CodeActionKind> codeActionKinds;
@@ -109,6 +110,8 @@
             false,
         definitionLocationLink =
             raw.textDocument?.definition?.linkSupport ?? false,
+        typeDefinitionLocationLink =
+            raw.textDocument?.typeDefinition?.linkSupport ?? false,
         completionItemTags = _listToSet(
             raw.textDocument?.completion?.completionItem?.tagSupport?.valueSet),
         diagnosticTags = _listToSet(
diff --git a/pkg/analysis_server/lib/src/lsp/client_configuration.dart b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
index c7497dd..a5ef8f7 100644
--- a/pkg/analysis_server/lib/src/lsp/client_configuration.dart
+++ b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
@@ -151,6 +151,14 @@
 /// known editors allow per-file configuration and it allows us to keep the
 /// settings cached, invalidated only when WorkspaceFolders change.
 class LspResourceClientConfiguration {
+  /// The maximum number of completions to return for completion requests by
+  /// default.
+  ///
+  /// This has been set fairly high initially to avoid changing behaviour too
+  /// much. The Dart-Code extension will override this default with its own
+  /// to gather feedback and then this can be adjusted accordingly.
+  static const defaultMaxCompletions = 2000;
+
   final Map<String, Object?> _settings;
   final LspResourceClientConfiguration? _fallback;
 
@@ -166,16 +174,20 @@
       true;
 
   /// Whether to include Snippets in code completion results.
-  bool get enableSnippets =>
-      // TODO(dantup): Change this setting to `enableSnippets`
-      //    and default to `true`
-      //    and remove `initializeWithSnippetSupportAndPreviewFlag` from tests
-      //    once all snippets are implemented and VS Code has shipped a
-      //    version that maps `enableServerSnippets` to `enableSnippets` in
-      //    middleware to avoid dupes.
-      _settings['previewEnableSnippets'] as bool? ??
-      _fallback?.enableSnippets ??
-      false;
+  bool get enableSnippets {
+    // Versions of Dart-Code earlier than v3.36 (1 Mar 2022) send
+    // enableServerSnippets=false to opt-out of snippets. Later versions map
+    // this version to the documented 'enableSnippets' setting in middleware.
+    // Once the number of users on < 3.36 is insignificant, this check can be
+    // removed. At 24 Mar 2022, approx 9% of users are on < 3.36.
+    if (_settings['enableServerSnippets'] == false /* explicit false */) {
+      return false;
+    }
+
+    return _settings['enableSnippets'] as bool? ??
+        _fallback?.enableSnippets ??
+        true;
+  }
 
   /// The line length used when formatting documents.
   ///
@@ -183,6 +195,15 @@
   int? get lineLength =>
       _settings['lineLength'] as int? ?? _fallback?.lineLength;
 
+  /// Maximum number of CompletionItems per completion request.
+  ///
+  /// If more than this are available, the list is truncated and isIncomplete
+  /// is set to true.
+  int get maxCompletionItems =>
+      _settings['maxCompletionItems'] as int? ??
+      _fallback?.maxCompletionItems ??
+      defaultMaxCompletions;
+
   /// Whether to rename files when renaming classes inside them where the file
   /// and class name match.
   ///
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index 55257c6..cb2da70 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -67,6 +67,7 @@
   static const fixAll = 'edit.fixAll';
   static const sendWorkspaceEdit = 'edit.sendWorkspaceEdit';
   static const performRefactor = 'refactor.perform';
+  static const validateRefactor = 'refactor.validate';
 }
 
 abstract class CustomMethods {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/abstract_refactor.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/abstract_refactor.dart
new file mode 100644
index 0000000..cc504be
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/abstract_refactor.dart
@@ -0,0 +1,197 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/constants.dart';
+import 'package:analysis_server/src/lsp/handlers/commands/simple_edit_handler.dart';
+import 'package:analysis_server/src/lsp/handlers/handlers.dart';
+import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
+import 'package:analysis_server/src/lsp/progress.dart';
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/refactoring/refactoring.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+
+final _manager = LspRefactorManager._();
+
+/// A base class for refactoring commands that need to create Refactorings from
+/// client-supplied arguments.
+abstract class AbstractRefactorCommandHandler extends SimpleEditCommandHandler {
+  AbstractRefactorCommandHandler(LspAnalysisServer server) : super(server);
+
+  @override
+  String get commandName => 'Perform Refactor';
+
+  LspRefactorManager get manager => _manager;
+
+  FutureOr<ErrorOr<void>> execute(
+      String path,
+      String kind,
+      int offset,
+      int length,
+      Map<String, Object?>? options,
+      CancellationToken cancellationToken,
+      ProgressReporter reporter,
+      int? docVersion);
+
+  Future<ErrorOr<Refactoring>> getRefactoring(
+    RefactoringKind kind,
+    ResolvedUnitResult result,
+    int offset,
+    int length,
+    Map<String, dynamic>? options,
+  ) async {
+    switch (kind) {
+      case RefactoringKind.EXTRACT_METHOD:
+        final refactor = ExtractMethodRefactoring(
+            server.searchEngine, result, offset, length);
+
+        var preferredName = options != null ? options['name'] as String : null;
+        // checkInitialConditions will populate names with suggestions.
+        if (preferredName == null) {
+          await refactor.checkInitialConditions();
+          if (refactor.names.isNotEmpty) {
+            preferredName = refactor.names.first;
+          }
+        }
+        refactor.name = preferredName ?? 'newMethod';
+
+        // Defaults to true, but may be surprising if users didn't have an option
+        // to opt in.
+        refactor.extractAll = false;
+        return success(refactor);
+
+      case RefactoringKind.EXTRACT_LOCAL_VARIABLE:
+        final refactor = ExtractLocalRefactoring(result, offset, length);
+
+        var preferredName = options != null ? options['name'] as String : null;
+        // checkInitialConditions will populate names with suggestions.
+        if (preferredName == null) {
+          await refactor.checkInitialConditions();
+          if (refactor.names.isNotEmpty) {
+            preferredName = refactor.names.first;
+          }
+        }
+        refactor.name = preferredName ?? 'newVariable';
+
+        // Defaults to true, but may be surprising if users didn't have an option
+        // to opt in.
+        refactor.extractAll = false;
+        return success(refactor);
+
+      case RefactoringKind.EXTRACT_WIDGET:
+        final refactor = ExtractWidgetRefactoring(
+            server.searchEngine, result, offset, length);
+        // Provide a default name for clients that do not have any custom
+        // handling.
+        // Clients can use the information documented for refactor.perform to
+        // inject their own user-provided names until LSP has some native
+        // support:
+        // https://github.com/microsoft/language-server-protocol/issues/764
+        refactor.name =
+            options != null ? options['name'] as String : 'NewWidget';
+        return success(refactor);
+
+      case RefactoringKind.INLINE_LOCAL_VARIABLE:
+        final refactor =
+            InlineLocalRefactoring(server.searchEngine, result, offset);
+        return success(refactor);
+
+      case RefactoringKind.INLINE_METHOD:
+        final refactor =
+            InlineMethodRefactoring(server.searchEngine, result, offset);
+        return success(refactor);
+
+      case RefactoringKind.CONVERT_GETTER_TO_METHOD:
+        final node = NodeLocator(offset).searchWithin(result.unit);
+        final element = server.getElementOfNode(node);
+        if (element != null) {
+          if (element is PropertyAccessorElement) {
+            final refactor = ConvertGetterToMethodRefactoring(
+                server.searchEngine, result.session, element);
+            return success(refactor);
+          }
+        }
+        return error(ServerErrorCodes.InvalidCommandArguments,
+            'Location supplied to $commandName $kind is not longer valid');
+
+      case RefactoringKind.CONVERT_METHOD_TO_GETTER:
+        final node = NodeLocator(offset).searchWithin(result.unit);
+        final element = server.getElementOfNode(node);
+        if (element != null) {
+          if (element is ExecutableElement) {
+            final refactor = ConvertMethodToGetterRefactoring(
+                server.searchEngine, result.session, element);
+            return success(refactor);
+          }
+        }
+        return error(ServerErrorCodes.InvalidCommandArguments,
+            'Location supplied to $commandName $kind is not longer valid');
+
+      default:
+        return error(ServerErrorCodes.InvalidCommandArguments,
+            'Unknown RefactoringKind $kind was supplied to $commandName');
+    }
+  }
+
+  @override
+  Future<ErrorOr<void>> handle(List<Object?>? arguments,
+      ProgressReporter reporter, CancellationToken cancellationToken) async {
+    if (arguments == null ||
+        arguments.length != 6 ||
+        arguments[0] is! String || // kind
+        arguments[1] is! String || // path
+        (arguments[2] != null && arguments[2] is! int) || // docVersion
+        arguments[3] is! int || // offset
+        arguments[4] is! int || // length
+        // options
+        // Important: This arguments position is documented in
+        // tool/lsp_spec/README.md to allow clients with custom code (such as
+        // VS Code) to intercept the request and inject options (such as a
+        // user-provided name). Any changes to these arguments must be backwards
+        // compatible, keeping the options in this position.
+        (arguments[5] != null && arguments[5] is! Map<String, Object?>)) {
+      return ErrorOr.error(ResponseError(
+        code: ServerErrorCodes.InvalidCommandArguments,
+        message:
+            '$commandName requires 6 parameters: RefactoringKind, docVersion, filePath, offset, length, options (optional)',
+      ));
+    }
+
+    final kind = arguments[0] as String;
+    final path = arguments[1] as String;
+    final docVersion = arguments[2] as int?;
+    final offset = arguments[3] as int;
+    final length = arguments[4] as int;
+    final options = arguments[5] as Map<String, Object?>?;
+
+    return execute(path, kind, offset, length, options, cancellationToken,
+        reporter, docVersion);
+  }
+}
+
+/// Manages a running refactor to help ensure only one refactor runs at a time.
+class LspRefactorManager {
+  /// The cancellation token for the current in-progress refactor (or null).
+  CancelableToken? _currentRefactoringCancellationToken;
+
+  LspRefactorManager._();
+
+  /// Begins a new refactor, cancelling any other in-progress refactors.
+  void begin(CancelableToken cancelToken) {
+    _currentRefactoringCancellationToken?.cancel();
+    _currentRefactoringCancellationToken = cancelToken;
+  }
+
+  /// Marks a refactor as no longer current.
+  void end(CancelableToken cancelToken) {
+    if (_currentRefactoringCancellationToken == cancelToken) {
+      _currentRefactoringCancellationToken = null;
+    }
+  }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/fix_all.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/fix_all.dart
index 4d4f523..c4d005f 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/fix_all.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/fix_all.dart
@@ -43,7 +43,9 @@
     }
 
     return result.mapResult((result) async {
-      final workspace = DartChangeWorkspace(server.currentSessions);
+      final workspace = DartChangeWorkspace(
+        await server.currentSessions,
+      );
       final processor =
           BulkFixProcessor(server.instrumentationService, workspace);
 
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
index 4de8bfe..3690cf5 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
@@ -2,59 +2,38 @@
 // 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 'dart:async';
+
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analysis_server/src/lsp/constants.dart';
-import 'package:analysis_server/src/lsp/handlers/commands/simple_edit_handler.dart';
+import 'package:analysis_server/src/lsp/handlers/commands/abstract_refactor.dart';
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 import 'package:analysis_server/src/lsp/mapping.dart';
 import 'package:analysis_server/src/lsp/progress.dart';
 import 'package:analysis_server/src/protocol_server.dart';
-import 'package:analysis_server/src/services/refactoring/refactoring.dart';
-import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/src/dart/ast/utilities.dart';
 
-final _manager = _RefactorManager();
-
-class PerformRefactorCommandHandler extends SimpleEditCommandHandler {
+class PerformRefactorCommandHandler extends AbstractRefactorCommandHandler {
   PerformRefactorCommandHandler(LspAnalysisServer server) : super(server);
 
   @override
   String get commandName => 'Perform Refactor';
 
   @override
-  Future<ErrorOr<void>> handle(List<Object?>? arguments,
-      ProgressReporter reporter, CancellationToken cancellationToken) async {
-    if (arguments == null ||
-        arguments.length != 6 ||
-        arguments[0] is! String || // kind
-        arguments[1] is! String || // path
-        (arguments[2] != null && arguments[2] is! int) || // docVersion
-        arguments[3] is! int || // offset
-        arguments[4] is! int || // length
-        // options
-        (arguments[5] != null && arguments[5] is! Map<String, dynamic>)) {
-      // length
-      return ErrorOr.error(ResponseError(
-        code: ServerErrorCodes.InvalidCommandArguments,
-        message:
-            '$commandName requires 6 parameters: RefactoringKind, docVersion, filePath, offset, length, options (optional)',
-      ));
-    }
-
-    final kind = arguments[0] as String;
-    final path = arguments[1] as String;
-    final docVersion = arguments[2] as int?;
-    final offset = arguments[3] as int;
-    final length = arguments[4] as int;
-    final options = arguments[5] as Map<String, Object?>?;
-
+  FutureOr<ErrorOr<void>> execute(
+    String path,
+    String kind,
+    int offset,
+    int length,
+    Map<String, Object?>? options,
+    CancellationToken cancellationToken,
+    ProgressReporter reporter,
+    int? docVersion,
+  ) async {
     final result = await requireResolvedUnit(path);
     return result.mapResult((result) async {
-      final refactoring = await _getRefactoring(
+      final refactoring = await getRefactoring(
           RefactoringKind(kind), result, offset, length, options);
       return refactoring.mapResult((refactoring) async {
         // If the token we were given is not cancellable, replace it with one that
@@ -64,7 +43,7 @@
         final cancelableToken = cancellationToken is CancelableToken
             ? cancellationToken
             : CancelableToken();
-        _manager.begin(cancelableToken);
+        manager.begin(cancelableToken);
 
         try {
           reporter.begin('Refactoring…');
@@ -98,131 +77,11 @@
 
           final edit = createWorkspaceEdit(server, change);
           return await sendWorkspaceEditToClient(edit);
-        } on InconsistentAnalysisException {
-          return fileModifiedError;
         } finally {
-          _manager.end(cancelableToken);
+          manager.end(cancelableToken);
           reporter.end();
         }
       });
     });
   }
-
-  Future<ErrorOr<Refactoring>> _getRefactoring(
-    RefactoringKind kind,
-    ResolvedUnitResult result,
-    int offset,
-    int length,
-    Map<String, dynamic>? options,
-  ) async {
-    switch (kind) {
-      case RefactoringKind.EXTRACT_METHOD:
-        final refactor = ExtractMethodRefactoring(
-            server.searchEngine, result, offset, length);
-
-        var preferredName = options != null ? options['name'] as String : null;
-        // checkInitialConditions will populate names with suggestions.
-        if (preferredName == null) {
-          await refactor.checkInitialConditions();
-          if (refactor.names.isNotEmpty) {
-            preferredName = refactor.names.first;
-          }
-        }
-        refactor.name = preferredName ?? 'newMethod';
-
-        // Defaults to true, but may be surprising if users didn't have an option
-        // to opt in.
-        refactor.extractAll = false;
-        return success(refactor);
-
-      case RefactoringKind.EXTRACT_LOCAL_VARIABLE:
-        final refactor = ExtractLocalRefactoring(result, offset, length);
-
-        var preferredName = options != null ? options['name'] as String : null;
-        // checkInitialConditions will populate names with suggestions.
-        if (preferredName == null) {
-          await refactor.checkInitialConditions();
-          if (refactor.names.isNotEmpty) {
-            preferredName = refactor.names.first;
-          }
-        }
-        refactor.name = preferredName ?? 'newVariable';
-
-        // Defaults to true, but may be surprising if users didn't have an option
-        // to opt in.
-        refactor.extractAll = false;
-        return success(refactor);
-
-      case RefactoringKind.EXTRACT_WIDGET:
-        final refactor = ExtractWidgetRefactoring(
-            server.searchEngine, result, offset, length);
-        // TODO(dantup): For now we don't have a good way to prompt the user
-        // for a method name so we just use a placeholder and expect them to
-        // rename (this is what C#/Omnisharp does), but there's an open request
-        // to handle this better.
-        // https://github.com/microsoft/language-server-protocol/issues/764
-        refactor.name =
-            options != null ? options['name'] as String : 'NewWidget';
-        return success(refactor);
-
-      case RefactoringKind.INLINE_LOCAL_VARIABLE:
-        final refactor =
-            InlineLocalRefactoring(server.searchEngine, result, offset);
-        return success(refactor);
-
-      case RefactoringKind.INLINE_METHOD:
-        final refactor =
-            InlineMethodRefactoring(server.searchEngine, result, offset);
-        return success(refactor);
-
-      case RefactoringKind.CONVERT_GETTER_TO_METHOD:
-        final node = NodeLocator(offset).searchWithin(result.unit);
-        final element = server.getElementOfNode(node);
-        if (element != null) {
-          if (element is PropertyAccessorElement) {
-            final refactor = ConvertGetterToMethodRefactoring(
-                server.searchEngine, result.session, element);
-            return success(refactor);
-          }
-        }
-        return error(ServerErrorCodes.InvalidCommandArguments,
-            'Location supplied to $commandName $kind is not longer valid');
-
-      case RefactoringKind.CONVERT_METHOD_TO_GETTER:
-        final node = NodeLocator(offset).searchWithin(result.unit);
-        final element = server.getElementOfNode(node);
-        if (element != null) {
-          if (element is ExecutableElement) {
-            final refactor = ConvertMethodToGetterRefactoring(
-                server.searchEngine, result.session, element);
-            return success(refactor);
-          }
-        }
-        return error(ServerErrorCodes.InvalidCommandArguments,
-            'Location supplied to $commandName $kind is not longer valid');
-
-      default:
-        return error(ServerErrorCodes.InvalidCommandArguments,
-            'Unknown RefactoringKind $kind was supplied to $commandName');
-    }
-  }
-}
-
-/// Manages a running refactor to help ensure only one refactor runs at a time.
-class _RefactorManager {
-  /// The cancellation token for the current in-progress refactor (or null).
-  CancelableToken? _currentRefactoringCancellationToken;
-
-  /// Begins a new refactor, cancelling any other in-progress refactors.
-  void begin(CancelableToken cancelToken) {
-    _currentRefactoringCancellationToken?.cancel();
-    _currentRefactoringCancellationToken = cancelToken;
-  }
-
-  /// Marks a refactor as no longer current.
-  void end(CancelableToken cancelToken) {
-    if (_currentRefactoringCancellationToken == cancelToken) {
-      _currentRefactoringCancellationToken = null;
-    }
-  }
 }
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart
index ee853d6..7e3ecad 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart
@@ -40,8 +40,7 @@
     final clientCapabilities = server.clientCapabilities;
     if (clientCapabilities == null) {
       // This should not happen unless a client misbehaves.
-      return error(ErrorCodes.ServerNotInitialized,
-          'Requests not before server is initilized');
+      return serverNotInitializedError;
     }
 
     final lineInfo = unit.lineInfo;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/sort_members.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/sort_members.dart
index a4c2286..d10e4db 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/sort_members.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/sort_members.dart
@@ -35,8 +35,8 @@
     final path = arguments.single as String;
     final docIdentifier = server.getVersionedDocumentIdentifier(path);
 
-    var driver = server.getAnalysisDriver(path);
-    final result = driver?.parseFileSync(path);
+    var session = await server.getAnalysisSession(path);
+    final result = session?.getParsedUnit(path);
 
     if (cancellationToken.isCancellationRequested) {
       return error(ErrorCodes.RequestCancelled, 'Request was cancelled');
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/validate_refactor.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/validate_refactor.dart
new file mode 100644
index 0000000..6c54c7c
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/validate_refactor.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/lsp_protocol/protocol_custom_generated.dart';
+import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/handlers/commands/abstract_refactor.dart';
+import 'package:analysis_server/src/lsp/handlers/handlers.dart';
+import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
+import 'package:analysis_server/src/lsp/progress.dart';
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+
+class ValidateRefactorCommandHandler extends AbstractRefactorCommandHandler {
+  ValidateRefactorCommandHandler(LspAnalysisServer server) : super(server);
+
+  @override
+  String get commandName => 'Validate Refactor';
+
+  @override
+  FutureOr<ErrorOr<ValidateRefactorResult>> execute(
+    String path,
+    String kind,
+    int offset,
+    int length,
+    Map<String, Object?>? options,
+    CancellationToken cancellationToken,
+    ProgressReporter reporter,
+    int? docVersion,
+  ) async {
+    // In order to prevent clients asking users for a method/widget name and
+    // then failing because of something like "Cannot extract closure as method"
+    // this command allows the client to call `checkInitialConditions()` after
+    // the user selects the action but before prompting for a name.
+    //
+    // We do not perform that check when building the code actions because there
+    // will be no visibility of the reason why the refactor is not available to
+    // the user.
+
+    final result = await requireResolvedUnit(path);
+    return result.mapResult((result) async {
+      final refactoring = await getRefactoring(
+          RefactoringKind(kind), result, offset, length, options);
+      return refactoring.mapResult((refactoring) async {
+        // If the token we were given is not cancellable, replace it with one that
+        // is for the rest of this request, as a future refactor may need to cancel
+        // this request.
+        // The original token should be kept and also checked for cancellation.
+        final cancelableToken = cancellationToken is CancelableToken
+            ? cancellationToken
+            : CancelableToken();
+        manager.begin(cancelableToken);
+
+        try {
+          reporter.begin('Preparing Refactor…');
+          final status = await refactoring.checkInitialConditions();
+
+          if (status.hasError) {
+            return success(
+                ValidateRefactorResult(valid: false, message: status.message!));
+          }
+
+          return success(ValidateRefactorResult(valid: true));
+        } on InconsistentAnalysisException {
+          return failure(fileModifiedError);
+        } finally {
+          manager.end(cancelableToken);
+          reporter.end();
+        }
+      });
+    });
+  }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index 521927d..acff954 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -75,8 +75,7 @@
     final clientCapabilities = server.clientCapabilities;
     if (clientCapabilities == null) {
       // This should not happen unless a client misbehaves.
-      return error(ErrorCodes.ServerNotInitialized,
-          'Requests not before server is initilized');
+      return serverNotInitializedError;
     }
 
     final supportsApplyEdit = clientCapabilities.applyEdit;
@@ -169,12 +168,13 @@
   /// version of each document being modified so it's important to call this
   /// immediately after computing edits to ensure the document is not modified
   /// before the version number is read.
-  CodeAction _createAssistAction(SourceChange change) {
+  CodeAction _createAssistAction(SourceChange change, ResolvedUnitResult unit) {
     return CodeAction(
       title: change.message,
       kind: toCodeActionKind(change.id, CodeActionKind.Refactor),
       diagnostics: const [],
-      edit: createWorkspaceEdit(server, change),
+      edit: createWorkspaceEdit(server, change,
+          allowSnippets: true, filePath: unit.path, lineInfo: unit.lineInfo),
     );
   }
 
@@ -182,12 +182,14 @@
   /// version of each document being modified so it's important to call this
   /// immediately after computing edits to ensure the document is not modified
   /// before the version number is read.
-  CodeAction _createFixAction(SourceChange change, Diagnostic diagnostic) {
+  CodeAction _createFixAction(
+      SourceChange change, Diagnostic diagnostic, ResolvedUnitResult unit) {
     return CodeAction(
       title: change.message,
       kind: toCodeActionKind(change.id, CodeActionKind.QuickFix),
       diagnostics: [diagnostic],
-      edit: createWorkspaceEdit(server, change),
+      edit: createWorkspaceEdit(server, change,
+          allowSnippets: true, filePath: unit.path, lineInfo: unit.lineInfo),
     );
   }
 
@@ -253,7 +255,9 @@
     try {
       var context = DartAssistContextImpl(
         server.instrumentationService,
-        DartChangeWorkspace(server.currentSessions),
+        DartChangeWorkspace(
+          await server.currentSessions,
+        ),
         unit,
         offset,
         length,
@@ -267,12 +271,12 @@
 
       final codeActions = <CodeAction>[];
       codeActions.addAll(assists.map((assist) {
-        final action = _createAssistAction(assist.change);
+        final action = _createAssistAction(assist.change, unit);
         codeActionPriorities[action] = assist.kind.priority;
         return action;
       }));
       codeActions.addAll(pluginChanges.map((change) {
-        final action = _createAssistAction(change.change);
+        final action = _createAssistAction(change.change, unit);
         codeActionPriorities[action] = change.priority;
         return action;
       }));
@@ -344,7 +348,9 @@
         if (errorLine < range.start.line || errorLine > range.end.line) {
           continue;
         }
-        var workspace = DartChangeWorkspace(server.currentSessions);
+        var workspace = DartChangeWorkspace(
+          await server.currentSessions,
+        );
         var context = DartFixContextImpl(
             server.instrumentationService, workspace, unit, error);
         final fixes = await fixContributor.computeFixes(context);
@@ -357,7 +363,7 @@
           );
           codeActions.addAll(
             fixes.map((fix) {
-              final action = _createFixAction(fix.change, diagnostic);
+              final action = _createFixAction(fix.change, diagnostic, unit);
               codeActionPriorities[action] = fix.kind.priority;
               return action;
             }),
@@ -378,7 +384,7 @@
       final pluginFixActions = pluginFixes.expand(
         (fix) => fix.fixes.map((fixChange) {
           final action = _createFixAction(
-              fixChange.change, pluginErrorToDiagnostic(fix.error));
+              fixChange.change, pluginErrorToDiagnostic(fix.error), unit);
           codeActionPriorities[action] = fixChange.priority;
           return action;
         }),
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index f64f323..fed7a76 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -55,8 +55,7 @@
     final clientCapabilities = server.clientCapabilities;
     if (clientCapabilities == null) {
       // This should not happen unless a client misbehaves.
-      return error(ErrorCodes.ServerNotInitialized,
-          'Requests not before server is initilized');
+      return serverNotInitializedError;
     }
 
     final triggerCharacter = params.context?.triggerCharacter;
@@ -79,13 +78,31 @@
       final pathContext = server.resourceProvider.pathContext;
       final fileExtension = pathContext.extension(path.result);
 
+      final maxResults = server.clientConfiguration
+          .forResource(path.result)
+          .maxCompletionItems;
+
+      CompletionPerformance? completionPerformance;
       if (fileExtension == '.dart' && !unit.isError) {
+        final result = unit.result;
+        var performanceOperation = OperationPerformanceImpl('<root>');
+        completionPerformance = CompletionPerformance(
+          operation: performanceOperation,
+          path: result.path,
+          content: result.content,
+          offset: offset,
+        );
+        server.performanceStats.completion.add(completionPerformance);
+
         serverResultsFuture = _getServerDartItems(
           clientCapabilities,
-          unit.result,
+          result,
+          completionPerformance,
+          performanceOperation,
           offset,
           triggerCharacter,
           token,
+          maxResults: maxResults,
         );
       } else if (fileExtension == '.yaml') {
         YamlCompletionGenerator? generator;
@@ -125,14 +142,25 @@
       if (serverResults.isError) return serverResults;
       if (pluginResults.isError) return pluginResults;
 
+      final untruncatedItems = serverResults.result.items
+          .followedBy(pluginResults.result.items)
+          .toList();
+
+      final truncatedItems = untruncatedItems.length > maxResults
+          ? (untruncatedItems..sort(sortTextComparer)).sublist(0, maxResults)
+          : untruncatedItems;
+
+      // If we're tracing performance (only Dart), record the number of results
+      // after truncation.
+      completionPerformance?.transmittedSuggestionCount = truncatedItems.length;
+
       return success(CompletionList(
         // If any set of the results is incomplete, the whole batch must be
         // marked as such.
         isIncomplete: serverResults.result.isIncomplete ||
-            pluginResults.result.isIncomplete,
-        items: serverResults.result.items
-            .followedBy(pluginResults.result.items)
-            .toList(),
+            pluginResults.result.isIncomplete ||
+            truncatedItems.length != untruncatedItems.length,
+        items: truncatedItems,
       ));
     });
   }
@@ -233,25 +261,19 @@
   Future<ErrorOr<CompletionList>> _getServerDartItems(
     LspClientCapabilities capabilities,
     ResolvedUnitResult unit,
+    CompletionPerformance completionPerformance,
+    OperationPerformanceImpl operationPerformance,
     int offset,
     String? triggerCharacter,
-    CancellationToken token,
-  ) async {
+    CancellationToken token, {
+    required int maxResults,
+  }) async {
     final useSuggestionSets =
         suggestFromUnimportedLibraries && capabilities.applyEdit;
 
-    var performance = OperationPerformanceImpl('<root>');
-    return await performance.runAsync(
+    return await operationPerformance.runAsync(
       'request',
       (performance) async {
-        final completionPerformance = CompletionPerformance(
-          operation: performance,
-          path: unit.path,
-          content: unit.content,
-          offset: offset,
-        );
-        server.performanceStats.completion.add(completionPerformance);
-
         final completionRequest = DartCompletionRequest.forResolvedUnit(
           resolvedUnit: unit,
           offset: offset,
@@ -475,7 +497,9 @@
               .where((e) => fuzzyMatcher.score(e.filterText ?? e.label) > 0)
               .toList();
 
-          completionPerformance.suggestionCount = results.length;
+          // Transmitted count will be set after combining with plugins.
+          completionPerformance.computedSuggestionCount =
+              matchingResults.length;
 
           return success(
               CompletionList(isIncomplete: false, items: matchingResults));
@@ -621,4 +645,37 @@
 
     return true; // Any other trigger character can be handled always.
   }
+
+  /// Compares [CompletionItem]s by the `sortText` field, which is derived from
+  /// relevance.
+  ///
+  /// For items with the same relevance, shorter items are sorted first so that
+  /// truncation always removes longer items first (which can be included by
+  /// typing more of their characters).
+  static int sortTextComparer(CompletionItem item1, CompletionItem item2) {
+    // Note: It should never be the case that we produce items without sortText
+    // but if they're null, fall back to label which is what the client would do
+    // when sorting.
+    final item1Text = item1.sortText ?? item1.label;
+    final item2Text = item2.sortText ?? item2.label;
+
+    // If both items have the same text, this means they had the same relevance.
+    // In this case, sort by the length of the name ascending, so that shorter
+    // items are first. This is because longer items can be obtained by typing
+    // additional characters where shorter ones may not.
+    //
+    // For example, with:
+    //   - String aaa1;
+    //   - String aaa2;
+    //   - ...
+    //   - String aaa(N); // up to past the truncation amount
+    //   - String aaa;    // declared last, same prefix
+    //
+    // Typing 'aaa' should not allow 'aaa' to be truncated before 'aaa1'.
+    if (item1Text == item2Text) {
+      return item1.label.length.compareTo(item2.label.length);
+    }
+
+    return item1Text.compareTo(item2Text);
+  }
 }
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
index fa99c2b..ef497f6 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
@@ -66,8 +66,7 @@
     _latestCompletionItem = item;
     while (item == _latestCompletionItem && timer.elapsed < timeout) {
       try {
-        final analysisDriver = server.getAnalysisDriver(file);
-        final session = analysisDriver?.currentSession;
+        final session = await server.getAnalysisSession(file);
 
         // We shouldn't not get a driver/session, but if we did perhaps the file
         // was removed from the analysis set so assume the request is no longer
@@ -199,8 +198,7 @@
     final clientCapabilities = server.clientCapabilities;
     if (clientCapabilities == null) {
       // This should not happen unless a client misbehaves.
-      return error(ErrorCodes.ServerNotInitialized,
-          'Requests not before server is initilized');
+      return serverNotInitializedError;
     }
 
     final file = data.file;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
index 36aff2e..1383b80 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
@@ -61,7 +61,7 @@
       computeDartNavigation(
           server.resourceProvider, collector, unit, offset, 0);
       if (supportsLocationLink) {
-        _updateTargetsWithCodeLocations(collector);
+        await _updateTargetsWithCodeLocations(collector);
       }
       collector.createRegions();
     }
@@ -76,8 +76,7 @@
     final clientCapabilities = server.clientCapabilities;
     if (clientCapabilities == null) {
       // This should not happen unless a client misbehaves.
-      return error(ErrorCodes.ServerNotInitialized,
-          'Requests not before server is initilized');
+      return serverNotInitializedError;
     }
 
     final supportsLocationLink = clientCapabilities.definitionLocationLink;
@@ -180,7 +179,7 @@
   }
 
   /// Get the location of the code (excluding leading doc comments) for this element.
-  protocol.Location? _getCodeLocation(Element element) {
+  Future<protocol.Location?> _getCodeLocation(Element element) async {
     var codeElement = element;
     // For synthetic getters created for fields, we need to access the associated
     // variable to get the codeOffset/codeLength.
@@ -206,7 +205,7 @@
     // Read the declaration so we can get the offset after the doc comments.
     // TODO(dantup): Skip this for parts (getParsedLibrary will throw), but find
     // a better solution.
-    final declaration = _parsedDeclaration(codeElement);
+    final declaration = await _parsedDeclaration(codeElement);
     var node = declaration?.node;
     if (node is VariableDeclaration) {
       node = node.parent;
@@ -244,9 +243,11 @@
         : null;
   }
 
-  void _updateTargetsWithCodeLocations(NavigationCollectorImpl collector) {
+  Future<void> _updateTargetsWithCodeLocations(
+    NavigationCollectorImpl collector,
+  ) async {
     for (var targetToUpdate in collector.targetsToUpdate) {
-      var codeLocation = _getCodeLocation(targetToUpdate.element);
+      var codeLocation = await _getCodeLocation(targetToUpdate.element);
       if (codeLocation != null) {
         targetToUpdate.target
           ..codeOffset = codeLocation.offset
@@ -255,7 +256,9 @@
     }
   }
 
-  static ElementDeclarationResult? _parsedDeclaration(Element element) {
+  static Future<ElementDeclarationResult?> _parsedDeclaration(
+    Element element,
+  ) async {
     var session = element.session;
     if (session == null) {
       return null;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart
index 06fdd44..ed196a1 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_color_presentation.dart
@@ -123,6 +123,7 @@
         SourceRange(editStart.result, editEnd.result - editStart.result);
 
     final sessionHelper = AnalysisSessionHelper(unit.session);
+    final analysisContext = unit.session.analysisContext;
     final flutter = Flutter.instance;
     final colorType = await sessionHelper.getClass(flutter.widgetsUri, 'Color');
     if (colorType == null) {
@@ -132,6 +133,13 @@
       return success([]);
     }
 
+    // If this file is outside of analysis roots, we cannot build edits for it
+    // so return null to signal to the client that it should not try to modify
+    // the source.
+    if (!analysisContext.contextRoot.isAnalyzed(unit.path)) {
+      return success([]);
+    }
+
     final colorValue = _colorValueForComponents(alpha, red, green, blue);
     final colorValueHex =
         '0x${colorValue.toRadixString(16).toUpperCase().padLeft(8, '0')}';
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
index 02c8a93..cdc6fa5 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
@@ -10,6 +10,7 @@
 import 'package:analysis_server/src/lsp/handlers/commands/perform_refactor.dart';
 import 'package:analysis_server/src/lsp/handlers/commands/send_workspace_edit.dart';
 import 'package:analysis_server/src/lsp/handlers/commands/sort_members.dart';
+import 'package:analysis_server/src/lsp/handlers/commands/validate_refactor.dart';
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 import 'package:analysis_server/src/lsp/progress.dart';
@@ -25,6 +26,7 @@
           Commands.organizeImports: OrganizeImportsCommandHandler(server),
           Commands.fixAll: FixAllCommandHandler(server),
           Commands.performRefactor: PerformRefactorCommandHandler(server),
+          Commands.validateRefactor: ValidateRefactorCommandHandler(server),
           Commands.sendWorkspaceEdit: SendWorkspaceEditCommandHandler(server),
         },
         super(server);
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_folding.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_folding.dart
index 1149fff..a14bb7d 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_folding.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_folding.dart
@@ -30,7 +30,7 @@
       final partialResults = <List<FoldingRegion>>[];
       LineInfo? lineInfo;
 
-      final unit = server.getParsedUnit(path);
+      final unit = await server.getParsedUnit(path);
       if (unit != null) {
         lineInfo = unit.lineInfo;
 
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
index 84da8f2..528c52b 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
@@ -20,14 +20,14 @@
   LspJsonHandler<DocumentOnTypeFormattingParams> get jsonHandler =>
       DocumentOnTypeFormattingParams.jsonHandler;
 
-  ErrorOr<List<TextEdit>?> formatFile(String path) {
+  Future<ErrorOr<List<TextEdit>?>> formatFile(String path) async {
     final file = server.resourceProvider.getFile(path);
     if (!file.exists) {
       return error(
           ServerErrorCodes.InvalidFilePath, 'File does not exist', path);
     }
 
-    final result = server.getParsedUnit(path);
+    final result = await server.getParsedUnit(path);
     if (result == null || result.errors.isNotEmpty) {
       return success(null);
     }
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
index f28ef15..44143fc 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
@@ -20,14 +20,14 @@
   LspJsonHandler<DocumentRangeFormattingParams> get jsonHandler =>
       DocumentRangeFormattingParams.jsonHandler;
 
-  ErrorOr<List<TextEdit>?> formatRange(String path, Range range) {
+  Future<ErrorOr<List<TextEdit>?>> formatRange(String path, Range range) async {
     final file = server.resourceProvider.getFile(path);
     if (!file.exists) {
       return error(
           ServerErrorCodes.InvalidFilePath, 'File does not exist', path);
     }
 
-    final result = server.getParsedUnit(path);
+    final result = await server.getParsedUnit(path);
     if (result == null || result.errors.isNotEmpty) {
       return success(null);
     }
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
index 6ff4313..ea279ad 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
@@ -20,14 +20,14 @@
   LspJsonHandler<DocumentFormattingParams> get jsonHandler =>
       DocumentFormattingParams.jsonHandler;
 
-  ErrorOr<List<TextEdit>?> formatFile(String path) {
+  Future<ErrorOr<List<TextEdit>?>> formatFile(String path) async {
     final file = server.resourceProvider.getFile(path);
     if (!file.exists) {
       return error(
           ServerErrorCodes.InvalidFilePath, 'File does not exist', path);
     }
 
-    final result = server.getParsedUnit(path);
+    final result = await server.getParsedUnit(path);
     if (result == null || result.errors.isNotEmpty) {
       return success(null);
     }
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_select_range.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_select_range.dart
index 193002f..0a97e4d 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_select_range.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_select_range.dart
@@ -39,7 +39,7 @@
         return success(null);
       }
 
-      final unit = requireUnresolvedUnit(path);
+      final unit = await requireUnresolvedUnit(path);
       final positions = params.positions;
       final offsets = await unit.mapResult((unit) =>
           ErrorOr.all(positions.map((pos) => toOffset(unit.lineInfo, pos))));
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
index 25a78ca..cb0344b 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
@@ -32,8 +32,7 @@
     final clientCapabilities = server.clientCapabilities;
     if (clientCapabilities == null) {
       // This should not happen unless a client misbehaves.
-      return error(ErrorCodes.ServerNotInitialized,
-          'Requests not before server is initilized');
+      return serverNotInitializedError;
     }
 
     // If triggered automatically by pressing the trigger character, we will
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
index abc53cf..ceb4204 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
@@ -36,6 +36,7 @@
 import 'package:analysis_server/src/lsp/handlers/handler_shutdown.dart';
 import 'package:analysis_server/src/lsp/handlers/handler_signature_help.dart';
 import 'package:analysis_server/src/lsp/handlers/handler_text_document_changes.dart';
+import 'package:analysis_server/src/lsp/handlers/handler_type_definition.dart';
 import 'package:analysis_server/src/lsp/handlers/handler_will_rename_files.dart';
 import 'package:analysis_server/src/lsp/handlers/handler_workspace_configuration.dart';
 import 'package:analysis_server/src/lsp/handlers/handler_workspace_symbols.dart';
@@ -82,6 +83,7 @@
     registerHandler(DocumentColorPresentationHandler(server));
     registerHandler(SignatureHelpHandler(server));
     registerHandler(DefinitionHandler(server));
+    registerHandler(TypeDefinitionHandler(server));
     registerHandler(SuperHandler(server));
     registerHandler(ReferencesHandler(server));
     registerHandler(ImplementationHandler(server));
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_type_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_type_definition.dart
new file mode 100644
index 0000000..f32c4c1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_type_definition.dart
@@ -0,0 +1,166 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/handlers/handlers.dart';
+import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/dart/element/element.dart' show ElementImpl;
+import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
+import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
+
+typedef _LocationsOrLinks = Either2<List<Location>, List<LocationLink>>;
+
+class TypeDefinitionHandler
+    extends MessageHandler<TypeDefinitionParams, _LocationsOrLinks>
+    with LspPluginRequestHandlerMixin {
+  static const _emptyResult = _LocationsOrLinks.t1([]);
+
+  TypeDefinitionHandler(LspAnalysisServer server) : super(server);
+
+  @override
+  Method get handlesMessage => Method.textDocument_typeDefinition;
+
+  @override
+  LspJsonHandler<TypeDefinitionParams> get jsonHandler =>
+      TypeDefinitionParams.jsonHandler;
+
+  @override
+  Future<ErrorOr<_LocationsOrLinks>> handle(
+      TypeDefinitionParams params, CancellationToken token) async {
+    if (!isDartDocument(params.textDocument)) {
+      return success(_emptyResult);
+    }
+
+    final clientCapabilities = server.clientCapabilities;
+    if (clientCapabilities == null) {
+      // This should not happen unless a client misbehaves.
+      return serverNotInitializedError;
+    }
+
+    /// Whether the client supports `LocationLink` results instead of the
+    /// original `Location`. `LocationLink`s can include an additional `Range`
+    /// to distinguish between codeRange and nameRange (selectionRange), and
+    /// also an `originSelectionRange` that tells the client which range the
+    /// result is valid for.
+    final supportsLocationLink = clientCapabilities.typeDefinitionLocationLink;
+    final pos = params.position;
+    final path = pathOfDoc(params.textDocument);
+
+    return path.mapResult((path) async {
+      final result = await server.getResolvedUnit(path);
+      if (result == null) {
+        return success(_emptyResult);
+      }
+
+      final offset = toOffset(result.lineInfo, pos);
+      return offset.mapResult((offset) async {
+        final node = NodeLocator(offset).searchWithin(result.unit);
+        if (node == null) {
+          return success(_emptyResult);
+        }
+
+        final type = node is Expression ? _getType(node) : null;
+        final element = type?.element;
+        if (element is! ElementImpl) {
+          return success(_emptyResult);
+        }
+
+        // Obtain a `LineInfo` for the targets file to map offsets.
+        final targetUnitElement =
+            element.thisOrAncestorOfType<CompilationUnitElement>();
+        final targetLineInfo = targetUnitElement?.lineInfo;
+        if (targetLineInfo == null) {
+          return success(_emptyResult);
+        }
+
+        final converter = AnalyzerConverter();
+        final location = converter.locationFromElement(element);
+        if (location == null) {
+          return success(_emptyResult);
+        }
+
+        if (supportsLocationLink) {
+          return success(_LocationsOrLinks.t2([
+            _toLocationLink(
+                result.lineInfo, targetLineInfo, node, element, location)
+          ]));
+        } else {
+          return success(
+              _LocationsOrLinks.t1([_toLocation(location, targetLineInfo)]));
+        }
+      });
+    });
+  }
+
+  /// Creates an LSP [Location] for the server [location].
+  Location _toLocation(plugin.Location location, LineInfo lineInfo) {
+    return Location(
+      uri: Uri.file(location.file).toString(),
+      range: toRange(lineInfo, location.offset, location.length),
+    );
+  }
+
+  /// Creates an LSP [LocationLink] for the server [targetLocation].
+  ///
+  /// Uses [originLineInfo] and [originNode] to compute `originSelectionRange`
+  /// and [targetLineInfo] and [targetElement] for code ranges.
+  LocationLink _toLocationLink(
+    LineInfo originLineInfo,
+    LineInfo targetLineInfo,
+    AstNode originNode,
+    ElementImpl targetElement,
+    plugin.Location targetLocation,
+  ) {
+    final nameRange =
+        toRange(targetLineInfo, targetLocation.offset, targetLocation.length);
+
+    final codeOffset = targetElement.codeOffset;
+    final codeLength = targetElement.codeLength;
+    final codeRange = codeOffset != null && codeLength != null
+        ? toRange(targetLineInfo, codeOffset, codeLength)
+        : nameRange;
+
+    return LocationLink(
+      originSelectionRange:
+          toRange(originLineInfo, originNode.offset, originNode.length),
+      targetUri: Uri.file(targetLocation.file).toString(),
+      targetRange: codeRange,
+      targetSelectionRange: nameRange,
+    );
+  }
+
+  /// Returns the [DartType] most appropriate for navigating to from [node] when
+  /// invoking Go to Type Definition.
+  static DartType? _getType(Expression node) {
+    if (node is SimpleIdentifier) {
+      final element = node.staticElement;
+      if (element is ClassElement) {
+        return element.thisType;
+      } else if (element is VariableElement) {
+        if (node.inDeclarationContext()) {
+          return element.type;
+        }
+        final parent = node.parent?.parent;
+        if (parent is NamedExpression && parent.name.label == node) {
+          return element.type;
+        }
+      } else if (node.inSetterContext()) {
+        final writeElement = node.writeElement;
+        if (writeElement is PropertyAccessorElement) {
+          return writeElement.variable.type;
+        }
+      }
+    }
+
+    return node.staticType;
+  }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
index 435013c..31ea25d 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
@@ -25,8 +25,7 @@
     final clientCapabilities = server.clientCapabilities;
     if (clientCapabilities == null) {
       // This should not happen unless a client misbehaves.
-      return error(ErrorCodes.ServerNotInitialized,
-          'Requests not before server is initilized');
+      return serverNotInitializedError;
     }
 
     // Respond to empty queries with an empty list. The spec says this should
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart b/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
index ddd82dc..9d3225d 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
@@ -44,6 +44,9 @@
   final fileModifiedError = error<R>(ErrorCodes.ContentModified,
       'Document was modified before operation completed', null);
 
+  final serverNotInitializedError = error<R>(ErrorCodes.ServerNotInitialized,
+      'Request not valid before server is initialized');
+
   LspAnalysisServer get server;
 
   bool fileHasBeenModified(String path, num? clientVersion) {
@@ -83,8 +86,8 @@
     return success(result);
   }
 
-  ErrorOr<ParsedUnitResult> requireUnresolvedUnit(String path) {
-    final result = server.getParsedUnit(path);
+  Future<ErrorOr<ParsedUnitResult>> requireUnresolvedUnit(String path) async {
+    final result = await server.getParsedUnit(path);
     if (result == null) {
       if (server.isAnalyzed(path)) {
         // If the file was being analyzed and we got a null result, that usually
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
index d0300bb..12284b0 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -38,6 +38,7 @@
 import 'package:analysis_server/src/utilities/process.dart';
 import 'package:analyzer/dart/analysis/context_locator.dart';
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/exception/exception.dart';
 import 'package:analyzer/file_system/file_system.dart';
@@ -355,6 +356,13 @@
         } else {
           showErrorMessageToUser('Unknown message type');
         }
+      } on InconsistentAnalysisException {
+        sendErrorResponse(
+            message,
+            ResponseError(
+              code: ErrorCodes.ContentModified,
+              message: 'Document was modified before operation completed',
+            ));
       } catch (error, stackTrace) {
         final errorMessage = message is ResponseMessage
             ? 'An error occurred while handling the response to request ${message.id}'
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index 9026e05..fa6a104 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -119,51 +119,74 @@
   return edit;
 }
 
-/// Creates a [lsp.WorkspaceEdit] from a [server.SourceChange] that can include
-/// experimental [server.SnippetTextEdit]s if the client has indicated support
-/// for these in the experimental section of their client capabilities.
+/// Creates a [lsp.WorkspaceEdit] from a [server.SourceChange].
+///
+/// Can return experimental [server.SnippetTextEdit]s if the following are true:
+/// - the client has indicated support for in the experimental section of their
+///   client capabilities, and
+/// - [allowSnippets] is true, and
+/// - [change] contains only a single edit to the single file [filePath]
+/// - [lineInfo] is provided (which should be for the single edited file)
 ///
 /// Note: This code will fetch the version of each document being modified so
 /// it's important to call this immediately after computing edits to ensure
 /// the document is not modified before the version number is read.
 lsp.WorkspaceEdit createWorkspaceEdit(
-    lsp.LspAnalysisServer server, server.SourceChange change) {
+  lsp.LspAnalysisServer server,
+  server.SourceChange change, {
+  // The caller must specify whether snippets are valid here for where they're
+  // sending this edit. Right now, support is limited to CodeActions.
+  bool allowSnippets = false,
+  String? filePath,
+  LineInfo? lineInfo,
+}) {
   // In order to return snippets, we must ensure we are only modifying a single
-  // existing file with a single edit and that there is a linked edit group with
-  // only one position and no suggestions.
-  if (!server.clientCapabilities!.experimentalSnippetTextEdit ||
+  // existing file with a single edit and that there is either a selection or a
+  // linked edit group (otherwise there's no value in snippets).
+  if (!allowSnippets ||
+      !server.clientCapabilities!.experimentalSnippetTextEdit ||
+      !server.clientCapabilities!.documentChanges ||
+      filePath == null ||
+      lineInfo == null ||
       change.edits.length != 1 ||
-      change.edits.first.fileStamp == -1 || // new file
-      change.edits.first.edits.length != 1 ||
-      change.linkedEditGroups.isEmpty ||
-      change.linkedEditGroups.first.positions.length != 1 ||
-      change.linkedEditGroups.first.suggestions.isNotEmpty) {
+      change.edits.single.fileStamp == -1 || // new file
+      change.edits.single.file != filePath ||
+      change.edits.single.edits.length != 1 ||
+      (change.selection == null && change.linkedEditGroups.isEmpty)) {
     return createPlainWorkspaceEdit(server, change.edits);
   }
 
-  // Additionally, the selection must fall within the edit offset.
-  final edit = change.edits.first.edits.first;
-  final selectionOffset = change.linkedEditGroups.first.positions.first.offset;
-  final selectionLength = change.linkedEditGroups.first.length;
+  final fileEdit = change.edits.single;
+  final snippetEdits = toSnippetTextEdits(
+    fileEdit.file,
+    fileEdit,
+    change.linkedEditGroups,
+    lineInfo,
+    selectionOffset: change.selection?.offset,
+  );
 
-  if (selectionOffset < edit.offset ||
-      selectionOffset + selectionLength > edit.offset + edit.length) {
-    return createPlainWorkspaceEdit(server, change.edits);
-  }
+  // Compile the edits into a TextDocumentEdit for this file.
+  final textDocumentEdit = lsp.TextDocumentEdit(
+    textDocument: server.getVersionedDocumentIdentifier(fileEdit.file),
+    edits: snippetEdits
+        .map((e) => Either3<lsp.SnippetTextEdit, lsp.AnnotatedTextEdit,
+            lsp.TextEdit>.t1(e))
+        .toList(),
+  );
 
-  return toWorkspaceEdit(
-      server.clientCapabilities!,
-      change.edits
-          .map((e) => FileEditInformation(
-                server.getVersionedDocumentIdentifier(e.file),
-                // We should never produce edits for a file with no LineInfo.
-                server.getLineInfo(e.file)!,
-                e.edits,
-                selectionOffsetRelative: selectionOffset - edit.offset,
-                selectionLength: selectionLength,
-                newFile: e.fileStamp == -1,
-              ))
-          .toList());
+  // Convert to the union that documentChanges require.
+  final textDocumentEditsAsUnion = Either4<lsp.TextDocumentEdit, lsp.CreateFile,
+      lsp.RenameFile, lsp.DeleteFile>.t1(textDocumentEdit);
+
+  // Convert to the union that documentChanges is.
+  final documentChanges = Either2<
+      List<lsp.TextDocumentEdit>,
+      List<
+          Either4<lsp.TextDocumentEdit, lsp.CreateFile, lsp.RenameFile,
+              lsp.DeleteFile>>>.t2([textDocumentEditsAsUnion]);
+
+  /// Add the textDocumentEdit to a WorkspaceEdit.
+  return lsp.WorkspaceEdit(documentChanges: documentChanges);
 }
 
 lsp.CompletionItemKind? declarationKindToCompletionItemKind(
@@ -323,8 +346,9 @@
     // we can assume if an item is callable it's probably being used in a context
     // that can invoke it.
     isInvocation: isCallable,
-    defaultArgumentListString: declaration.defaultArgumentListString,
-    defaultArgumentListTextRanges: declaration.defaultArgumentListTextRanges,
+    requiredArgumentListString: declaration.defaultArgumentListString,
+    requiredArgumentListTextRanges: declaration.defaultArgumentListTextRanges,
+    hasOptionalParameters: declaration.parameterNames?.isNotEmpty ?? false,
     completion: completion,
     selectionOffset: 0,
     selectionLength: 0,
@@ -1156,8 +1180,9 @@
     completeFunctionCalls: completeFunctionCalls,
     isCallable: isCallable,
     isInvocation: isInvocation,
-    defaultArgumentListString: suggestion.defaultArgumentListString,
-    defaultArgumentListTextRanges: suggestion.defaultArgumentListTextRanges,
+    requiredArgumentListString: suggestion.defaultArgumentListString,
+    requiredArgumentListTextRanges: suggestion.defaultArgumentListTextRanges,
+    hasOptionalParameters: suggestion.parameterNames?.isNotEmpty ?? false,
     completion: suggestion.completion,
     selectionOffset: suggestion.selectionOffset,
     selectionLength: suggestion.selectionLength,
@@ -1641,8 +1666,9 @@
   required bool completeFunctionCalls,
   required bool isCallable,
   required bool isInvocation,
-  required String? defaultArgumentListString,
-  required List<int>? defaultArgumentListTextRanges,
+  required String? requiredArgumentListString,
+  required List<int>? requiredArgumentListTextRanges,
+  required bool hasOptionalParameters,
   required String completion,
   required int selectionOffset,
   required int selectionLength,
@@ -1669,13 +1695,16 @@
         isInvocation) {
       insertTextFormat = lsp.InsertTextFormat.Snippet;
       final hasRequiredParameters =
-          (defaultArgumentListTextRanges?.length ?? 0) > 0;
+          requiredArgumentListTextRanges?.isNotEmpty ?? false;
       final functionCallSuffix =
-          hasRequiredParameters && defaultArgumentListString != null
+          hasRequiredParameters && requiredArgumentListString != null
               ? buildSnippetStringWithTabStops(
-                  defaultArgumentListString, defaultArgumentListTextRanges)
-              // No required params still gets a final tab stop in the parens.
-              : SnippetBuilder.finalTabStop;
+                  requiredArgumentListString, requiredArgumentListTextRanges)
+              // Optional params still gets a final tab stop in the parens.
+              : hasOptionalParameters
+                  ? SnippetBuilder.finalTabStop
+                  // And no parameters at all we skip the tabstop in the parens.
+                  : '';
       insertText =
           '${SnippetBuilder.escapeSnippetPlainText(insertText)}($functionCallSuffix)';
     } else if (selectionOffset != 0 &&
diff --git a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
index 0b28c2c..9146784 100644
--- a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
+++ b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
@@ -39,6 +39,7 @@
     Method.textDocument_rename,
     Method.textDocument_foldingRange,
     Method.textDocument_selectionRange,
+    Method.textDocument_typeDefinition,
     // workspace.fileOperations covers all file operation methods but we only
     // support this one.
     Method.workspace_willRenameFiles,
@@ -109,6 +110,9 @@
   bool get textSync =>
       _capabilities.textDocument?.synchronization?.dynamicRegistration ?? false;
 
+  bool get typeDefinition =>
+      _capabilities.textDocument?.typeDefinition?.dynamicRegistration ?? false;
+
   bool get typeFormatting =>
       _capabilities.textDocument?.onTypeFormatting?.dynamicRegistration ??
       false;
@@ -450,6 +454,13 @@
       TextDocumentRegistrationOptions(documentSelector: fullySupportedTypes),
     );
     register(
+      dynamicRegistrations.typeDefinition,
+      Method.textDocument_typeDefinition,
+      TextDocumentRegistrationOptions(
+        documentSelector: [dartFiles], // This one is currently Dart-specific
+      ),
+    );
+    register(
       dynamicRegistrations.implementation,
       Method.textDocument_implementation,
       TextDocumentRegistrationOptions(documentSelector: fullySupportedTypes),
diff --git a/pkg/analysis_server/lib/src/lsp/snippets.dart b/pkg/analysis_server/lib/src/lsp/snippets.dart
index e88dcaa..0c9d5c6 100644
--- a/pkg/analysis_server/lib/src/lsp/snippets.dart
+++ b/pkg/analysis_server/lib/src/lsp/snippets.dart
@@ -85,6 +85,11 @@
         // Use the index as an ID to keep all related positions together (so
         // the remain "linked").
         linkedGroupId: index,
+        // If there is no selection, no tabstops, and only a single edit group
+        // allow it to be the final tabstop.
+        isFinal: selectionOffset == null &&
+            (tabStopOffsetLengthPairs?.isEmpty ?? false) &&
+            editGroups?.length == 1,
       ),
     );
   }
diff --git a/pkg/analysis_server/lib/src/plugin/plugin_locator.dart b/pkg/analysis_server/lib/src/plugin/plugin_locator.dart
index 4453aea..f5e1247 100644
--- a/pkg/analysis_server/lib/src/plugin/plugin_locator.dart
+++ b/pkg/analysis_server/lib/src/plugin/plugin_locator.dart
@@ -14,9 +14,6 @@
   /// directory.
   static const String defaultPluginFolderName = 'analyzer_plugin';
 
-  /// The name of the `pubspec.yaml` file.
-  static const String pubspecFileName = 'pubspec.yaml';
-
   /// The name of the `tools` directory, in which the default plugin directory
   /// is located.
   static const String toolsFolderName = 'tools';
diff --git a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
index fa6eee9..ae25ec1 100644
--- a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
+++ b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
@@ -14,6 +14,8 @@
 import 'package:analyzer/instrumentation/instrumentation.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
 import 'package:analyzer/src/util/glob.dart';
 import 'package:analyzer/src/workspace/bazel.dart';
 import 'package:analyzer/src/workspace/gn.dart';
@@ -106,6 +108,13 @@
   String toString() => message;
 }
 
+class PluginFiles {
+  final File execution;
+  final File packages;
+
+  PluginFiles(this.execution, this.packages);
+}
+
 /// Information about a single plugin.
 abstract class PluginInfo {
   /// The object used to manage the receiving and sending of notifications.
@@ -314,9 +323,9 @@
     var isNew = false;
     if (plugin == null) {
       isNew = true;
-      List<String> pluginPaths;
+      PluginFiles pluginFiles;
       try {
-        pluginPaths = pathsFor(path);
+        pluginFiles = filesFor(path);
       } catch (exception, stackTrace) {
         plugin = DiscoveredPluginInfo(
             path, '', '', notificationManager, instrumentationService);
@@ -324,8 +333,12 @@
         _pluginMap[path] = plugin;
         return;
       }
-      plugin = DiscoveredPluginInfo(path, pluginPaths[0], pluginPaths[1],
-          notificationManager, instrumentationService);
+      plugin = DiscoveredPluginInfo(
+          path,
+          pluginFiles.execution.path,
+          pluginFiles.packages.path,
+          notificationManager,
+          instrumentationService);
       _pluginMap[path] = plugin;
       try {
         var session = await plugin.start(byteStorePath, sdkPath);
@@ -412,24 +425,24 @@
     return responses;
   }
 
-  /// Return the execution path and .packages path associated with the plugin at
-  /// the given [path]. Throw a [PluginException] if there is a problem that
-  /// prevents the plugin from being executing.
+  /// Return the files associated with the plugin at the given [pluginPath].
+  /// Throw a [PluginException] if there is a problem that prevents the plugin
+  /// from being executing.
   @visibleForTesting
-  List<String> pathsFor(String pluginPath) {
+  PluginFiles filesFor(String pluginPath) {
     var pluginFolder = resourceProvider.getFolder(pluginPath);
-    var pubspecFile = pluginFolder.getChildAssumingFile('pubspec.yaml');
+    var pubspecFile = pluginFolder.getChildAssumingFile(file_paths.pubspecYaml);
     if (!pubspecFile.exists) {
       // If there's no pubspec file, then we don't need to copy the package
       // because we won't be running pub.
-      return _computePaths(pluginFolder);
+      return _computeFiles(pluginFolder);
     }
     var workspace = BazelWorkspace.find(resourceProvider, pluginFolder.path) ??
         GnWorkspace.find(resourceProvider, pluginFolder.path);
     if (workspace != null) {
       // Similarly, we won't be running pub if we're in a workspace because
       // there is exactly one version of each package.
-      return _computePaths(pluginFolder, workspace: workspace);
+      return _computeFiles(pluginFolder, workspace: workspace);
     }
     //
     // Copy the plugin directory to a unique subdirectory of the plugin
@@ -446,10 +459,10 @@
     if (parentFolder.exists) {
       var executionFolder =
           parentFolder.getChildAssumingFolder(pluginFolder.shortName);
-      return _computePaths(executionFolder, pubCommand: 'upgrade');
+      return _computeFiles(executionFolder, pubCommand: 'upgrade');
     }
     var executionFolder = pluginFolder.copyTo(parentFolder);
-    return _computePaths(executionFolder, pubCommand: 'get');
+    return _computeFiles(executionFolder, pubCommand: 'get');
   }
 
   /// Return a list of all of the plugins that are currently associated with the
@@ -602,11 +615,11 @@
     }));
   }
 
-  /// Compute the paths to be returned by the enclosing method given that the
+  /// Compute the files to be returned by the enclosing method given that the
   /// plugin should exist in the given [pluginFolder].
   ///
   /// Runs pub if [pubCommand] is provided and not null.
-  List<String> _computePaths(Folder pluginFolder,
+  PluginFiles _computeFiles(Folder pluginFolder,
       {String? pubCommand, Workspace? workspace}) {
     var pluginFile = pluginFolder
         .getChildAssumingFolder('bin')
@@ -615,7 +628,9 @@
       throw PluginException('File "${pluginFile.path}" does not exist.');
     }
     String? reason;
-    File? packagesFile = pluginFolder.getChildAssumingFile('.packages');
+    File? packagesFile = pluginFolder
+        .getChildAssumingFolder(file_paths.dotDartTool)
+        .getChildAssumingFile(file_paths.packageConfigJson);
     if (pubCommand != null) {
       var vmPath = Platform.executable;
       var pubPath = path.join(path.dirname(vmPath), 'pub');
@@ -647,7 +662,8 @@
         packagesFile =
             _createPackagesFile(pluginFolder, workspace.packageUriResolver);
         if (packagesFile == null) {
-          reason = 'Could not create .packages file in workspace $workspace.';
+          var name = file_paths.packageConfigJson;
+          reason = 'Could not create $name file in workspace $workspace.';
         }
       } else {
         reason = 'Could not create "${packagesFile.path}".';
@@ -658,7 +674,7 @@
       reason ??= 'Could not create packages file for an unknown reason.';
       throw PluginException(reason);
     }
-    return <String>[pluginFile.path, packagesFile.path];
+    return PluginFiles(pluginFile, packagesFile);
   }
 
   WatchEventType _convertChangeType(watcher.ChangeType type) {
@@ -678,53 +694,73 @@
     return WatchEvent(_convertChangeType(watchEvent.type), watchEvent.path);
   }
 
-  /// Return a temporary `.packages` file that is appropriate for the plugin in
-  /// the given [pluginFolder]. The [packageUriResolver] is used to determine
-  /// the location of the packages that need to be included in the packages
-  /// file.
+  /// Return a temporary `package_config.json` file that is appropriate for
+  /// the plugin in the given [pluginFolder]. The [packageUriResolver] is
+  /// used to determine the location of the packages that need to be included
+  /// in the packages file.
   File? _createPackagesFile(
       Folder pluginFolder, UriResolver packageUriResolver) {
     var pluginPath = pluginFolder.path;
     var stateFolder = resourceProvider.getStateLocation('.plugin_manager')!;
-    var stateName = _uniqueDirectoryName(pluginPath) + '.packages';
+    var stateName = '${_uniqueDirectoryName(pluginPath)}.packages';
     var packagesFile = stateFolder.getChildAssumingFile(stateName);
     if (!packagesFile.exists) {
-      var pluginPubspec = pluginFolder.getChildAssumingFile('pubspec.yaml');
+      var pluginPubspec =
+          pluginFolder.getChildAssumingFile(file_paths.pubspecYaml);
       if (!pluginPubspec.exists) {
         return null;
       }
 
       try {
-        var visitedPackages = <String, String>{};
+        var visitedPackageNames = <String>{};
+        var packages = <_Package>[];
         var context = resourceProvider.pathContext;
-        visitedPackages[context.basename(pluginPath)] =
-            context.join(pluginFolder.path, 'lib');
+        packages.add(
+          _Package(
+            context.basename(pluginPath),
+            pluginFolder,
+          ),
+        );
         var pubspecFiles = <File>[];
         pubspecFiles.add(pluginPubspec);
         while (pubspecFiles.isNotEmpty) {
           var pubspecFile = pubspecFiles.removeLast();
-          for (var packageName in _readDependecies(pubspecFile)) {
-            if (!visitedPackages.containsKey(packageName)) {
+          for (var packageName in _readDependencies(pubspecFile)) {
+            if (visitedPackageNames.add(packageName)) {
               var uri = Uri.parse('package:$packageName/$packageName.dart');
               var packageSource = packageUriResolver.resolveAbsolute(uri);
               if (packageSource != null) {
-                var libDirPath = context.dirname(packageSource.fullName);
-                visitedPackages[packageName] = libDirPath;
-                var pubspecPath =
-                    context.join(context.dirname(libDirPath), 'pubspec.yaml');
-                pubspecFiles.add(resourceProvider.getFile(pubspecPath));
+                var packageRoot = resourceProvider
+                    .getFile(packageSource.fullName)
+                    .parent
+                    .parent;
+                packages.add(
+                  _Package(packageName, packageRoot),
+                );
+                pubspecFiles.add(
+                  packageRoot.getChildAssumingFile(file_paths.pubspecYaml),
+                );
               }
             }
           }
         }
 
-        var buffer = StringBuffer();
-        visitedPackages.forEach((String name, String path) {
-          buffer.write(name);
-          buffer.write(':');
-          buffer.writeln(Uri.file(path));
-        });
-        packagesFile.writeAsStringSync(buffer.toString());
+        packages.sort((a, b) => a.name.compareTo(b.name));
+
+        var packageConfigBuilder = PackageConfigFileBuilder();
+        for (var package in packages) {
+          packageConfigBuilder.add(
+            name: package.name,
+            rootPath: package.root.path,
+          );
+        }
+        packagesFile.writeAsStringSync(
+          packageConfigBuilder.toContent(
+            toUriStr: (path) {
+              return resourceProvider.pathContext.toUri(path).toString();
+            },
+          ),
+        );
       } catch (exception) {
         // If we are not able to produce a .packages file, return null so that
         // callers will not try to load the plugin.
@@ -738,7 +774,7 @@
 
   /// Return the names of packages that are listed as dependencies in the given
   /// [pubspecFile].
-  Iterable<String> _readDependecies(File pubspecFile) {
+  Iterable<String> _readDependencies(File pubspecFile) {
     var document = loadYamlDocument(pubspecFile.readAsStringSync(),
         sourceUrl: pubspecFile.toUri());
     var contents = document.contents;
@@ -983,6 +1019,13 @@
   }
 }
 
+class _Package {
+  final String name;
+  final Folder root;
+
+  _Package(this.name, this.root);
+}
+
 /// Information about a request that has been sent but for which a response has
 /// not yet been received.
 class _PendingRequest {
diff --git a/pkg/analysis_server/lib/src/protocol_server.dart b/pkg/analysis_server/lib/src/protocol_server.dart
index 9b829c7..7f8d2e5 100644
--- a/pkg/analysis_server/lib/src/protocol_server.dart
+++ b/pkg/analysis_server/lib/src/protocol_server.dart
@@ -14,7 +14,6 @@
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/diagnostic/diagnostic.dart' as engine;
 import 'package:analyzer/error/error.dart' as engine;
-import 'package:analyzer/exception/exception.dart';
 import 'package:analyzer/source/error_processor.dart';
 import 'package:analyzer/src/generated/source.dart' as engine;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
@@ -299,25 +298,16 @@
 /// Creates a new [Location].
 Location _locationForArgs(
     engine.CompilationUnitElement unitElement, engine.SourceRange range) {
-  var startLine = 0;
-  var startColumn = 0;
-  var endLine = 0;
-  var endColumn = 0;
-  try {
-    var lineInfo = unitElement.lineInfo;
-    if (lineInfo != null) {
-      var startLocation = lineInfo.getLocation(range.offset);
-      startLine = startLocation.lineNumber;
-      startColumn = startLocation.columnNumber;
+  var lineInfo = unitElement.lineInfo;
 
-      var endLocation = lineInfo.getLocation(range.end);
-      endLine = endLocation.lineNumber;
-      endColumn = endLocation.columnNumber;
-    }
-  } on AnalysisException {
-    // TODO(brianwilkerson) It doesn't look like the code in the try block
-    //  should be able to throw an exception. Try removing the try statement.
-  }
+  var startLocation = lineInfo.getLocation(range.offset);
+  var endLocation = lineInfo.getLocation(range.end);
+
+  var startLine = startLocation.lineNumber;
+  var startColumn = startLocation.columnNumber;
+  var endLine = endLocation.lineNumber;
+  var endColumn = endLocation.columnNumber;
+
   return Location(unitElement.source.fullName, range.offset, range.length,
       startLine, startColumn,
       endLine: endLine, endColumn: endColumn);
diff --git a/pkg/analysis_server/lib/src/services/completion/completion_performance.dart b/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
index 7815def..c41731b 100644
--- a/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
+++ b/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
@@ -38,7 +38,8 @@
   final OperationPerformance operation;
   final String path;
   final String snippet;
-  int suggestionCount = -1;
+  int computedSuggestionCount = -1;
+  int transmittedSuggestionCount = -1;
 
   CompletionPerformance({
     required this.operation,
@@ -47,12 +48,17 @@
     required int offset,
   }) : snippet = _computeCompletionSnippet(content, offset);
 
+  String get computedSuggestionCountStr {
+    if (computedSuggestionCount < 1) return '';
+    return '$computedSuggestionCount';
+  }
+
   int get elapsedInMilliseconds {
     return operation.elapsed.inMilliseconds;
   }
 
-  String get suggestionCountStr {
-    if (suggestionCount < 1) return '';
-    return '$suggestionCount';
+  String get transmittedSuggestionCountStr {
+    if (transmittedSuggestionCount < 1) return '';
+    return '$transmittedSuggestionCount';
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart
index 2fee0c2..9a11b19 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart
@@ -2,7 +2,6 @@
 // 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:_fe_analyzer_shared/src/scanner/token.dart';
 import 'package:analysis_server/src/provisional/completion/completion_core.dart';
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
@@ -200,16 +199,15 @@
   bool _isEditingNamedArgLabel() {
     if (argumentList != null) {
       var entity = request.target.entity;
-      if (entity is SimpleIdentifier &&
-          entity.isSynthetic &&
-          entity.token.next?.type == TokenType.COLON) {
-        return true;
-      }
       if (entity is NamedExpression) {
         var offset = request.offset;
-        if (entity.offset < offset && offset < entity.end) {
-          return true;
+        var nameId = entity.name.label;
+        // `^id: value` - add a new named argument.
+        // `^: value` - edit the name of this named argument.
+        if (offset == nameId.offset && !nameId.isSynthetic) {
+          return false;
         }
+        return nameId.offset <= offset && offset <= nameId.end;
       }
     }
     return false;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart b/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
index 59a5f61..69e49fe 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/fuzzy_filter_sort.dart
@@ -6,6 +6,8 @@
 import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analysis_server/src/services/completion/filtering/fuzzy_matcher.dart';
 
+final _identifierPattern = RegExp(r'([_a-zA-Z][_a-zA-Z0-9]*)');
+
 /// Filters and scores [suggestions] according to how well they match the
 /// [pattern]. Sorts [suggestions] by the score, relevance, and name.
 List<CompletionSuggestionBuilder> fuzzyFilterSort({
@@ -15,16 +17,18 @@
   var matcher = FuzzyMatcher(pattern, matchStyle: MatchStyle.SYMBOL);
 
   double score(CompletionSuggestionBuilder suggestion) {
-    var suggestionTextToMatch = suggestion.completion;
+    var textToMatch = suggestion.completion;
 
-    if (suggestion.kind == CompletionSuggestionKind.NAMED_ARGUMENT) {
-      var index = suggestionTextToMatch.indexOf(':');
-      if (index != -1) {
-        suggestionTextToMatch = suggestionTextToMatch.substring(0, index);
+    if (suggestion.kind == CompletionSuggestionKind.KEYWORD ||
+        suggestion.kind == CompletionSuggestionKind.NAMED_ARGUMENT) {
+      var identifier = _identifierPattern.matchAsPrefix(textToMatch)?.group(1);
+      if (identifier == null) {
+        return -1;
       }
+      textToMatch = identifier;
     }
 
-    return matcher.score(suggestionTextToMatch);
+    return matcher.score(textToMatch);
   }
 
   var scored = suggestions
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
index 18f1072..46710be 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
@@ -38,6 +38,9 @@
         AnalyzerOptions.implicitDynamic: EmptyProducer(),
       }),
     }),
+    AnalyzerOptions.codeStyle: MapProducer({
+      AnalyzerOptions.format: BooleanProducer(),
+    }),
     // TODO(brianwilkerson) Create a producer to produce `package:` URIs.
     AnalyzerOptions.include: EmptyProducer(),
     // TODO(brianwilkerson) Create constants for 'linter' and 'rules'.
diff --git a/pkg/analysis_server/lib/src/services/correction/assist.dart b/pkg/analysis_server/lib/src/services/correction/assist.dart
index 0e0b1c9..2dbdf85 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist.dart
@@ -201,10 +201,10 @@
     30,
     'Convert to a spread',
   );
-  static const CONVERT_TO_SUPER_INITIALIZING_PARAMETER = AssistKind(
-    'dart.assist.convert.toSuperInitializingParameter',
+  static const CONVERT_TO_SUPER_PARAMETERS = AssistKind(
+    'dart.assist.convert.toSuperParameters',
     30,
-    'Convert to a super initializing parameter',
+    'Convert to using super parameters',
   );
   static const ENCAPSULATE_FIELD = AssistKind(
     'dart.assist.encapsulateField',
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
index 70835d5..c63b5e7 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -39,7 +39,7 @@
 import 'package:analysis_server/src/services/correction/dart/convert_to_package_import.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_to_relative_import.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_to_set_literal.dart';
-import 'package:analysis_server/src/services/correction/dart/convert_to_super_initializing_parameter.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_super_parameters.dart';
 import 'package:analysis_server/src/services/correction/dart/encapsulate_field.dart';
 import 'package:analysis_server/src/services/correction/dart/exchange_operands.dart';
 import 'package:analysis_server/src/services/correction/dart/flutter_convert_to_children.dart';
@@ -120,7 +120,7 @@
     ConvertToRelativeImport.newInstance,
     ConvertToSetLiteral.newInstance,
     ConvertToSingleQuotes.newInstance,
-    ConvertToSuperInitializingParameter.newInstance,
+    ConvertToSuperParameters.newInstance,
     EncapsulateField.newInstance,
     ExchangeOperands.newInstance,
     FlutterConvertToChildren.newInstance,
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 5f286c8..3104866 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -417,8 +417,13 @@
       var configFile = provider.getFile(configFileName);
       try {
         var content = configFile.readAsStringSync();
-        var parser = TransformOverrideSetParser(ErrorReporter(
-            AnalysisErrorListener.NULL_LISTENER, configFile.createSource()));
+        var parser = TransformOverrideSetParser(
+          ErrorReporter(
+            AnalysisErrorListener.NULL_LISTENER,
+            configFile.createSource(),
+            isNonNullableByDefault: false,
+          ),
+        );
         return parser.parse(content);
       } on FileSystemException {
         // Fall through to return null.
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_trailing_comma.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_trailing_comma.dart
index 0995f2f..1b4aed0 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_trailing_comma.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_trailing_comma.dart
@@ -24,10 +24,18 @@
   @override
   Future<void> compute(ChangeBuilder builder) async {
     final node = this.node;
-    if (node is! ArgumentList) return;
+    if (node is ArgumentList) {
+      await _insertComma(builder, node.arguments.last);
+    } else if (node is FormalParameterList) {
+      await _insertComma(builder, node.parameters.last);
+    } else if (node is Assertion) {
+      await _insertComma(builder, node.message ?? node.condition);
+    }
+  }
 
+  Future<void> _insertComma(ChangeBuilder builder, AstNode lastNode) async {
     await builder.addDartFileEdit(file, (builder) {
-      builder.addSimpleInsertion(node.arguments.last.end, ',');
+      builder.addSimpleInsertion(lastNode.end, ',');
     });
   }
 
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_class_to_enum.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_class_to_enum.dart
index 9530b09..61cf3f9 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_class_to_enum.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_class_to_enum.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/services/correction/assist.dart';
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/services/correction/util.dart';
 import 'package:analysis_server/src/utilities/extensions/range_factory.dart';
 import 'package:analyzer/dart/analysis/features.dart';
@@ -16,6 +17,7 @@
 import 'package:analyzer_plugin/utilities/assist/assist.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:analyzer_plugin/utilities/range_factory.dart';
 import 'package:collection/collection.dart';
 
@@ -24,6 +26,18 @@
   AssistKind get assistKind => DartAssistKind.CONVERT_CLASS_TO_ENUM;
 
   @override
+  bool get canBeAppliedInBulk => true;
+
+  @override
+  bool get canBeAppliedToFile => true;
+
+  @override
+  FixKind get fixKind => DartFixKind.CONVERT_CLASS_TO_ENUM;
+
+  @override
+  FixKind get multiFixKind => DartFixKind.CONVERT_CLASS_TO_ENUM_MULTI;
+
+  @override
   Future<void> compute(ChangeBuilder builder) async {
     if (!libraryElement.featureSet.isEnabled(Feature.enhanced_enums)) {
       // If the library doesn't support enhanced_enums then the class can't be
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_for_each_to_for_loop.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_for_each_to_for_loop.dart
new file mode 100644
index 0000000..2da9100
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_for_each_to_for_loop.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertForEachToForLoop extends CorrectionProducer {
+  @override
+  bool get canBeAppliedInBulk => true;
+
+  @override
+  bool get canBeAppliedToFile => true;
+
+  @override
+  FixKind get fixKind => DartFixKind.CONVERT_FOR_EACH_TO_FOR_LOOP;
+
+  @override
+  FixKind get multiFixKind => DartFixKind.CONVERT_FOR_EACH_TO_FOR_LOOP_MULTI;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+    var invocation = node.parent;
+    if (invocation is! MethodInvocation) {
+      return;
+    }
+    var statement = invocation.parent;
+    if (statement is! ExpressionStatement) {
+      return;
+    }
+    var argument = invocation.argumentList.arguments[0];
+    if (argument is! FunctionExpression) {
+      return;
+    }
+    var parameters = argument.parameters?.parameters;
+    if (parameters == null || parameters.length != 1) {
+      return;
+    }
+    var parameter = parameters[0];
+    if (parameter is! NormalFormalParameter) {
+      return;
+    }
+    var loopVariableName = parameter.identifier?.name;
+    if (loopVariableName == null) {
+      return;
+    }
+    var target = utils.getNodeText(invocation.target!);
+    var body = argument.body;
+    if (body is BlockFunctionBody) {
+      await builder.addDartFileEdit(file, (builder) {
+        builder.addReplacement(range.startStart(invocation, body), (builder) {
+          builder.write('for (var ');
+          builder.write(loopVariableName);
+          builder.write(' in ');
+          builder.write(target);
+          builder.write(') ');
+        });
+        builder.addDeletion(range.endEnd(body, statement));
+        body.visitChildren(_ReturnVisitor(builder));
+      });
+    } else if (body is ExpressionFunctionBody) {
+      await builder.addDartFileEdit(file, (builder) {
+        var expression = body.expression;
+        var prefix = utils.getPrefix(statement.offset);
+        builder.addReplacement(range.startStart(invocation, expression),
+            (builder) {
+          builder.write('for (var ');
+          builder.write(loopVariableName);
+          builder.write(' in ');
+          builder.write(target);
+          builder.writeln(') {');
+          builder.write(prefix);
+          builder.write('  ');
+        });
+        builder.addReplacement(range.endEnd(expression, statement), (builder) {
+          builder.writeln(';');
+          builder.write(prefix);
+          builder.write('}');
+        });
+        body.visitChildren(_ReturnVisitor(builder));
+      });
+    }
+  }
+
+  /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+  static ConvertForEachToForLoop newInstance() => ConvertForEachToForLoop();
+}
+
+class _ReturnVisitor extends RecursiveAstVisitor<void> {
+  final DartFileEditBuilder builder;
+
+  _ReturnVisitor(this.builder);
+
+  @override
+  void visitReturnStatement(ReturnStatement node) {
+    builder.addSimpleReplacement(range.node(node), 'continue;');
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_cascade.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_cascade.dart
new file mode 100644
index 0000000..818a11c
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_cascade.dart
@@ -0,0 +1,72 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertToCascade extends CorrectionProducer {
+  @override
+  FixKind get fixKind => DartFixKind.CONVERT_TO_CASCADE;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+    var node = this.node;
+    if (node is! ExpressionStatement) return;
+
+    var block = node.parent;
+    if (block is! Block) return;
+
+    var previous = _getPrevious(block, node);
+    if (previous is! ExpressionStatement) return;
+    var previousOperator = _getTargetAndOperator(previous.expression)?.operator;
+
+    var expression = node.expression;
+    var target = _getTargetAndOperator(expression)?.target;
+    if (target == null) return;
+
+    var targetReplacement = expression is CascadeExpression ? '' : '.';
+
+    await builder.addDartFileEdit(file, (builder) {
+      if (previousOperator != null) {
+        builder.addSimpleInsertion(previousOperator.offset, '.');
+      }
+      builder.addDeletion(range.token(previous.semicolon!));
+      builder.addSimpleReplacement(range.node(target), targetReplacement);
+    });
+  }
+
+  Statement? _getPrevious(Block block, Statement statement) {
+    var statements = block.statements;
+    var index = statements.indexOf(statement);
+    return index > 0 ? statements[index - 1] : null;
+  }
+
+  _TargetAndOperator? _getTargetAndOperator(Expression expression) {
+    if (expression is AssignmentExpression) {
+      var lhs = expression.leftHandSide;
+      if (lhs is PrefixedIdentifier) {
+        return _TargetAndOperator(lhs.prefix, lhs.period);
+      }
+    } else if (expression is MethodInvocation) {
+      return _TargetAndOperator(expression.target, expression.operator);
+    } else if (expression is CascadeExpression) {
+      return _TargetAndOperator(expression.target, null);
+    }
+    return null;
+  }
+
+  /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+  static ConvertToCascade newInstance() => ConvertToCascade();
+}
+
+class _TargetAndOperator {
+  AstNode? target;
+  Token? operator;
+  _TargetAndOperator(this.target, this.operator);
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart
index c66c747..31d1539 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart
@@ -34,6 +34,13 @@
     if (body is! BlockFunctionBody || body.isGenerator) {
       return;
     }
+    if (body.keyword?.precedingComments != null ||
+        body.block.leftBracket.precedingComments != null ||
+        body.block.rightBracket.precedingComments != null) {
+      // TODO(https://github.com/dart-lang/sdk/issues/29313): Include comments
+      // in fixed output.
+      return;
+    }
     var parent = body.parent;
     if (parent is ConstructorDeclaration && parent.factoryKeyword == null) {
       return;
@@ -48,8 +55,31 @@
     Expression? returnExpression;
     if (onlyStatement is ReturnStatement) {
       returnExpression = onlyStatement.expression;
+      if (onlyStatement.returnKeyword.precedingComments != null) {
+        // TODO(https://github.com/dart-lang/sdk/issues/29313): Include comments
+        // in fixed output.
+        return;
+      }
+      // TODO(https://github.com/dart-lang/sdk/issues/29313): If there are
+      // comments after `return` keyword, before the expression, either return
+      // without offering a fix, or include the comments in the fixed output.
+
+      if (onlyStatement.semicolon.precedingComments != null) {
+        // TODO(https://github.com/dart-lang/sdk/issues/29313): Include
+        // comments in fixed output.
+        return;
+      }
     } else if (onlyStatement is ExpressionStatement) {
       returnExpression = onlyStatement.expression;
+      // TODO(https://github.com/dart-lang/sdk/issues/29313): If there are
+      // comments before the expression, either return without offering a fix,
+      // or include the comments in the fixed output.
+
+      if (onlyStatement.semicolon?.precedingComments != null) {
+        // TODO(https://github.com/dart-lang/sdk/issues/29313): Include comments
+        // in fixed output.
+        return;
+      }
     }
     if (returnExpression == null) {
       return;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_for_loop.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_for_loop.dart
deleted file mode 100644
index 4abb1bf..0000000
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_for_loop.dart
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
-import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
-import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
-import 'package:analyzer_plugin/utilities