Version 2.2.0-dev.2.0

Merge commit 'c92d5ca288da15b54b04c0a40ffe5d94a8883d77' into dev
diff --git a/.gitignore b/.gitignore
index 3e23d6d..2e3efaa3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@
 .idea
 CMakeLists.txt
 .clang_complete
+cmake-build-debug
 
 # VSCode project files
 .vscode
@@ -49,6 +50,9 @@
 # GDB files
 .gdb_history
 
+# https://github.com/Dart-Code/Dart-Code/issues/1295
+analysis_options.yaml
+
 # Built by chromebot and downloaded from Google Storage
 client/tests/drt
 
diff --git a/.packages b/.packages
index 9d7e756..a686ef2 100644
--- a/.packages
+++ b/.packages
@@ -8,6 +8,7 @@
 #
 analysis_server:pkg/analysis_server/lib
 analysis_server_client:pkg/analysis_server_client/lib
+analysis_tool:pkg/analysis_tool/lib
 analyzer:pkg/analyzer/lib
 analyzer_cli:pkg/analyzer_cli/lib
 analyzer_fe_comparison:pkg/analyzer_fe_comparison/lib
diff --git a/BUILD.gn b/BUILD.gn
index ae4c230..6d899ee 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -47,6 +47,8 @@
     "runtime/bin:sample_extension",
     "runtime/bin:test_extension",
     "runtime/bin:entrypoints_verification_test_extension",
+    "runtime/bin:ffi_test_dynamic_library",
+    "runtime/bin:ffi_test_functions",
     "runtime/vm:kernel_platform_files($host_toolchain)",
     "utils/kernel-service:kernel-service",
   ]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc9f141..44b7ec9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,29 +1,103 @@
-## 2.1.2-dev.0.0
+## 2.2.0-dev.2.0
 
-* Merge commit 011a1239fc4c0ed140ddce4773cc791da1936cd4 into dev
+### Language
 
-## 2.1.1
+Sets now have a literal syntax like lists and maps do:
 
-* Identical to 2.1.1-dev.3.2
+```dart
+var set = {1, 2, 3};
+```
 
-## 2.1.1-dev.3.2
+Using curly braces makes empty sets ambiguous with maps:
 
-* Cherry-pick 9d25cc93e850d4717cdc9e1c4bd3623e09c16d47 to dev
+```dart
+var collection = {}; // Empty set or map?
+```
 
-## 2.1.1-dev.3.1
+To avoid breaking existing code, an ambiguous literal is treated as a map.
+To create an empty set, you can rely on either a surrounding context type
+or an explicit type argument:
 
-* Cherry-pick 46080dd886a622c5520895d49c97506ecedb1df8 to dev
-* Cherry-pick fc62cf037343248c5ace87629d8eb1063f9f2428 to dev
-* Cherry-pick 770ab5275ac34af62d7c39da8eac8c56fdc48edb to dev
-* Cherry-pick 957e194735bda4fcf06cdcc68fa80f3290b17d79 to dev
+```dart
+// Variable type forces this to be a set:
+Set<int> set = {};
 
-## 2.1.1-dev.3.0
+// A single type argument means this must be a set:
+var set2 = <int>{};
+```
 
-* Cherry-pick 3cb16d20e7810a2a378bb897d939f67c0b380d88 to dev
+Set literals are released on all platforms. The `set-literals` experiment flag
+has been disabled.
 
-## 2.1.1-dev.2.0
+### Tools
 
-### Core library changes
+#### Analyzer
+
+*   The `DEPRECATED_MEMBER_USE` hint was split into two hints:
+
+    *   `DEPRECATED_MEMBER_USE` reports on usage of `@deprecated` members
+        declared in a different package.
+    *   `DEPRECATED_MEMBER_USE_FROM_SAME_PACKAGE` reports on usage of
+        `@deprecated` members declared in the same package.
+
+#### Linter
+
+Upgraded the linter to `0.1.82` which adds the following improvements:
+
+*   Added `provide_deprecation_message`, and
+    `use_full_hex_values_for_flutter_colors`, `prefer_null_aware_operators`.
+*   Fixed `prefer_const_declarations` set literal false-positives.
+*   Updated `prefer_collection_literals` to support set literals.
+*   Updated `unnecessary_parenthesis` play nicer with cascades.
+*   Removed deprecated lints from the "all options" sample.
+*   Stopped registering "default lints".
+*   Fixed `hash_and_equals` to respect `hashCode` fields.
+
+### Other libraries
+
+#### `package:kernel`
+
+*   **Breaking change:** The `klass` getter on the `InstanceConstant` class in
+    the Kernel AST API has been renamed to `classNode` for consistency.
+
+*   **Breaking change:** Updated `Link` implementation to utilize true symbolic
+    links instead of junctions on Windows. Existing junctions will continue to
+    work with the new `Link` implementation, but all new links will create
+    symbolic links.
+
+    To create a symbolic link, Dart must be run with administrative privileges
+    or Developer Mode must be enabled, otherwise a `FileSystemException` will be
+    raised with errno set to `ERROR_PRIVILEGE_NOT_HELD` (Issue [33966]).
+
+[33966]: https://github.com/dart-lang/sdk/issues/33966
+
+## 2.1.1 - 2019-02-18
+
+This is a patch version release. Again, the team's focus was mostly on improving
+performance and stability after the large changes in Dart 2.0.0. In particular,
+dart2js now always uses the "fast startup" emitter and the old emitter has been
+removed.
+
+There are a couple of very minor **breaking changes:**
+
+*   In `dart:io`, adding to a closed `IOSink` now throws a `StateError`.
+
+*   On the Dart VM, a soundness hole when using `dart:mirrors` to reflectively
+    invoke a method in an incorrect way that violates its static types has
+    been fixed (Issue [35611][]).
+
+### Language
+
+This release has no language changes.
+
+### Core library
+
+#### `dart:core`
+
+*   Made `DateTime.parse()` also recognize `,` as a valid decimal separator
+    when parsing from a string (Issue [35576][]).
+
+[35576]: https://github.com/dart-lang/sdk/issues/35576
 
 #### `dart:html`
 
@@ -32,10 +106,10 @@
 *   Improved dart2js compilation of `element.attributes.remove(name)` to
     generate `element.removeAttribute(name)`, so that there is no performance
     reason to migrate to the above methods.
-*   Fixed a number of `dart:html` P1 bugs:
+*   Fixed a number of `dart:html` bugs:
 
     *   Fixed HTML API's with callback typedef to correctly convert Dart
-        function to JS function (Issue [35484]).
+        functions to JS functions (Issue [35484]).
     *   HttpStatus constants exposed in `dart:html` (Issue [34318]).
     *   Expose DomName `ondblclick` and `dblclickEvent` for Angular analyzer.
     *   Fixed `removeAll` on `classes`; `elements` parameter should be
@@ -54,110 +128,16 @@
 
 #### `dart:io`
 
+*   **Breaking Change:** Adding to a closed `IOSink` now throws a `StateError`.
 *   Added ability to get and set low level socket options.
-* **Breaking Change:** Adding to a closed `IOSink` now throws a `StateError`.
 
 [29554]: https://github.com/dart-lang/sdk/issues/29554
 
-### Other library changes
-
-#### `package:kernel`
-
-*   **Breaking change:** The `klass` getter on the `InstanceConstant` class in
-    the Kernel AST API has been renamed to `classNode` for consistency.
-
-*   **Breaking change:** Updated `Link` implementation to utilize true symbolic
-    links instead of junctions on Windows. Existing junctions will continue to
-    work with the new `Link` implementation, but all new links will create
-    symbolic links.
-    
-    To create a symbolic link, Dart must be run with
-    administrative privileges or Developer Mode must be enabled, otherwise a
-    `FileSystemException` will be raised with errno set to
-    `ERROR_PRIVILEGE_NOT_HELD` (Issue [33966]).
-
-[33966]: https://github.com/dart-lang/sdk/issues/33966
-
-### Dart VM
-
-### Tool Changes
-
-#### Analyzer
-
-*   The `DEPRECATED_MEMBER_USE` hint was split into two hints:
-
-    *   `DEPRECATED_MEMBER_USE` reports on usage of `@deprecated` members
-        declared in a different package.
-    *   `DEPRECATED_MEMBER_USE_FROM_SAME_PACKAGE` reports on usage of
-        `@deprecated` members declared in the same package.
-
-### Linter
-
-The linter was bumped to `0.1.79` which introduces the following linter improvements to the SDK:
-
-* `unnecessary_parenthesis` updated to play nicer with cascades
-* new lint: `use_full_hex_values_for_flutter_colors`
-* new lint: `prefer_null_aware_operators`
-* miscellaneous documentation fixes
-* removed deprecated lints from the "all options" sample
-* stopped registering "default lints"
-* `hash_and_equals` fixed to respect `hashCode` fields
-
-
-#### Other Tools
-
-## 2.1.1-dev.3.2
-
-* Cherry-pick 9d25cc93e850d4717cdc9e1c4bd3623e09c16d47 to dev
-
-## 2.1.1-dev.3.1
-
-* Cherry-pick 46080dd886a622c5520895d49c97506ecedb1df8 to dev
-* Cherry-pick fc62cf037343248c5ace87629d8eb1063f9f2428 to dev
-* Cherry-pick 770ab5275ac34af62d7c39da8eac8c56fdc48edb to dev
-* Cherry-pick 957e194735bda4fcf06cdcc68fa80f3290b17d79 to dev
-
-## 2.1.1-dev.3.0
-
-* Cherry-pick 3cb16d20e7810a2a378bb897d939f67c0b380d88 to dev
-
-## 2.1.1-dev.2.0
-
-### Core library changes
-
-#### `dart:core`
-
-*   Made `DateTime.parse()` also recognize `,` as a valid decimal separator
-    when parsing from a string. (Issue [35576][])
-
-[35576]: https://github.com/dart-lang/sdk/issues/35576
-
-### Tool Changes
-
-#### Analyzer
-
-*   New hints added:
-
-    *   `NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR` and
-        `NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR_USING_NEW` when a `@literal`
-        const constructor is called in a non-const context (or with `new`).
-
-#### dart2js
-
-* `--fast-startup` is forced on.  The flag is silently ignored and will be
-  deprecated and then removed at a later date.
-
-  The alternative 'full emitter' is no longer available. The generated code for
-  `--fast-startup` is optimized to load faster, even though it can be slightly
-  larger.
-
-## 2.1.1-dev.1.0
-
 ### Dart VM
 
 In previous releases it was possible to violate static types using
-`dart:mirrors` but this bug is fixed now. Meaning that the code below would run
-without any TypeErrors and print "impossible" output.
+`dart:mirrors`. This code would run without any TypeErrors and print
+"impossible" output:
 
 ```dart
 import 'dart:mirrors';
@@ -176,11 +156,89 @@
 }
 ```
 
-Only code that already violates static typing will break.
+This bug is fixed now. Only code that already violates static typing will break.
+See Issue [35611][] for more details.
 
-See Issue [#35611](https://github.com/dart-lang/sdk/issues/35611) for more details.
+[35611]: https://github.com/dart-lang/sdk/issues/35611
 
-### Tool Changes
+### Dart for the Web
+
+#### dart2js
+
+*   The old "full emitter" back-end is removed and dart2js always uses the "fast
+    startup" back-end. The generated fast startup code is optimized to load
+    faster, even though it can be slightly larger. The `--fast-startup` and
+    `--no-fast-startup` are allowed but ignored. They will be removed in a
+    future version.
+
+*   We fixed a bug in how deferred constructor calls were incorrectly not marked
+    as deferred. The old behavior didn't cause breakages, but was imprecise and
+    pushed more code to the main output unit.
+
+*   A new deferred split algorithm implementation was added.
+
+    This implementation fixes a soundness bug and addresses performance issues
+    of the previous implementation, because of that it can have a visible impact
+    on apps. In particular:
+
+    *   We fixed a performance issue which was introduced when we migrated to
+        the common front-end. On large apps, the fix can cut 2/3 of the time
+        spent on this task.
+
+    *   We fixed a bug in how inferred types were categorized (Issue [35311][]).
+        The old behavior was unsound and could produce broken programs. The fix
+        may cause more code to be pulled into the main output unit.
+
+        This shows up frequently when returning deferred values from closures
+        since the closure's inferred return type is the deferred type. For
+        example, if you have:
+
+        ```dart
+        () async {
+          await deferred_prefix.loadLibrary();
+          return new deferred_prefix.Foo();
+        }
+        ```
+
+        The closure's return type is `Future<Foo>`. The old implementation
+        defers `Foo`, and incorrectly makes the return type `Future<dynamic>`.
+        This may break in places where the correct type is expected.
+
+        The new implementation will not defer `Foo`, and will place it in the
+        main output unit. If your intent is to defer it, then you need to ensure
+        the return type is not inferred to be `Foo`. For example, you can do so
+        by changing the code to a named closure with a declared type, or by
+        ensuring that the return expression has the type you want, like:
+
+        ```dart
+        () async {
+          await deferred_prefix.loadLibrary();
+          return new deferred_prefix.Foo() as dynamic;
+        }
+        ```
+
+        Because the new implementation might require you to inspect and fix your
+        app, we exposed two temporary flags:
+
+    *   The `--report-invalid-deferred-types` causes dart2js to run both the
+        old and new algorithms and report any cases where an invalid type was
+        detected.
+
+    *   The `--new-deferred-split` flag enables this new algorithm.
+
+*   The `--categories=*` flag is being replaced. `--categories=all` was only
+    used for testing and it is no longer supported. `--categories=Server`
+    continues to work at this time but it is deprecated, please use
+    `--server-mode` instead.
+
+*   The `--library-root` flag was replaced by `--libraries-spec`. This flag is
+    rarely used by developers invoking dart2js directly. It's important for
+    integrating dart2js with build systems. See `--help` for more details on the
+    new flag.
+
+[35311]: https://github.com/dart-lang/sdk/issues/35311
+
+### Tools
 
 #### Analyzer
 
@@ -191,135 +249,39 @@
 
 *   New hints added:
 
-    *   `INVALID_LITERAL_ANNOTATION` when something other than a const
+    *   `NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR` and
+        `NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR_USING_NEW` inform you when a
+        `@literal` const constructor is called in a non-const context (or with
+        `new`).
+    *   `INVALID_LITERAL_ANNOTATION` reports when something other than a const
         constructor is annotated with `@literal`.
-    *   `SUBTYPE_OF_SEALED_CLASS` when any class or mixin subclasses (extends,
-        implements, mixes in, or constrains to) a `@sealed` class, and the two
-        are declared in different packages.
-    *   `MIXIN_ON_SEALED_CLASS` when a `@sealed` class is used as a superclass
-        constraint of a mixin.
-
-#### dart2js
-
-* We fixed a bug in how deferred constructor calls were incorrectly not
-  marked as deferred. The old behavior didn't cause breakages, but was imprecise
-  and pushed more code to the main output unit.
-
-* A new deferred split algorithm implementation was added.
-
-  This implementation fixes a soundness bug and addresses performance issues of
-  the previous implementation, because of that it can have a visible impact
-  on apps. In particular,
-
-    * We fixed a performance issue which was introduced when we migrated to the
-      Common front-end. On large apps, the fix can cut down 2/3 of the time
-      spent on this task.
-
-    * We fixed a bug in how inferred types were miscategorized (Issue [35311]). The old
-      behavior was unsound and could produce broken programs. The fix may cause
-      more code to be pulled into the main output unit.
-
-      This shows up frequently when returning deferred values from closures
-      since the closure's inferred return type is the deferred type.
-      For example, if you have:
-
-      ```dart
-      () async {
-        await deferred_prefix.loadLibrary();
-        return new deferred_prefix.Foo();
-      }
-      ```
-
-      The closure's return type is `Future<Foo>`. The old implementation defers
-      `Foo`, and incorrectly makes the return type `Future<dynamic>`. This may
-      break in places where the correct type is expected.
-
-      The new implementation will not defer `Foo`, and will place it in the main
-      output unit. If your intent is to defer it, then you need to ensure the
-      return type is not inferred to be `Foo`. For example, you can do so by
-      changing the code to a named closure with a declared type, or by ensuring
-      that the return expression has the type you want, like:
-
-      ```dart
-      () async {
-        await deferred_prefix.loadLibrary();
-        return new deferred_prefix.Foo() as dynamic;
-      }
-      ```
-
-  Because the new implementation might require you to inspect and fix
-  your app, we exposed two temporary flags:
-
-    * `--report-invalid-deferred-types`: when provided, we will run both the
-      old and new algorithm and report where the issue was detected.
-
-    * `--new-deferred-split`: enables the new algorithm.
-
-[35311]: https://github.com/dart-lang/sdk/issues/35311
+    *   `SUBTYPE_OF_SEALED_CLASS` reports when any class or mixin subclasses
+        (extends, implements, mixes in, or constrains to) a `@sealed` class, and
+        the two are declared in different packages.
+    *   `MIXIN_ON_SEALED_CLASS` reports when a `@sealed` class is used as a
+        superclass constraint of a mixin.
 
 #### dartdoc
 
-* dartdoc default styles now work much better on mobile.  Simple browsing
-  and searching of API docs now work in many cases.
+Default styles now work much better on mobile. Simple browsing and searching of
+API docs now work in many cases.
 
-#### Linter
+Upgraded the linter to `0.1.78` which adds the following improvements:
 
-The linter was bumped to `0.1.78` which introduces the following linter fixes to the SDK:
-
-* fixed `type_annotate_public_apis` false positives on local functions
-* fixed `avoid_shadowing_type_parameters` to report shadowed type parameters in generic typedefs
-* fixed `use_setters_to_change_properties` to not wrongly lint overriding methods
-* fixed `cascade_invocations` to not lint awaited targets
-* fixed `prefer_conditional_assignment` false positives
-* fixed `join_return_with_assignment` false positives
-* fixed `cascade_invocations` false positives
-* miscellaneous documentation improvements
-* updated `invariant_booleans` status to experimental
-
-and adds:
-
-* a new `prefer_final_in_for_each` lint rule to flag loop variables that could be declared final
-
-## 2.1.1-dev.0.1
-
-* Cherry-pick 4914fe57ea9e034b948ef3ab5a4e7e511991f845 to dev
-* Cherry-pick 5a8ec419829337b60d705cabe0b3b1ab5d0d0883 to dev
-
-## 2.1.1-dev.0.0
-
-* Cherry-pick f8a680e5116493f8795c148a52dbecf8a84e4536 to dev
-* Cherry-pick b1c963c84b20e715bc5c1f7d443168071c2b971d to dev
-
-## 2.2.0-dev.1.1
-
-### Tool Changes
-
-#### Linter
-
-The linter was bumped to `0.1.73` which introduces the following new lints to the SDK:
-
-* `unnecessary_await_in_return`
-* `use_function_type_syntax_for_parameters`
-* `avoid_returning_null_for_future`
-* `avoid_shadowing_type_parameters`
-
-In addition, `prefer_bool_in_asserts` has been deprecated as its semantics are
-redundant with Dart 2 checks.
-
-## 2.2.0-dev.0.0
-
-### Dart for the Web
-
-#### dart2js
-
-* The `--categories=*` flag is being replaced. `--categories=all` was only used
-  for testing and it is no longer supported. `--categories=Server` continues to
-  work at this time but it is deprecated, please use `--server-mode` instead.
-
-* The `--library-root` flag was replaced by `--libraries-spec`. This flag is
-  rarely used by developers invoking dart2js directly. It's important for
-  integrating dart2js with build systems. See `--help` for more details on the
-  new flag.
+*   Added `prefer_final_in_for_each`, `unnecessary_await_in_return`,
+    `use_function_type_syntax_for_parameters`,
+    `avoid_returning_null_for_future`, and `avoid_shadowing_type_parameters`.
+*   Updated `invariant_booleans` status to experimental.
+*   Fixed `type_annotate_public_apis` false positives on local functions.
+*   Fixed `avoid_shadowing_type_parameters` to report shadowed type parameters
+    in generic typedefs.
+*   Fixed `use_setters_to_change_properties` to not wrongly lint overriding
+    methods.
+*   Fixed `cascade_invocations` to not lint awaited targets.
+*   Fixed `prefer_conditional_assignment` false positives.
+*   Fixed `join_return_with_assignment` false positives.
+*   Fixed `cascade_invocations` false positives.
+*   Deprecated `prefer_bool_in_asserts` as it is redundant in Dart 2.
 
 ## 2.1.0 - 2018-11-15
 
diff --git a/DEPS b/DEPS
index 6fe9b46..e8098e9 100644
--- a/DEPS
+++ b/DEPS
@@ -81,7 +81,7 @@
   # For more details, see https://github.com/dart-lang/sdk/issues/30164
   "dart_style_tag": "1.2.2",  # Please see the note above before updating.
 
-  "dartdoc_tag" : "v0.28.0",
+  "dartdoc_tag" : "v0.28.1+2",
   "fixnum_tag": "0.10.9",
   "glob_tag": "1.1.7",
   "html_tag" : "0.13.3+2",
@@ -95,7 +95,7 @@
   "intl_tag": "0.15.7",
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
   "json_rpc_2_tag": "2.0.9",
-  "linter_tag": "0.1.79",
+  "linter_tag": "0.1.82",
   "logging_tag": "0.11.3+2",
   "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
   "markdown_tag": "2.0.2",
diff --git a/dartdoc_options.yaml b/dartdoc_options.yaml
deleted file mode 100644
index 6c233f4..0000000
--- a/dartdoc_options.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-dartdoc:
-  categoryOrder: ["Core", VM", "Web"]
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index bf31832..f7ed499 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.22.1
+  1.23.0
 </h1>
 <p>
   This document contains a specification of the API provided by the
@@ -1447,6 +1447,10 @@
   </p>
   
   
+  
+  
+  
+  
 <h3>Requests</h3><dl><dt class="request"><a name="request_completion.getSuggestions">completion.getSuggestions</a></dt><dd><div class="box"><pre>request: {
   "id": String
   "method": "completion.getSuggestions"
@@ -1493,6 +1497,9 @@
     "<b>replacementLength</b>": int
     "<b>results</b>": List&lt;<a href="#type_CompletionSuggestion">CompletionSuggestion</a>&gt;
     "<b>isLast</b>": bool
+    "<b>includedSuggestionSets</b>": <span style="color:#999999">optional</span> List&lt;<a href="#type_IncludedSuggestionSet">IncludedSuggestionSet</a>&gt;
+    "<b>includedSuggestionKinds</b>": <span style="color:#999999">optional</span> List&lt;<a href="#type_ElementKind">ElementKind</a>&gt;
+    "<b>includedSuggestionRelevanceTags</b>": <span style="color:#999999">optional</span> List&lt;<a href="#type_IncludedSuggestionRelevanceTag">IncludedSuggestionRelevanceTag</a>&gt;
   }
 }</pre></div>
     <p>
@@ -1541,6 +1548,44 @@
           True if this is that last set of results that will be
           returned for the indicated completion.
         </p>
+      </dd><dt class="field"><b>includedSuggestionSets: List&lt;<a href="#type_IncludedSuggestionSet">IncludedSuggestionSet</a>&gt;<span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          This field is experimental.
+        </p>
+        <p>
+          References to <tt>AvailableSuggestionSet</tt> objects previously sent
+          to the client. The client can include applicable names from the
+          referenced library in code completion suggestions.
+        </p>
+      </dd><dt class="field"><b>includedSuggestionKinds: List&lt;<a href="#type_ElementKind">ElementKind</a>&gt;<span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          This field is experimental.
+        </p>
+        <p>
+          The client is expected to check this list against the
+          <tt>ElementKind</tt> sent in <tt>IncludedSuggestionSet</tt> to decide
+          whether or not these symbols should should be presented to the user.
+        </p>
+      </dd><dt class="field"><b>includedSuggestionRelevanceTags: List&lt;<a href="#type_IncludedSuggestionRelevanceTag">IncludedSuggestionRelevanceTag</a>&gt;<span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          This field is experimental.
+        </p>
+        <p>
+          The client is expected to check this list against the values of the
+          field <tt>relevanceTags</tt> of <tt>AvailableSuggestion</tt> to
+          decide if the suggestion should be given a different relevance than
+          the <tt>IncludedSuggestionSet</tt> that contains it. This might be
+          used for example to give higher relevance to suggestions of matching
+          types.
+        </p>
+        <p>
+          If an <tt>AvailableSuggestion</tt> has relevance tags that match more
+          than one <tt>IncludedSuggestionRelevanceTag</tt>, the maximum
+          relevance boost is used.
+        </p>
       </dd></dl></dd></dl>
 <h2 class="domain"><a name="domain_search">search domain</a></h2>
   <p>
@@ -1835,6 +1880,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"
@@ -2644,6 +2690,14 @@
   
   
   
+  
+  
+  
+  
+  
+  
+  
+  
 <dl><dt class="typeDefinition"><a name="type_AddContentOverlay">AddContentOverlay: object</a></dt><dd>
     <p>
       A directive to begin overlaying the contents of a file. The supplied
@@ -2827,7 +2881,12 @@
           The name of the current target of analysis. This field is
           omitted if analyzing is false.
         </p>
-      </dd></dl></dd><dt class="typeDefinition"><a name="type_ChangeContentOverlay">ChangeContentOverlay: object</a></dt><dd>
+      </dd></dl></dd><dt class="typeDefinition"><a name="type_AvailableSuggestionRelevanceTag">AvailableSuggestionRelevanceTag: String</a></dt><dd>
+    
+    <p>
+      The opaque tag value.
+    </p>
+  </dd><dt class="typeDefinition"><a name="type_ChangeContentOverlay">ChangeContentOverlay: object</a></dt><dd>
     <p>
       A directive to modify an existing file content overlay. One or more ranges
       of text are deleted from the old file content overlay and replaced with
@@ -4348,6 +4407,12 @@
           An "edit.sortMembers" request specified a Dart file that has
           scan or parse errors.
         </p>
+      </dd><dt class="value">UNKNOWN_FIX</dt><dd>
+        
+        <p>
+          A dartfix request was received containing the name of a fix
+          which does not match the name of any known fixes.
+        </p>
       </dd><dt class="value">UNKNOWN_REQUEST</dt><dd>
         
         <p>
@@ -5045,7 +5110,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></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.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></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_completion.results">results</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.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><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_ChangeContentOverlay">ChangeContentOverlay</a></li><li><a href="#type_ClosingLabel">ClosingLabel</a></li><li><a href="#type_CompletionId">CompletionId</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_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_FileKind">FileKind</a></li><li><a href="#type_FilePath">FilePath</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_ImportedElements">ImportedElements</a></li><li><a href="#type_KytheEntry">KytheEntry</a></li><li><a href="#type_KytheVName">KytheVName</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></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.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></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></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.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><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_AvailableSuggestionRelevanceTag">AvailableSuggestionRelevanceTag</a></li><li><a href="#type_ChangeContentOverlay">ChangeContentOverlay</a></li><li><a href="#type_ClosingLabel">ClosingLabel</a></li><li><a href="#type_CompletionId">CompletionId</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_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_FileKind">FileKind</a></li><li><a href="#type_FilePath">FilePath</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_ImportedElements">ImportedElements</a></li><li><a href="#type_KytheEntry">KytheEntry</a></li><li><a href="#type_KytheVName">KytheVName</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/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart
index fd21adb..d599adc 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.22.1';
+const String PROTOCOL_VERSION = '1.23.0';
 
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
@@ -110,8 +110,21 @@
 const String ANALYTICS_REQUEST_SEND_TIMING_EVENT = 'event';
 const String ANALYTICS_REQUEST_SEND_TIMING_MILLIS = 'millis';
 const String ANALYTICS_RESPONSE_IS_ENABLED_ENABLED = 'enabled';
+const String COMPLETION_NOTIFICATION_AVAILABLE_SUGGESTIONS =
+    'completion.availableSuggestions';
+const String COMPLETION_NOTIFICATION_AVAILABLE_SUGGESTIONS_CHANGED_LIBRARIES =
+    'changedLibraries';
+const String COMPLETION_NOTIFICATION_AVAILABLE_SUGGESTIONS_REMOVED_LIBRARIES =
+    'removedLibraries';
 const String COMPLETION_NOTIFICATION_RESULTS = 'completion.results';
 const String COMPLETION_NOTIFICATION_RESULTS_ID = 'id';
+const String COMPLETION_NOTIFICATION_RESULTS_INCLUDED_SUGGESTION_KINDS =
+    'includedSuggestionKinds';
+const String
+    COMPLETION_NOTIFICATION_RESULTS_INCLUDED_SUGGESTION_RELEVANCE_TAGS =
+    'includedSuggestionRelevanceTags';
+const String COMPLETION_NOTIFICATION_RESULTS_INCLUDED_SUGGESTION_SETS =
+    'includedSuggestionSets';
 const String COMPLETION_NOTIFICATION_RESULTS_IS_LAST = 'isLast';
 const String COMPLETION_NOTIFICATION_RESULTS_REPLACEMENT_LENGTH =
     'replacementLength';
@@ -121,13 +134,33 @@
 const String COMPLETION_REQUEST_GET_SUGGESTIONS = 'completion.getSuggestions';
 const String COMPLETION_REQUEST_GET_SUGGESTIONS_FILE = 'file';
 const String COMPLETION_REQUEST_GET_SUGGESTIONS_OFFSET = 'offset';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS =
+    'completion.getSuggestionDetails';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS_FILE = 'file';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS_ID = 'id';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS_LABEL = 'label';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS_OFFSET = 'offset';
+const String COMPLETION_REQUEST_REGISTER_LIBRARY_PATHS =
+    'completion.registerLibraryPaths';
+const String COMPLETION_REQUEST_REGISTER_LIBRARY_PATHS_PATHS = 'paths';
+const String COMPLETION_REQUEST_SET_SUBSCRIPTIONS =
+    'completion.setSubscriptions';
+const String COMPLETION_REQUEST_SET_SUBSCRIPTIONS_SUBSCRIPTIONS =
+    'subscriptions';
 const String COMPLETION_RESPONSE_GET_SUGGESTIONS_ID = 'id';
+const String COMPLETION_RESPONSE_GET_SUGGESTION_DETAILS_CHANGE = 'change';
+const String COMPLETION_RESPONSE_GET_SUGGESTION_DETAILS_COMPLETION =
+    'completion';
 const String DIAGNOSTIC_REQUEST_GET_DIAGNOSTICS = 'diagnostic.getDiagnostics';
 const String DIAGNOSTIC_REQUEST_GET_SERVER_PORT = 'diagnostic.getServerPort';
 const String DIAGNOSTIC_RESPONSE_GET_DIAGNOSTICS_CONTEXTS = 'contexts';
 const String DIAGNOSTIC_RESPONSE_GET_SERVER_PORT_PORT = 'port';
 const String EDIT_REQUEST_DARTFIX = 'edit.dartfix';
+const String EDIT_REQUEST_DARTFIX_EXCLUDED_FIXES = 'excludedFixes';
 const String EDIT_REQUEST_DARTFIX_INCLUDED = 'included';
+const String EDIT_REQUEST_DARTFIX_INCLUDED_FIXES = 'includedFixes';
+const String EDIT_REQUEST_DARTFIX_INCLUDE_REQUIRED_FIXES =
+    'includeRequiredFixes';
 const String EDIT_REQUEST_FORMAT = 'edit.format';
 const String EDIT_REQUEST_FORMAT_FILE = 'file';
 const String EDIT_REQUEST_FORMAT_LINE_LENGTH = 'lineLength';
@@ -142,6 +175,7 @@
 const String EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS_FILE = 'file';
 const String EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS_LENGTH = 'length';
 const String EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS_OFFSET = 'offset';
+const String EDIT_REQUEST_GET_DARTFIX_INFO = 'edit.getDartfixInfo';
 const String EDIT_REQUEST_GET_FIXES = 'edit.getFixes';
 const String EDIT_REQUEST_GET_FIXES_FILE = 'file';
 const String EDIT_REQUEST_GET_FIXES_OFFSET = 'offset';
@@ -183,6 +217,7 @@
 const String EDIT_RESPONSE_FORMAT_SELECTION_OFFSET = 'selectionOffset';
 const String EDIT_RESPONSE_GET_ASSISTS_ASSISTS = 'assists';
 const String EDIT_RESPONSE_GET_AVAILABLE_REFACTORINGS_KINDS = 'kinds';
+const String EDIT_RESPONSE_GET_DARTFIX_INFO_FIXES = 'fixes';
 const String EDIT_RESPONSE_GET_FIXES_FIXES = 'fixes';
 const String EDIT_RESPONSE_GET_POSTFIX_COMPLETION_CHANGE = 'change';
 const String EDIT_RESPONSE_GET_REFACTORING_CHANGE = 'change';
diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart
index 5130033..9f29f1a 100644
--- a/pkg/analysis_server/lib/protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart
@@ -5071,6 +5071,415 @@
 }
 
 /**
+ * AvailableSuggestion
+ *
+ * {
+ *   "label": String
+ *   "element": Element
+ *   "docComplete": optional String
+ *   "docSummary": optional String
+ *   "parameterNames": optional List<String>
+ *   "parameterTypes": optional List<String>
+ *   "relevanceTags": optional List<AvailableSuggestionRelevanceTag>
+ *   "requiredParameterCount": optional int
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class AvailableSuggestion implements HasToJson {
+  String _label;
+
+  Element _element;
+
+  String _docComplete;
+
+  String _docSummary;
+
+  List<String> _parameterNames;
+
+  List<String> _parameterTypes;
+
+  List<String> _relevanceTags;
+
+  int _requiredParameterCount;
+
+  /**
+   * The identifier to present to the user for code completion.
+   */
+  String get label => _label;
+
+  /**
+   * The identifier to present to the user for code completion.
+   */
+  void set label(String value) {
+    assert(value != null);
+    this._label = value;
+  }
+
+  /**
+   * Information about the element reference being suggested.
+   */
+  Element get element => _element;
+
+  /**
+   * Information about the element reference being suggested.
+   */
+  void set element(Element value) {
+    assert(value != null);
+    this._element = value;
+  }
+
+  /**
+   * The Dartdoc associated with the element being suggested. This field is
+   * omitted if there is no Dartdoc associated with the element.
+   */
+  String get docComplete => _docComplete;
+
+  /**
+   * The Dartdoc associated with the element being suggested. This field is
+   * omitted if there is no Dartdoc associated with the element.
+   */
+  void set docComplete(String value) {
+    this._docComplete = value;
+  }
+
+  /**
+   * An abbreviated version of the Dartdoc associated with the element being
+   * suggested. This field is omitted if there is no Dartdoc associated with
+   * the element.
+   */
+  String get docSummary => _docSummary;
+
+  /**
+   * An abbreviated version of the Dartdoc associated with the element being
+   * suggested. This field is omitted if there is no Dartdoc associated with
+   * the element.
+   */
+  void set docSummary(String value) {
+    this._docSummary = value;
+  }
+
+  /**
+   * If the element is an executable, the names of the formal parameters of all
+   * kinds - required, optional positional, and optional named. The names of
+   * positional parameters are empty strings. Omitted if the element is not an
+   * executable.
+   */
+  List<String> get parameterNames => _parameterNames;
+
+  /**
+   * If the element is an executable, the names of the formal parameters of all
+   * kinds - required, optional positional, and optional named. The names of
+   * positional parameters are empty strings. Omitted if the element is not an
+   * executable.
+   */
+  void set parameterNames(List<String> value) {
+    this._parameterNames = value;
+  }
+
+  /**
+   * If the element is an executable, the declared types of the formal
+   * parameters of all kinds - required, optional positional, and optional
+   * named. Omitted if the element is not an executable.
+   */
+  List<String> get parameterTypes => _parameterTypes;
+
+  /**
+   * If the element is an executable, the declared types of the formal
+   * parameters of all kinds - required, optional positional, and optional
+   * named. Omitted if the element is not an executable.
+   */
+  void set parameterTypes(List<String> value) {
+    this._parameterTypes = value;
+  }
+
+  /**
+   * This field is set if the relevance of this suggestion might be changed
+   * depending on where completion is requested.
+   */
+  List<String> get relevanceTags => _relevanceTags;
+
+  /**
+   * This field is set if the relevance of this suggestion might be changed
+   * depending on where completion is requested.
+   */
+  void set relevanceTags(List<String> value) {
+    this._relevanceTags = value;
+  }
+
+  int get requiredParameterCount => _requiredParameterCount;
+
+  void set requiredParameterCount(int value) {
+    this._requiredParameterCount = value;
+  }
+
+  AvailableSuggestion(String label, Element element,
+      {String docComplete,
+      String docSummary,
+      List<String> parameterNames,
+      List<String> parameterTypes,
+      List<String> relevanceTags,
+      int requiredParameterCount}) {
+    this.label = label;
+    this.element = element;
+    this.docComplete = docComplete;
+    this.docSummary = docSummary;
+    this.parameterNames = parameterNames;
+    this.parameterTypes = parameterTypes;
+    this.relevanceTags = relevanceTags;
+    this.requiredParameterCount = requiredParameterCount;
+  }
+
+  factory AvailableSuggestion.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      String label;
+      if (json.containsKey("label")) {
+        label = jsonDecoder.decodeString(jsonPath + ".label", json["label"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "label");
+      }
+      Element element;
+      if (json.containsKey("element")) {
+        element = new Element.fromJson(
+            jsonDecoder, jsonPath + ".element", json["element"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "element");
+      }
+      String docComplete;
+      if (json.containsKey("docComplete")) {
+        docComplete = jsonDecoder.decodeString(
+            jsonPath + ".docComplete", json["docComplete"]);
+      }
+      String docSummary;
+      if (json.containsKey("docSummary")) {
+        docSummary = jsonDecoder.decodeString(
+            jsonPath + ".docSummary", json["docSummary"]);
+      }
+      List<String> parameterNames;
+      if (json.containsKey("parameterNames")) {
+        parameterNames = jsonDecoder.decodeList(jsonPath + ".parameterNames",
+            json["parameterNames"], jsonDecoder.decodeString);
+      }
+      List<String> parameterTypes;
+      if (json.containsKey("parameterTypes")) {
+        parameterTypes = jsonDecoder.decodeList(jsonPath + ".parameterTypes",
+            json["parameterTypes"], jsonDecoder.decodeString);
+      }
+      List<String> relevanceTags;
+      if (json.containsKey("relevanceTags")) {
+        relevanceTags = jsonDecoder.decodeList(jsonPath + ".relevanceTags",
+            json["relevanceTags"], jsonDecoder.decodeString);
+      }
+      int requiredParameterCount;
+      if (json.containsKey("requiredParameterCount")) {
+        requiredParameterCount = jsonDecoder.decodeInt(
+            jsonPath + ".requiredParameterCount",
+            json["requiredParameterCount"]);
+      }
+      return new AvailableSuggestion(label, element,
+          docComplete: docComplete,
+          docSummary: docSummary,
+          parameterNames: parameterNames,
+          parameterTypes: parameterTypes,
+          relevanceTags: relevanceTags,
+          requiredParameterCount: requiredParameterCount);
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, "AvailableSuggestion", json);
+    }
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["label"] = label;
+    result["element"] = element.toJson();
+    if (docComplete != null) {
+      result["docComplete"] = docComplete;
+    }
+    if (docSummary != null) {
+      result["docSummary"] = docSummary;
+    }
+    if (parameterNames != null) {
+      result["parameterNames"] = parameterNames;
+    }
+    if (parameterTypes != null) {
+      result["parameterTypes"] = parameterTypes;
+    }
+    if (relevanceTags != null) {
+      result["relevanceTags"] = relevanceTags;
+    }
+    if (requiredParameterCount != null) {
+      result["requiredParameterCount"] = requiredParameterCount;
+    }
+    return result;
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is AvailableSuggestion) {
+      return label == other.label &&
+          element == other.element &&
+          docComplete == other.docComplete &&
+          docSummary == other.docSummary &&
+          listEqual(parameterNames, other.parameterNames,
+              (String a, String b) => a == b) &&
+          listEqual(parameterTypes, other.parameterTypes,
+              (String a, String b) => a == b) &&
+          listEqual(relevanceTags, other.relevanceTags,
+              (String a, String b) => a == b) &&
+          requiredParameterCount == other.requiredParameterCount;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, label.hashCode);
+    hash = JenkinsSmiHash.combine(hash, element.hashCode);
+    hash = JenkinsSmiHash.combine(hash, docComplete.hashCode);
+    hash = JenkinsSmiHash.combine(hash, docSummary.hashCode);
+    hash = JenkinsSmiHash.combine(hash, parameterNames.hashCode);
+    hash = JenkinsSmiHash.combine(hash, parameterTypes.hashCode);
+    hash = JenkinsSmiHash.combine(hash, relevanceTags.hashCode);
+    hash = JenkinsSmiHash.combine(hash, requiredParameterCount.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
+ * AvailableSuggestionSet
+ *
+ * {
+ *   "id": int
+ *   "uri": String
+ *   "items": List<AvailableSuggestion>
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class AvailableSuggestionSet implements HasToJson {
+  int _id;
+
+  String _uri;
+
+  List<AvailableSuggestion> _items;
+
+  /**
+   * The id associated with the library.
+   */
+  int get id => _id;
+
+  /**
+   * The id associated with the library.
+   */
+  void set id(int value) {
+    assert(value != null);
+    this._id = value;
+  }
+
+  /**
+   * The URI of the library.
+   */
+  String get uri => _uri;
+
+  /**
+   * The URI of the library.
+   */
+  void set uri(String value) {
+    assert(value != null);
+    this._uri = value;
+  }
+
+  List<AvailableSuggestion> get items => _items;
+
+  void set items(List<AvailableSuggestion> value) {
+    assert(value != null);
+    this._items = value;
+  }
+
+  AvailableSuggestionSet(int id, String uri, List<AvailableSuggestion> items) {
+    this.id = id;
+    this.uri = uri;
+    this.items = items;
+  }
+
+  factory AvailableSuggestionSet.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      int id;
+      if (json.containsKey("id")) {
+        id = jsonDecoder.decodeInt(jsonPath + ".id", json["id"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "id");
+      }
+      String uri;
+      if (json.containsKey("uri")) {
+        uri = jsonDecoder.decodeString(jsonPath + ".uri", json["uri"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "uri");
+      }
+      List<AvailableSuggestion> items;
+      if (json.containsKey("items")) {
+        items = jsonDecoder.decodeList(
+            jsonPath + ".items",
+            json["items"],
+            (String jsonPath, Object json) =>
+                new AvailableSuggestion.fromJson(jsonDecoder, jsonPath, json));
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "items");
+      }
+      return new AvailableSuggestionSet(id, uri, items);
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, "AvailableSuggestionSet", json);
+    }
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["id"] = id;
+    result["uri"] = uri;
+    result["items"] =
+        items.map((AvailableSuggestion value) => value.toJson()).toList();
+    return result;
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is AvailableSuggestionSet) {
+      return id == other.id &&
+          uri == other.uri &&
+          listEqual(items, other.items,
+              (AvailableSuggestion a, AvailableSuggestion b) => a == b);
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, id.hashCode);
+    hash = JenkinsSmiHash.combine(hash, uri.hashCode);
+    hash = JenkinsSmiHash.combine(hash, items.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
  * ClosingLabel
  *
  * {
@@ -5196,6 +5605,417 @@
 }
 
 /**
+ * completion.availableSuggestions params
+ *
+ * {
+ *   "changedLibraries": optional List<AvailableSuggestionSet>
+ *   "removedLibraries": optional List<int>
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class CompletionAvailableSuggestionsParams implements HasToJson {
+  List<AvailableSuggestionSet> _changedLibraries;
+
+  List<int> _removedLibraries;
+
+  /**
+   * A list of pre-computed, potential completions coming from this set of
+   * completion suggestions.
+   */
+  List<AvailableSuggestionSet> get changedLibraries => _changedLibraries;
+
+  /**
+   * A list of pre-computed, potential completions coming from this set of
+   * completion suggestions.
+   */
+  void set changedLibraries(List<AvailableSuggestionSet> value) {
+    this._changedLibraries = value;
+  }
+
+  /**
+   * A list of library ids that no longer apply.
+   */
+  List<int> get removedLibraries => _removedLibraries;
+
+  /**
+   * A list of library ids that no longer apply.
+   */
+  void set removedLibraries(List<int> value) {
+    this._removedLibraries = value;
+  }
+
+  CompletionAvailableSuggestionsParams(
+      {List<AvailableSuggestionSet> changedLibraries,
+      List<int> removedLibraries}) {
+    this.changedLibraries = changedLibraries;
+    this.removedLibraries = removedLibraries;
+  }
+
+  factory CompletionAvailableSuggestionsParams.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      List<AvailableSuggestionSet> changedLibraries;
+      if (json.containsKey("changedLibraries")) {
+        changedLibraries = jsonDecoder.decodeList(
+            jsonPath + ".changedLibraries",
+            json["changedLibraries"],
+            (String jsonPath, Object json) =>
+                new AvailableSuggestionSet.fromJson(
+                    jsonDecoder, jsonPath, json));
+      }
+      List<int> removedLibraries;
+      if (json.containsKey("removedLibraries")) {
+        removedLibraries = jsonDecoder.decodeList(
+            jsonPath + ".removedLibraries",
+            json["removedLibraries"],
+            jsonDecoder.decodeInt);
+      }
+      return new CompletionAvailableSuggestionsParams(
+          changedLibraries: changedLibraries,
+          removedLibraries: removedLibraries);
+    } else {
+      throw jsonDecoder.mismatch(
+          jsonPath, "completion.availableSuggestions params", json);
+    }
+  }
+
+  factory CompletionAvailableSuggestionsParams.fromNotification(
+      Notification notification) {
+    return new CompletionAvailableSuggestionsParams.fromJson(
+        new ResponseDecoder(null), "params", notification.params);
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    if (changedLibraries != null) {
+      result["changedLibraries"] = changedLibraries
+          .map((AvailableSuggestionSet value) => value.toJson())
+          .toList();
+    }
+    if (removedLibraries != null) {
+      result["removedLibraries"] = removedLibraries;
+    }
+    return result;
+  }
+
+  Notification toNotification() {
+    return new Notification("completion.availableSuggestions", toJson());
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is CompletionAvailableSuggestionsParams) {
+      return listEqual(changedLibraries, other.changedLibraries,
+              (AvailableSuggestionSet a, AvailableSuggestionSet b) => a == b) &&
+          listEqual(removedLibraries, other.removedLibraries,
+              (int a, int b) => a == b);
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, changedLibraries.hashCode);
+    hash = JenkinsSmiHash.combine(hash, removedLibraries.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
+ * completion.getSuggestionDetails params
+ *
+ * {
+ *   "file": FilePath
+ *   "id": int
+ *   "label": String
+ *   "offset": int
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class CompletionGetSuggestionDetailsParams implements RequestParams {
+  String _file;
+
+  int _id;
+
+  String _label;
+
+  int _offset;
+
+  /**
+   * The path of the file into which this completion is being inserted.
+   */
+  String get file => _file;
+
+  /**
+   * The path of the file into which this completion is being inserted.
+   */
+  void set file(String value) {
+    assert(value != null);
+    this._file = value;
+  }
+
+  /**
+   * The identifier of the AvailableSuggestionSet containing the selected
+   * label.
+   */
+  int get id => _id;
+
+  /**
+   * The identifier of the AvailableSuggestionSet containing the selected
+   * label.
+   */
+  void set id(int value) {
+    assert(value != null);
+    this._id = value;
+  }
+
+  /**
+   * The label from the AvailableSuggestionSet with the `id` for which
+   * insertion information is requested.
+   */
+  String get label => _label;
+
+  /**
+   * The label from the AvailableSuggestionSet with the `id` for which
+   * insertion information is requested.
+   */
+  void set label(String value) {
+    assert(value != null);
+    this._label = value;
+  }
+
+  /**
+   * The offset in the file where the completion will be inserted.
+   */
+  int get offset => _offset;
+
+  /**
+   * The offset in the file where the completion will be inserted.
+   */
+  void set offset(int value) {
+    assert(value != null);
+    this._offset = value;
+  }
+
+  CompletionGetSuggestionDetailsParams(
+      String file, int id, String label, int offset) {
+    this.file = file;
+    this.id = id;
+    this.label = label;
+    this.offset = offset;
+  }
+
+  factory CompletionGetSuggestionDetailsParams.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      String file;
+      if (json.containsKey("file")) {
+        file = jsonDecoder.decodeString(jsonPath + ".file", json["file"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "file");
+      }
+      int id;
+      if (json.containsKey("id")) {
+        id = jsonDecoder.decodeInt(jsonPath + ".id", json["id"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "id");
+      }
+      String label;
+      if (json.containsKey("label")) {
+        label = jsonDecoder.decodeString(jsonPath + ".label", json["label"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "label");
+      }
+      int offset;
+      if (json.containsKey("offset")) {
+        offset = jsonDecoder.decodeInt(jsonPath + ".offset", json["offset"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "offset");
+      }
+      return new CompletionGetSuggestionDetailsParams(file, id, label, offset);
+    } else {
+      throw jsonDecoder.mismatch(
+          jsonPath, "completion.getSuggestionDetails params", json);
+    }
+  }
+
+  factory CompletionGetSuggestionDetailsParams.fromRequest(Request request) {
+    return new CompletionGetSuggestionDetailsParams.fromJson(
+        new RequestDecoder(request), "params", request.params);
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["file"] = file;
+    result["id"] = id;
+    result["label"] = label;
+    result["offset"] = offset;
+    return result;
+  }
+
+  @override
+  Request toRequest(String id) {
+    return new Request(id, "completion.getSuggestionDetails", toJson());
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is CompletionGetSuggestionDetailsParams) {
+      return file == other.file &&
+          id == other.id &&
+          label == other.label &&
+          offset == other.offset;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, file.hashCode);
+    hash = JenkinsSmiHash.combine(hash, id.hashCode);
+    hash = JenkinsSmiHash.combine(hash, label.hashCode);
+    hash = JenkinsSmiHash.combine(hash, offset.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
+ * completion.getSuggestionDetails result
+ *
+ * {
+ *   "completion": String
+ *   "change": optional SourceChange
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class CompletionGetSuggestionDetailsResult implements ResponseResult {
+  String _completion;
+
+  SourceChange _change;
+
+  /**
+   * The full text to insert, including any optional import prefix.
+   */
+  String get completion => _completion;
+
+  /**
+   * The full text to insert, including any optional import prefix.
+   */
+  void set completion(String value) {
+    assert(value != null);
+    this._completion = value;
+  }
+
+  /**
+   * A change for the client to apply in case the library containing the
+   * accepted completion suggestion needs to be imported. The field will be
+   * omitted if there are no additional changes that need to be made.
+   */
+  SourceChange get change => _change;
+
+  /**
+   * A change for the client to apply in case the library containing the
+   * accepted completion suggestion needs to be imported. The field will be
+   * omitted if there are no additional changes that need to be made.
+   */
+  void set change(SourceChange value) {
+    this._change = value;
+  }
+
+  CompletionGetSuggestionDetailsResult(String completion,
+      {SourceChange change}) {
+    this.completion = completion;
+    this.change = change;
+  }
+
+  factory CompletionGetSuggestionDetailsResult.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      String completion;
+      if (json.containsKey("completion")) {
+        completion = jsonDecoder.decodeString(
+            jsonPath + ".completion", json["completion"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "completion");
+      }
+      SourceChange change;
+      if (json.containsKey("change")) {
+        change = new SourceChange.fromJson(
+            jsonDecoder, jsonPath + ".change", json["change"]);
+      }
+      return new CompletionGetSuggestionDetailsResult(completion,
+          change: change);
+    } else {
+      throw jsonDecoder.mismatch(
+          jsonPath, "completion.getSuggestionDetails result", json);
+    }
+  }
+
+  factory CompletionGetSuggestionDetailsResult.fromResponse(Response response) {
+    return new CompletionGetSuggestionDetailsResult.fromJson(
+        new ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)),
+        "result",
+        response.result);
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["completion"] = completion;
+    if (change != null) {
+      result["change"] = change.toJson();
+    }
+    return result;
+  }
+
+  @override
+  Response toResponse(String id) {
+    return new Response(id, result: toJson());
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is CompletionGetSuggestionDetailsResult) {
+      return completion == other.completion && change == other.change;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, completion.hashCode);
+    hash = JenkinsSmiHash.combine(hash, change.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
  * completion.getSuggestions params
  *
  * {
@@ -5391,6 +6211,130 @@
 }
 
 /**
+ * completion.registerLibraryPaths params
+ *
+ * {
+ *   "paths": List<LibraryPathSet>
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class CompletionRegisterLibraryPathsParams implements RequestParams {
+  List<LibraryPathSet> _paths;
+
+  /**
+   * A list of objects each containing a path and the additional libraries from
+   * which the client is interested in receiving completion suggestions. If one
+   * configured path is beneath another, the descendent will override the
+   * ancestors' configured libraries of interest.
+   */
+  List<LibraryPathSet> get paths => _paths;
+
+  /**
+   * A list of objects each containing a path and the additional libraries from
+   * which the client is interested in receiving completion suggestions. If one
+   * configured path is beneath another, the descendent will override the
+   * ancestors' configured libraries of interest.
+   */
+  void set paths(List<LibraryPathSet> value) {
+    assert(value != null);
+    this._paths = value;
+  }
+
+  CompletionRegisterLibraryPathsParams(List<LibraryPathSet> paths) {
+    this.paths = paths;
+  }
+
+  factory CompletionRegisterLibraryPathsParams.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      List<LibraryPathSet> paths;
+      if (json.containsKey("paths")) {
+        paths = jsonDecoder.decodeList(
+            jsonPath + ".paths",
+            json["paths"],
+            (String jsonPath, Object json) =>
+                new LibraryPathSet.fromJson(jsonDecoder, jsonPath, json));
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "paths");
+      }
+      return new CompletionRegisterLibraryPathsParams(paths);
+    } else {
+      throw jsonDecoder.mismatch(
+          jsonPath, "completion.registerLibraryPaths params", json);
+    }
+  }
+
+  factory CompletionRegisterLibraryPathsParams.fromRequest(Request request) {
+    return new CompletionRegisterLibraryPathsParams.fromJson(
+        new RequestDecoder(request), "params", request.params);
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["paths"] =
+        paths.map((LibraryPathSet value) => value.toJson()).toList();
+    return result;
+  }
+
+  @override
+  Request toRequest(String id) {
+    return new Request(id, "completion.registerLibraryPaths", toJson());
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is CompletionRegisterLibraryPathsParams) {
+      return listEqual(
+          paths, other.paths, (LibraryPathSet a, LibraryPathSet b) => a == b);
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, paths.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
+ * completion.registerLibraryPaths result
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class CompletionRegisterLibraryPathsResult implements ResponseResult {
+  @override
+  Map<String, dynamic> toJson() => <String, dynamic>{};
+
+  @override
+  Response toResponse(String id) {
+    return new Response(id, result: null);
+  }
+
+  @override
+  bool operator ==(other) {
+    if (other is CompletionRegisterLibraryPathsResult) {
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    return 104675661;
+  }
+}
+
+/**
  * completion.results params
  *
  * {
@@ -5399,6 +6343,9 @@
  *   "replacementLength": int
  *   "results": List<CompletionSuggestion>
  *   "isLast": bool
+ *   "includedSuggestionSets": optional List<IncludedSuggestionSet>
+ *   "includedSuggestionKinds": optional List<ElementKind>
+ *   "includedSuggestionRelevanceTags": optional List<IncludedSuggestionRelevanceTag>
  * }
  *
  * Clients may not extend, implement or mix-in this class.
@@ -5414,6 +6361,12 @@
 
   bool _isLast;
 
+  List<IncludedSuggestionSet> _includedSuggestionSets;
+
+  List<ElementKind> _includedSuggestionKinds;
+
+  List<IncludedSuggestionRelevanceTag> _includedSuggestionRelevanceTags;
+
   /**
    * The id associated with the completion.
    */
@@ -5499,13 +6452,92 @@
     this._isLast = value;
   }
 
+  /**
+   * This field is experimental.
+   *
+   * References to AvailableSuggestionSet objects previously sent to the
+   * client. The client can include applicable names from the referenced
+   * library in code completion suggestions.
+   */
+  List<IncludedSuggestionSet> get includedSuggestionSets =>
+      _includedSuggestionSets;
+
+  /**
+   * This field is experimental.
+   *
+   * References to AvailableSuggestionSet objects previously sent to the
+   * client. The client can include applicable names from the referenced
+   * library in code completion suggestions.
+   */
+  void set includedSuggestionSets(List<IncludedSuggestionSet> value) {
+    this._includedSuggestionSets = value;
+  }
+
+  /**
+   * This field is experimental.
+   *
+   * The client is expected to check this list against the ElementKind sent in
+   * IncludedSuggestionSet to decide whether or not these symbols should should
+   * be presented to the user.
+   */
+  List<ElementKind> get includedSuggestionKinds => _includedSuggestionKinds;
+
+  /**
+   * This field is experimental.
+   *
+   * The client is expected to check this list against the ElementKind sent in
+   * IncludedSuggestionSet to decide whether or not these symbols should should
+   * be presented to the user.
+   */
+  void set includedSuggestionKinds(List<ElementKind> value) {
+    this._includedSuggestionKinds = value;
+  }
+
+  /**
+   * This field is experimental.
+   *
+   * The client is expected to check this list against the values of the field
+   * relevanceTags of AvailableSuggestion to decide if the suggestion should be
+   * given a different relevance than the IncludedSuggestionSet that contains
+   * it. This might be used for example to give higher relevance to suggestions
+   * of matching types.
+   *
+   * If an AvailableSuggestion has relevance tags that match more than one
+   * IncludedSuggestionRelevanceTag, the maximum relevance boost is used.
+   */
+  List<IncludedSuggestionRelevanceTag> get includedSuggestionRelevanceTags =>
+      _includedSuggestionRelevanceTags;
+
+  /**
+   * This field is experimental.
+   *
+   * The client is expected to check this list against the values of the field
+   * relevanceTags of AvailableSuggestion to decide if the suggestion should be
+   * given a different relevance than the IncludedSuggestionSet that contains
+   * it. This might be used for example to give higher relevance to suggestions
+   * of matching types.
+   *
+   * If an AvailableSuggestion has relevance tags that match more than one
+   * IncludedSuggestionRelevanceTag, the maximum relevance boost is used.
+   */
+  void set includedSuggestionRelevanceTags(
+      List<IncludedSuggestionRelevanceTag> value) {
+    this._includedSuggestionRelevanceTags = value;
+  }
+
   CompletionResultsParams(String id, int replacementOffset,
-      int replacementLength, List<CompletionSuggestion> results, bool isLast) {
+      int replacementLength, List<CompletionSuggestion> results, bool isLast,
+      {List<IncludedSuggestionSet> includedSuggestionSets,
+      List<ElementKind> includedSuggestionKinds,
+      List<IncludedSuggestionRelevanceTag> includedSuggestionRelevanceTags}) {
     this.id = id;
     this.replacementOffset = replacementOffset;
     this.replacementLength = replacementLength;
     this.results = results;
     this.isLast = isLast;
+    this.includedSuggestionSets = includedSuggestionSets;
+    this.includedSuggestionKinds = includedSuggestionKinds;
+    this.includedSuggestionRelevanceTags = includedSuggestionRelevanceTags;
   }
 
   factory CompletionResultsParams.fromJson(
@@ -5550,8 +6582,37 @@
       } else {
         throw jsonDecoder.mismatch(jsonPath, "isLast");
       }
+      List<IncludedSuggestionSet> includedSuggestionSets;
+      if (json.containsKey("includedSuggestionSets")) {
+        includedSuggestionSets = jsonDecoder.decodeList(
+            jsonPath + ".includedSuggestionSets",
+            json["includedSuggestionSets"],
+            (String jsonPath, Object json) =>
+                new IncludedSuggestionSet.fromJson(
+                    jsonDecoder, jsonPath, json));
+      }
+      List<ElementKind> includedSuggestionKinds;
+      if (json.containsKey("includedSuggestionKinds")) {
+        includedSuggestionKinds = jsonDecoder.decodeList(
+            jsonPath + ".includedSuggestionKinds",
+            json["includedSuggestionKinds"],
+            (String jsonPath, Object json) =>
+                new ElementKind.fromJson(jsonDecoder, jsonPath, json));
+      }
+      List<IncludedSuggestionRelevanceTag> includedSuggestionRelevanceTags;
+      if (json.containsKey("includedSuggestionRelevanceTags")) {
+        includedSuggestionRelevanceTags = jsonDecoder.decodeList(
+            jsonPath + ".includedSuggestionRelevanceTags",
+            json["includedSuggestionRelevanceTags"],
+            (String jsonPath, Object json) =>
+                new IncludedSuggestionRelevanceTag.fromJson(
+                    jsonDecoder, jsonPath, json));
+      }
       return new CompletionResultsParams(
-          id, replacementOffset, replacementLength, results, isLast);
+          id, replacementOffset, replacementLength, results, isLast,
+          includedSuggestionSets: includedSuggestionSets,
+          includedSuggestionKinds: includedSuggestionKinds,
+          includedSuggestionRelevanceTags: includedSuggestionRelevanceTags);
     } else {
       throw jsonDecoder.mismatch(jsonPath, "completion.results params", json);
     }
@@ -5571,6 +6632,22 @@
     result["results"] =
         results.map((CompletionSuggestion value) => value.toJson()).toList();
     result["isLast"] = isLast;
+    if (includedSuggestionSets != null) {
+      result["includedSuggestionSets"] = includedSuggestionSets
+          .map((IncludedSuggestionSet value) => value.toJson())
+          .toList();
+    }
+    if (includedSuggestionKinds != null) {
+      result["includedSuggestionKinds"] = includedSuggestionKinds
+          .map((ElementKind value) => value.toJson())
+          .toList();
+    }
+    if (includedSuggestionRelevanceTags != null) {
+      result["includedSuggestionRelevanceTags"] =
+          includedSuggestionRelevanceTags
+              .map((IncludedSuggestionRelevanceTag value) => value.toJson())
+              .toList();
+    }
     return result;
   }
 
@@ -5589,7 +6666,17 @@
           replacementLength == other.replacementLength &&
           listEqual(results, other.results,
               (CompletionSuggestion a, CompletionSuggestion b) => a == b) &&
-          isLast == other.isLast;
+          isLast == other.isLast &&
+          listEqual(includedSuggestionSets, other.includedSuggestionSets,
+              (IncludedSuggestionSet a, IncludedSuggestionSet b) => a == b) &&
+          listEqual(includedSuggestionKinds, other.includedSuggestionKinds,
+              (ElementKind a, ElementKind b) => a == b) &&
+          listEqual(
+              includedSuggestionRelevanceTags,
+              other.includedSuggestionRelevanceTags,
+              (IncludedSuggestionRelevanceTag a,
+                      IncludedSuggestionRelevanceTag b) =>
+                  a == b);
     }
     return false;
   }
@@ -5602,11 +6689,191 @@
     hash = JenkinsSmiHash.combine(hash, replacementLength.hashCode);
     hash = JenkinsSmiHash.combine(hash, results.hashCode);
     hash = JenkinsSmiHash.combine(hash, isLast.hashCode);
+    hash = JenkinsSmiHash.combine(hash, includedSuggestionSets.hashCode);
+    hash = JenkinsSmiHash.combine(hash, includedSuggestionKinds.hashCode);
+    hash =
+        JenkinsSmiHash.combine(hash, includedSuggestionRelevanceTags.hashCode);
     return JenkinsSmiHash.finish(hash);
   }
 }
 
 /**
+ * CompletionService
+ *
+ * enum {
+ *   AVAILABLE_SUGGESTION_SETS
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class CompletionService implements Enum {
+  /**
+   * The client will receive notifications once subscribed with completion
+   * suggestion sets from the libraries of interest. The client should keep an
+   * up-to-date record of these in memory so that it will be able to union
+   * these candidates with other completion suggestions when applicable at
+   * completion time.
+   */
+  static const CompletionService AVAILABLE_SUGGESTION_SETS =
+      const CompletionService._("AVAILABLE_SUGGESTION_SETS");
+
+  /**
+   * A list containing all of the enum values that are defined.
+   */
+  static const List<CompletionService> VALUES = const <CompletionService>[
+    AVAILABLE_SUGGESTION_SETS
+  ];
+
+  @override
+  final String name;
+
+  const CompletionService._(this.name);
+
+  factory CompletionService(String name) {
+    switch (name) {
+      case "AVAILABLE_SUGGESTION_SETS":
+        return AVAILABLE_SUGGESTION_SETS;
+    }
+    throw new Exception('Illegal enum value: $name');
+  }
+
+  factory CompletionService.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json is String) {
+      try {
+        return new CompletionService(json);
+      } catch (_) {
+        // Fall through
+      }
+    }
+    throw jsonDecoder.mismatch(jsonPath, "CompletionService", json);
+  }
+
+  @override
+  String toString() => "CompletionService.$name";
+
+  String toJson() => name;
+}
+
+/**
+ * completion.setSubscriptions params
+ *
+ * {
+ *   "subscriptions": List<CompletionService>
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class CompletionSetSubscriptionsParams implements RequestParams {
+  List<CompletionService> _subscriptions;
+
+  /**
+   * A list of the services being subscribed to.
+   */
+  List<CompletionService> get subscriptions => _subscriptions;
+
+  /**
+   * A list of the services being subscribed to.
+   */
+  void set subscriptions(List<CompletionService> value) {
+    assert(value != null);
+    this._subscriptions = value;
+  }
+
+  CompletionSetSubscriptionsParams(List<CompletionService> subscriptions) {
+    this.subscriptions = subscriptions;
+  }
+
+  factory CompletionSetSubscriptionsParams.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      List<CompletionService> subscriptions;
+      if (json.containsKey("subscriptions")) {
+        subscriptions = jsonDecoder.decodeList(
+            jsonPath + ".subscriptions",
+            json["subscriptions"],
+            (String jsonPath, Object json) =>
+                new CompletionService.fromJson(jsonDecoder, jsonPath, json));
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "subscriptions");
+      }
+      return new CompletionSetSubscriptionsParams(subscriptions);
+    } else {
+      throw jsonDecoder.mismatch(
+          jsonPath, "completion.setSubscriptions params", json);
+    }
+  }
+
+  factory CompletionSetSubscriptionsParams.fromRequest(Request request) {
+    return new CompletionSetSubscriptionsParams.fromJson(
+        new RequestDecoder(request), "params", request.params);
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["subscriptions"] =
+        subscriptions.map((CompletionService value) => value.toJson()).toList();
+    return result;
+  }
+
+  @override
+  Request toRequest(String id) {
+    return new Request(id, "completion.setSubscriptions", toJson());
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is CompletionSetSubscriptionsParams) {
+      return listEqual(subscriptions, other.subscriptions,
+          (CompletionService a, CompletionService b) => a == b);
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, subscriptions.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
+ * completion.setSubscriptions result
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class CompletionSetSubscriptionsResult implements ResponseResult {
+  @override
+  Map<String, dynamic> toJson() => <String, dynamic>{};
+
+  @override
+  Response toResponse(String id) {
+    return new Response(id, result: null);
+  }
+
+  @override
+  bool operator ==(other) {
+    if (other is CompletionSetSubscriptionsResult) {
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    return 2482770;
+  }
+}
+
+/**
  * ContextData
  *
  * {
@@ -5877,6 +7144,132 @@
 }
 
 /**
+ * DartFix
+ *
+ * {
+ *   "name": String
+ *   "description": optional String
+ *   "isRequired": optional bool
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class DartFix implements HasToJson {
+  String _name;
+
+  String _description;
+
+  bool _isRequired;
+
+  /**
+   * The name of the fix.
+   */
+  String get name => _name;
+
+  /**
+   * The name of the fix.
+   */
+  void set name(String value) {
+    assert(value != null);
+    this._name = value;
+  }
+
+  /**
+   * A human readable description of the fix.
+   */
+  String get description => _description;
+
+  /**
+   * A human readable description of the fix.
+   */
+  void set description(String value) {
+    this._description = value;
+  }
+
+  /**
+   * `true` if the fix is in the "required" fixes group.
+   */
+  bool get isRequired => _isRequired;
+
+  /**
+   * `true` if the fix is in the "required" fixes group.
+   */
+  void set isRequired(bool value) {
+    this._isRequired = value;
+  }
+
+  DartFix(String name, {String description, bool isRequired}) {
+    this.name = name;
+    this.description = description;
+    this.isRequired = isRequired;
+  }
+
+  factory DartFix.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      String name;
+      if (json.containsKey("name")) {
+        name = jsonDecoder.decodeString(jsonPath + ".name", json["name"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "name");
+      }
+      String description;
+      if (json.containsKey("description")) {
+        description = jsonDecoder.decodeString(
+            jsonPath + ".description", json["description"]);
+      }
+      bool isRequired;
+      if (json.containsKey("isRequired")) {
+        isRequired = jsonDecoder.decodeBool(
+            jsonPath + ".isRequired", json["isRequired"]);
+      }
+      return new DartFix(name,
+          description: description, isRequired: isRequired);
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, "DartFix", json);
+    }
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["name"] = name;
+    if (description != null) {
+      result["description"] = description;
+    }
+    if (isRequired != null) {
+      result["isRequired"] = isRequired;
+    }
+    return result;
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is DartFix) {
+      return name == other.name &&
+          description == other.description &&
+          isRequired == other.isRequired;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, name.hashCode);
+    hash = JenkinsSmiHash.combine(hash, description.hashCode);
+    hash = JenkinsSmiHash.combine(hash, isRequired.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
  * DartFixSuggestion
  *
  * {
@@ -6214,6 +7607,9 @@
  *
  * {
  *   "included": List<FilePath>
+ *   "includedFixes": optional List<String>
+ *   "includeRequiredFixes": optional bool
+ *   "excludedFixes": optional List<String>
  * }
  *
  * Clients may not extend, implement or mix-in this class.
@@ -6221,6 +7617,12 @@
 class EditDartfixParams implements RequestParams {
   List<String> _included;
 
+  List<String> _includedFixes;
+
+  bool _includeRequiredFixes;
+
+  List<String> _excludedFixes;
+
   /**
    * A list of the files and directories for which edits should be suggested.
    *
@@ -6248,8 +7650,62 @@
     this._included = value;
   }
 
-  EditDartfixParams(List<String> included) {
+  /**
+   * A list of names indicating which fixes should be applied.
+   *
+   * If a name is specified that does not match the name of a known fix, an
+   * error of type UNKNOWN_FIX will be generated.
+   */
+  List<String> get includedFixes => _includedFixes;
+
+  /**
+   * A list of names indicating which fixes should be applied.
+   *
+   * If a name is specified that does not match the name of a known fix, an
+   * error of type UNKNOWN_FIX will be generated.
+   */
+  void set includedFixes(List<String> value) {
+    this._includedFixes = value;
+  }
+
+  /**
+   * A flag indicating that "required" fixes should be applied.
+   */
+  bool get includeRequiredFixes => _includeRequiredFixes;
+
+  /**
+   * A flag indicating that "required" fixes should be applied.
+   */
+  void set includeRequiredFixes(bool value) {
+    this._includeRequiredFixes = value;
+  }
+
+  /**
+   * A list of names indicating which fixes should not be applied.
+   *
+   * If a name is specified that does not match the name of a known fix, an
+   * error of type UNKNOWN_FIX will be generated.
+   */
+  List<String> get excludedFixes => _excludedFixes;
+
+  /**
+   * A list of names indicating which fixes should not be applied.
+   *
+   * If a name is specified that does not match the name of a known fix, an
+   * error of type UNKNOWN_FIX will be generated.
+   */
+  void set excludedFixes(List<String> value) {
+    this._excludedFixes = value;
+  }
+
+  EditDartfixParams(List<String> included,
+      {List<String> includedFixes,
+      bool includeRequiredFixes,
+      List<String> excludedFixes}) {
     this.included = included;
+    this.includedFixes = includedFixes;
+    this.includeRequiredFixes = includeRequiredFixes;
+    this.excludedFixes = excludedFixes;
   }
 
   factory EditDartfixParams.fromJson(
@@ -6265,7 +7721,25 @@
       } else {
         throw jsonDecoder.mismatch(jsonPath, "included");
       }
-      return new EditDartfixParams(included);
+      List<String> includedFixes;
+      if (json.containsKey("includedFixes")) {
+        includedFixes = jsonDecoder.decodeList(jsonPath + ".includedFixes",
+            json["includedFixes"], jsonDecoder.decodeString);
+      }
+      bool includeRequiredFixes;
+      if (json.containsKey("includeRequiredFixes")) {
+        includeRequiredFixes = jsonDecoder.decodeBool(
+            jsonPath + ".includeRequiredFixes", json["includeRequiredFixes"]);
+      }
+      List<String> excludedFixes;
+      if (json.containsKey("excludedFixes")) {
+        excludedFixes = jsonDecoder.decodeList(jsonPath + ".excludedFixes",
+            json["excludedFixes"], jsonDecoder.decodeString);
+      }
+      return new EditDartfixParams(included,
+          includedFixes: includedFixes,
+          includeRequiredFixes: includeRequiredFixes,
+          excludedFixes: excludedFixes);
     } else {
       throw jsonDecoder.mismatch(jsonPath, "edit.dartfix params", json);
     }
@@ -6280,6 +7754,15 @@
   Map<String, dynamic> toJson() {
     Map<String, dynamic> result = {};
     result["included"] = included;
+    if (includedFixes != null) {
+      result["includedFixes"] = includedFixes;
+    }
+    if (includeRequiredFixes != null) {
+      result["includeRequiredFixes"] = includeRequiredFixes;
+    }
+    if (excludedFixes != null) {
+      result["excludedFixes"] = excludedFixes;
+    }
     return result;
   }
 
@@ -6295,7 +7778,12 @@
   bool operator ==(other) {
     if (other is EditDartfixParams) {
       return listEqual(
-          included, other.included, (String a, String b) => a == b);
+              included, other.included, (String a, String b) => a == b) &&
+          listEqual(includedFixes, other.includedFixes,
+              (String a, String b) => a == b) &&
+          includeRequiredFixes == other.includeRequiredFixes &&
+          listEqual(excludedFixes, other.excludedFixes,
+              (String a, String b) => a == b);
     }
     return false;
   }
@@ -6304,6 +7792,9 @@
   int get hashCode {
     int hash = 0;
     hash = JenkinsSmiHash.combine(hash, included.hashCode);
+    hash = JenkinsSmiHash.combine(hash, includedFixes.hashCode);
+    hash = JenkinsSmiHash.combine(hash, includeRequiredFixes.hashCode);
+    hash = JenkinsSmiHash.combine(hash, excludedFixes.hashCode);
     return JenkinsSmiHash.finish(hash);
   }
 }
@@ -7267,6 +8758,152 @@
 }
 
 /**
+ * edit.getDartfixInfo params
+ *
+ * {
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class EditGetDartfixInfoParams implements RequestParams {
+  EditGetDartfixInfoParams();
+
+  factory EditGetDartfixInfoParams.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      return new EditGetDartfixInfoParams();
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, "edit.getDartfixInfo params", json);
+    }
+  }
+
+  factory EditGetDartfixInfoParams.fromRequest(Request request) {
+    return new EditGetDartfixInfoParams.fromJson(
+        new RequestDecoder(request), "params", request.params);
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    return result;
+  }
+
+  @override
+  Request toRequest(String id) {
+    return new Request(id, "edit.getDartfixInfo", toJson());
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is EditGetDartfixInfoParams) {
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
+ * edit.getDartfixInfo result
+ *
+ * {
+ *   "fixes": List<DartFix>
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class EditGetDartfixInfoResult implements ResponseResult {
+  List<DartFix> _fixes;
+
+  /**
+   * A list of fixes that can be specified in an edit.dartfix request.
+   */
+  List<DartFix> get fixes => _fixes;
+
+  /**
+   * A list of fixes that can be specified in an edit.dartfix request.
+   */
+  void set fixes(List<DartFix> value) {
+    assert(value != null);
+    this._fixes = value;
+  }
+
+  EditGetDartfixInfoResult(List<DartFix> fixes) {
+    this.fixes = fixes;
+  }
+
+  factory EditGetDartfixInfoResult.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      List<DartFix> fixes;
+      if (json.containsKey("fixes")) {
+        fixes = jsonDecoder.decodeList(
+            jsonPath + ".fixes",
+            json["fixes"],
+            (String jsonPath, Object json) =>
+                new DartFix.fromJson(jsonDecoder, jsonPath, json));
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "fixes");
+      }
+      return new EditGetDartfixInfoResult(fixes);
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, "edit.getDartfixInfo result", json);
+    }
+  }
+
+  factory EditGetDartfixInfoResult.fromResponse(Response response) {
+    return new EditGetDartfixInfoResult.fromJson(
+        new ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)),
+        "result",
+        response.result);
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["fixes"] = fixes.map((DartFix value) => value.toJson()).toList();
+    return result;
+  }
+
+  @override
+  Response toResponse(String id) {
+    return new Response(id, result: toJson());
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is EditGetDartfixInfoResult) {
+      return listEqual(fixes, other.fixes, (DartFix a, DartFix b) => a == b);
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, fixes.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
  * edit.getFixes params
  *
  * {
@@ -14330,6 +15967,213 @@
 }
 
 /**
+ * IncludedSuggestionRelevanceTag
+ *
+ * {
+ *   "tag": AvailableSuggestionRelevanceTag
+ *   "relevanceBoost": int
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class IncludedSuggestionRelevanceTag implements HasToJson {
+  String _tag;
+
+  int _relevanceBoost;
+
+  /**
+   * The opaque value of the tag.
+   */
+  String get tag => _tag;
+
+  /**
+   * The opaque value of the tag.
+   */
+  void set tag(String value) {
+    assert(value != null);
+    this._tag = value;
+  }
+
+  /**
+   * The boost to the relevance of the completion suggestions that match this
+   * tag, which is added to the relevance of the containing
+   * IncludedSuggestionSet.
+   */
+  int get relevanceBoost => _relevanceBoost;
+
+  /**
+   * The boost to the relevance of the completion suggestions that match this
+   * tag, which is added to the relevance of the containing
+   * IncludedSuggestionSet.
+   */
+  void set relevanceBoost(int value) {
+    assert(value != null);
+    this._relevanceBoost = value;
+  }
+
+  IncludedSuggestionRelevanceTag(String tag, int relevanceBoost) {
+    this.tag = tag;
+    this.relevanceBoost = relevanceBoost;
+  }
+
+  factory IncludedSuggestionRelevanceTag.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      String tag;
+      if (json.containsKey("tag")) {
+        tag = jsonDecoder.decodeString(jsonPath + ".tag", json["tag"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "tag");
+      }
+      int relevanceBoost;
+      if (json.containsKey("relevanceBoost")) {
+        relevanceBoost = jsonDecoder.decodeInt(
+            jsonPath + ".relevanceBoost", json["relevanceBoost"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "relevanceBoost");
+      }
+      return new IncludedSuggestionRelevanceTag(tag, relevanceBoost);
+    } else {
+      throw jsonDecoder.mismatch(
+          jsonPath, "IncludedSuggestionRelevanceTag", json);
+    }
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["tag"] = tag;
+    result["relevanceBoost"] = relevanceBoost;
+    return result;
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is IncludedSuggestionRelevanceTag) {
+      return tag == other.tag && relevanceBoost == other.relevanceBoost;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, tag.hashCode);
+    hash = JenkinsSmiHash.combine(hash, relevanceBoost.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
+ * IncludedSuggestionSet
+ *
+ * {
+ *   "id": int
+ *   "relevance": int
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class IncludedSuggestionSet implements HasToJson {
+  int _id;
+
+  int _relevance;
+
+  /**
+   * Clients should use it to access the set of precomputed completions to be
+   * displayed to the user.
+   */
+  int get id => _id;
+
+  /**
+   * Clients should use it to access the set of precomputed completions to be
+   * displayed to the user.
+   */
+  void set id(int value) {
+    assert(value != null);
+    this._id = value;
+  }
+
+  /**
+   * The relevance of completion suggestions from this library where a higher
+   * number indicates a higher relevance.
+   */
+  int get relevance => _relevance;
+
+  /**
+   * The relevance of completion suggestions from this library where a higher
+   * number indicates a higher relevance.
+   */
+  void set relevance(int value) {
+    assert(value != null);
+    this._relevance = value;
+  }
+
+  IncludedSuggestionSet(int id, int relevance) {
+    this.id = id;
+    this.relevance = relevance;
+  }
+
+  factory IncludedSuggestionSet.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      int id;
+      if (json.containsKey("id")) {
+        id = jsonDecoder.decodeInt(jsonPath + ".id", json["id"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "id");
+      }
+      int relevance;
+      if (json.containsKey("relevance")) {
+        relevance =
+            jsonDecoder.decodeInt(jsonPath + ".relevance", json["relevance"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "relevance");
+      }
+      return new IncludedSuggestionSet(id, relevance);
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, "IncludedSuggestionSet", json);
+    }
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["id"] = id;
+    result["relevance"] = relevance;
+    return result;
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is IncludedSuggestionSet) {
+      return id == other.id && relevance == other.relevance;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, id.hashCode);
+    hash = JenkinsSmiHash.combine(hash, relevance.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
  * inlineLocalVariable feedback
  *
  * {
@@ -14905,6 +16749,113 @@
 }
 
 /**
+ * LibraryPathSet
+ *
+ * {
+ *   "scope": FilePath
+ *   "libraryPaths": List<FilePath>
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class LibraryPathSet implements HasToJson {
+  String _scope;
+
+  List<String> _libraryPaths;
+
+  /**
+   * The filepath for which this request's libraries should be active in
+   * completion suggestions. This object associates filesystem regions to
+   * libraries and library directories of interest to the client.
+   */
+  String get scope => _scope;
+
+  /**
+   * The filepath for which this request's libraries should be active in
+   * completion suggestions. This object associates filesystem regions to
+   * libraries and library directories of interest to the client.
+   */
+  void set scope(String value) {
+    assert(value != null);
+    this._scope = value;
+  }
+
+  /**
+   * The paths of the libraries of interest to the client for completion
+   * suggestions.
+   */
+  List<String> get libraryPaths => _libraryPaths;
+
+  /**
+   * The paths of the libraries of interest to the client for completion
+   * suggestions.
+   */
+  void set libraryPaths(List<String> value) {
+    assert(value != null);
+    this._libraryPaths = value;
+  }
+
+  LibraryPathSet(String scope, List<String> libraryPaths) {
+    this.scope = scope;
+    this.libraryPaths = libraryPaths;
+  }
+
+  factory LibraryPathSet.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      String scope;
+      if (json.containsKey("scope")) {
+        scope = jsonDecoder.decodeString(jsonPath + ".scope", json["scope"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "scope");
+      }
+      List<String> libraryPaths;
+      if (json.containsKey("libraryPaths")) {
+        libraryPaths = jsonDecoder.decodeList(jsonPath + ".libraryPaths",
+            json["libraryPaths"], jsonDecoder.decodeString);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "libraryPaths");
+      }
+      return new LibraryPathSet(scope, libraryPaths);
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, "LibraryPathSet", json);
+    }
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["scope"] = scope;
+    result["libraryPaths"] = libraryPaths;
+    return result;
+  }
+
+  @override
+  String toString() => json.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is LibraryPathSet) {
+      return scope == other.scope &&
+          listEqual(
+              libraryPaths, other.libraryPaths, (String a, String b) => a == b);
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, scope.hashCode);
+    hash = JenkinsSmiHash.combine(hash, libraryPaths.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
  * moveFile feedback
  *
  * Clients may not extend, implement or mix-in this class.
@@ -15953,6 +17904,7 @@
  *   SERVER_ERROR
  *   SORT_MEMBERS_INVALID_FILE
  *   SORT_MEMBERS_PARSE_ERRORS
+ *   UNKNOWN_FIX
  *   UNKNOWN_REQUEST
  *   UNSUPPORTED_FEATURE
  * }
@@ -16143,6 +18095,13 @@
       const RequestErrorCode._("SORT_MEMBERS_PARSE_ERRORS");
 
   /**
+   * A dartfix request was received containing the name of a fix which does not
+   * match the name of any known fixes.
+   */
+  static const RequestErrorCode UNKNOWN_FIX =
+      const RequestErrorCode._("UNKNOWN_FIX");
+
+  /**
    * A request was received which the analysis server does not recognize, or
    * cannot handle in its current configuration.
    */
@@ -16189,6 +18148,7 @@
     SERVER_ERROR,
     SORT_MEMBERS_INVALID_FILE,
     SORT_MEMBERS_PARSE_ERRORS,
+    UNKNOWN_FIX,
     UNKNOWN_REQUEST,
     UNSUPPORTED_FEATURE
   ];
@@ -16252,6 +18212,8 @@
         return SORT_MEMBERS_INVALID_FILE;
       case "SORT_MEMBERS_PARSE_ERRORS":
         return SORT_MEMBERS_PARSE_ERRORS;
+      case "UNKNOWN_FIX":
+        return UNKNOWN_FIX;
       case "UNKNOWN_REQUEST":
         return UNKNOWN_REQUEST;
       case "UNSUPPORTED_FEATURE":
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index aff4e6b..745f48d 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -31,6 +31,7 @@
 import 'package:analysis_server/src/domains/analysis/navigation_dart.dart';
 import 'package:analysis_server/src/domains/analysis/occurrences.dart';
 import 'package:analysis_server/src/domains/analysis/occurrences_dart.dart';
+import 'package:analysis_server/src/domains/completion/available_suggestions.dart';
 import 'package:analysis_server/src/edit/edit_domain.dart';
 import 'package:analysis_server/src/flutter/flutter_domain.dart';
 import 'package:analysis_server/src/flutter/flutter_notifications.dart';
@@ -65,6 +66,7 @@
 import 'package:analyzer/src/generated/source_io.dart';
 import 'package:analyzer/src/generated/utilities_general.dart';
 import 'package:analyzer/src/plugin/resolver_provider.dart';
+import 'package:analyzer/src/services/available_declarations.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
 import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
 import 'package:telemetry/crash_reporting.dart';
@@ -150,6 +152,7 @@
 
   ByteStore byteStore;
   nd.AnalysisDriverScheduler analysisDriverScheduler;
+  DeclarationsTracker declarationsTracker;
 
   /// The controller for [onAnalysisSetChanged].
   final StreamController _onAnalysisSetChangedController =
@@ -276,6 +279,36 @@
     return _onAnalysisStartedController.stream;
   }
 
+  void createDeclarationsTracker(void Function(LibraryChange) listener) {
+    if (declarationsTracker != null) return;
+
+    declarationsTracker = DeclarationsTracker(byteStore, resourceProvider);
+    declarationsTracker.changes.listen(listener);
+
+    _addContextsToDeclarationsTracker();
+
+    // Configure the scheduler to run the tracker.
+    analysisDriverScheduler.outOfBandWorker =
+        CompletionLibrariesWorker(declarationsTracker);
+
+    // We might have done running drivers work, so ask the scheduler to check.
+    analysisDriverScheduler.notify(null);
+  }
+
+  /// Notify the declarations tracker that the file with the given [path] was
+  /// changed - added, updated, or removed.  Schedule processing of the file.
+  void notifyDeclarationsTracker(String path) {
+    if (declarationsTracker != null) {
+      declarationsTracker.changeFile(path);
+      analysisDriverScheduler.notify(null);
+    }
+  }
+
+  void disposeDeclarationsTracker() {
+    declarationsTracker = null;
+    analysisDriverScheduler.outOfBandWorker = null;
+  }
+
   /// The socket from which requests are being read has been closed.
   void done() {}
 
@@ -483,6 +516,7 @@
   /// projects/contexts support.
   void setAnalysisRoots(String requestId, List<String> includedPaths,
       List<String> excludedPaths, Map<String, String> packageRoots) {
+    declarationsTracker?.discardContexts();
     if (notificationManager != null) {
       notificationManager.setAnalysisRoots(includedPaths, excludedPaths);
     }
@@ -492,6 +526,7 @@
       throw new RequestFailure(
           new Response.unsupportedFeature(requestId, e.message));
     }
+    _addContextsToDeclarationsTracker();
   }
 
   /// Implementation for `analysis.setSubscriptions`.
@@ -623,8 +658,11 @@
       }
 
       if (newContents != null) {
-        resourceProvider.setOverlay(file,
-            content: newContents, modificationStamp: 0);
+        resourceProvider.setOverlay(
+          file,
+          content: newContents,
+          modificationStamp: overlayModificationStamp++,
+        );
       } else {
         resourceProvider.removeOverlay(file);
       }
@@ -637,6 +675,8 @@
       // analyzed. Add it to driver to which it should have been added.
       contextManager.getDriverFor(file)?.addFile(file);
 
+      notifyDeclarationsTracker(file);
+
       // TODO(scheglov) implement other cases
     });
   }
@@ -666,6 +706,15 @@
 //    });
   }
 
+  void _addContextsToDeclarationsTracker() {
+    if (declarationsTracker != null) {
+      for (var driver in driverMap.values) {
+        declarationsTracker.addContext(driver.analysisContext);
+        driver.resetUriResolution();
+      }
+    }
+  }
+
   /// Return the path to the location of the byte store on disk, or `null` if
   /// there is no on-disk byte store.
   String _getByteStorePath() {
@@ -913,6 +962,7 @@
 
   @override
   void broadcastWatchEvent(WatchEvent event) {
+    analysisServer.notifyDeclarationsTracker(event.path);
     analysisServer.pluginManager.broadcastWatchEvent(event);
   }
 
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index b0b3d34..50834b3 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -25,6 +25,7 @@
     show EvictingFileByteStore;
 import 'package:analyzer/src/dart/analysis/file_state.dart' as nd;
 import 'package:analyzer/src/dart/analysis/status.dart' as nd;
+import 'package:analyzer/src/dart/ast/element_locator.dart';
 import 'package:analyzer/src/dart/ast/utilities.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/util/glob.dart';
@@ -75,6 +76,9 @@
   /// The [ResourceProvider] using which paths are converted into [Resource]s.
   final OverlayResourceProvider resourceProvider;
 
+  /// The next modification stamp for a changed file in the [resourceProvider].
+  int overlayModificationStamp = 0;
+
   /// A list of the globs used to determine which files should be analyzed. The
   /// list is lazily created and should be accessed using [analyzedFilesGlobs].
   List<Glob> _analyzedFilesGlobs = null;
@@ -228,6 +232,15 @@
     return null;
   }
 
+  /// Return the unresolved unit for the file with the given [path].
+  ParsedUnitResult getParsedUnit(String path) {
+    if (!AnalysisEngine.isDartFileName(path)) {
+      return null;
+    }
+
+    return getAnalysisDriver(path)?.currentSession?.getParsedUnit(path);
+  }
+
   /// Return the resolved unit for the file with the given [path]. The file is
   /// analyzed in one of the analysis drivers to which the file was added,
   /// otherwise in the first driver, otherwise `null` is returned.
@@ -246,13 +259,4 @@
         .getResult(path, sendCachedToStream: sendCachedToStream)
         .catchError((_) => null);
   }
-
-  /// Return the unresolved unit for the file with the given [path].
-  ParsedUnitResult getParsedUnit(String path) {
-    if (!AnalysisEngine.isDartFileName(path)) {
-      return null;
-    }
-
-    return getAnalysisDriver(path)?.currentSession?.getParsedUnit(path);
-  }
 }
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
index dddc8b4..0076909 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -415,20 +415,6 @@
   }
 
   @override
-  void visitCollectionForElement(CollectionForElement node) {
-    computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN);
-    computer._addRegion_token(node.forKeyword, HighlightRegionType.KEYWORD);
-    super.visitCollectionForElement(node);
-  }
-
-  @override
-  void visitCollectionIfElement(CollectionIfElement node) {
-    computer._addRegion_token(node.ifKeyword, HighlightRegionType.KEYWORD);
-    computer._addRegion_token(node.elseKeyword, HighlightRegionType.KEYWORD);
-    super.visitCollectionIfElement(node);
-  }
-
-  @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
     computer._addRegion_token(
         node.externalKeyword, HighlightRegionType.BUILT_IN);
@@ -509,6 +495,13 @@
   }
 
   @override
+  void visitForElement(ForElement node) {
+    computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN);
+    computer._addRegion_token(node.forKeyword, HighlightRegionType.KEYWORD);
+    super.visitForElement(node);
+  }
+
+  @override
   void visitForStatement(ForStatement node) {
     computer._addRegion_token(node.forKeyword, HighlightRegionType.KEYWORD);
     super.visitForStatement(node);
@@ -557,6 +550,13 @@
   }
 
   @override
+  void visitIfElement(IfElement node) {
+    computer._addRegion_token(node.ifKeyword, HighlightRegionType.KEYWORD);
+    computer._addRegion_token(node.elseKeyword, HighlightRegionType.KEYWORD);
+    super.visitIfElement(node);
+  }
+
+  @override
   void visitIfStatement(IfStatement node) {
     computer._addRegion_token(node.ifKeyword, HighlightRegionType.KEYWORD);
     computer._addRegion_token(node.elseKeyword, HighlightRegionType.KEYWORD);
@@ -622,20 +622,6 @@
   }
 
   @override
-  void visitMapForElement(MapForElement node) {
-    computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN);
-    computer._addRegion_token(node.forKeyword, HighlightRegionType.KEYWORD);
-    super.visitMapForElement(node);
-  }
-
-  @override
-  void visitMapIfElement(MapIfElement node) {
-    computer._addRegion_token(node.ifKeyword, HighlightRegionType.KEYWORD);
-    computer._addRegion_token(node.elseKeyword, HighlightRegionType.KEYWORD);
-    super.visitMapIfElement(node);
-  }
-
-  @override
   void visitMapLiteral(MapLiteral node) {
     computer._addRegion_node(node, HighlightRegionType.LITERAL_MAP);
     computer._addRegion_token(node.constKeyword, HighlightRegionType.KEYWORD);
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights2.dart b/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
index aaea61e..1f0326f 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
@@ -506,20 +506,6 @@
   }
 
   @override
-  void visitCollectionForElement(CollectionForElement node) {
-    computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN);
-    computer._addRegion_token(node.forKeyword, HighlightRegionType.KEYWORD);
-    super.visitCollectionForElement(node);
-  }
-
-  @override
-  void visitCollectionIfElement(CollectionIfElement node) {
-    computer._addRegion_token(node.ifKeyword, HighlightRegionType.KEYWORD);
-    computer._addRegion_token(node.elseKeyword, HighlightRegionType.KEYWORD);
-    super.visitCollectionIfElement(node);
-  }
-
-  @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
     computer._addRegion_token(
         node.externalKeyword, HighlightRegionType.BUILT_IN);
@@ -600,6 +586,13 @@
   }
 
   @override
+  void visitForElement(ForElement node) {
+    computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN);
+    computer._addRegion_token(node.forKeyword, HighlightRegionType.KEYWORD);
+    super.visitForElement(node);
+  }
+
+  @override
   void visitForStatement(ForStatement node) {
     computer._addRegion_token(node.forKeyword, HighlightRegionType.KEYWORD);
     super.visitForStatement(node);
@@ -648,6 +641,13 @@
   }
 
   @override
+  void visitIfElement(IfElement node) {
+    computer._addRegion_token(node.ifKeyword, HighlightRegionType.KEYWORD);
+    computer._addRegion_token(node.elseKeyword, HighlightRegionType.KEYWORD);
+    super.visitIfElement(node);
+  }
+
+  @override
   void visitIfStatement(IfStatement node) {
     computer._addRegion_token(node.ifKeyword, HighlightRegionType.KEYWORD);
     computer._addRegion_token(node.elseKeyword, HighlightRegionType.KEYWORD);
@@ -719,20 +719,6 @@
   }
 
   @override
-  void visitMapForElement(MapForElement node) {
-    computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN);
-    computer._addRegion_token(node.forKeyword, HighlightRegionType.KEYWORD);
-    super.visitMapForElement(node);
-  }
-
-  @override
-  void visitMapIfElement(MapIfElement node) {
-    computer._addRegion_token(node.ifKeyword, HighlightRegionType.KEYWORD);
-    computer._addRegion_token(node.elseKeyword, HighlightRegionType.KEYWORD);
-    super.visitMapIfElement(node);
-  }
-
-  @override
   void visitMapLiteral(MapLiteral node) {
     computer._addRegion_node(node, HighlightRegionType.LITERAL_MAP);
     computer._addRegion_token(node.constKeyword, HighlightRegionType.KEYWORD);
diff --git a/pkg/analysis_server/lib/src/computer/computer_hover.dart b/pkg/analysis_server/lib/src/computer/computer_hover.dart
index eb20b24..3af0fbd 100644
--- a/pkg/analysis_server/lib/src/computer/computer_hover.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_hover.dart
@@ -5,11 +5,12 @@
 import 'package:analysis_server/protocol/protocol_generated.dart'
     show HoverInformation;
 import 'package:analysis_server/src/computer/computer_overrides.dart';
-import 'package:analysis_server/src/utilities/documentation.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/src/dart/ast/element_locator.dart';
 import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/util/comment.dart';
 
 /**
  * A computer for the hover at the specified offset of a Dart [CompilationUnit].
@@ -118,7 +119,7 @@
     }
     // The documentation of the element itself.
     if (element.documentationComment != null) {
-      return removeDartDocDelimiters(element.documentationComment);
+      return getDartDocPlainText(element.documentationComment);
     }
     // Look for documentation comments of overridden members.
     OverriddenElements overridden = findOverriddenElements(element);
@@ -128,7 +129,7 @@
       String rawDoc = superElement.documentationComment;
       if (rawDoc != null) {
         Element interfaceClass = superElement.enclosingElement;
-        return removeDartDocDelimiters(rawDoc) +
+        return getDartDocPlainText(rawDoc) +
             '\n\nCopied from `${interfaceClass.displayName}`.';
       }
     }
diff --git a/pkg/analysis_server/lib/src/computer/computer_overrides.dart b/pkg/analysis_server/lib/src/computer/computer_overrides.dart
index cd1ff67..7eb2b68 100644
--- a/pkg/analysis_server/lib/src/computer/computer_overrides.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_overrides.dart
@@ -32,7 +32,7 @@
    */
   List<proto.Override> compute() {
     for (CompilationUnitMember unitMember in _unit.declarations) {
-      if (unitMember is ClassDeclaration) {
+      if (unitMember is ClassOrMixinDeclaration) {
         for (ClassMember classMember in unitMember.members) {
           if (classMember is MethodDeclaration) {
             if (classMember.isStatic) {
@@ -198,6 +198,7 @@
 
     _addSuperOverrides(type.superclass);
     type.mixins.forEach(_addSuperOverrides);
+    type.superclassConstraints.forEach(_addSuperOverrides);
   }
 
   Element _lookupMember(ClassElement classElement) {
diff --git a/pkg/analysis_server/lib/src/computer/computer_signature.dart b/pkg/analysis_server/lib/src/computer/computer_signature.dart
index 518a488..2dd950c 100644
--- a/pkg/analysis_server/lib/src/computer/computer_signature.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_signature.dart
@@ -8,6 +8,7 @@
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/ast/element_locator.dart';
 import 'package:analyzer/src/dart/ast/utilities.dart';
 
 /**
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index c70db3b..b9d51ae 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -10,6 +10,7 @@
 import 'package:analysis_server/src/analysis_server.dart';
 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/plugin/plugin_manager.dart';
 import 'package:analysis_server/src/provisional/completion/completion_core.dart';
 import 'package:analysis_server/src/services/completion/completion_core.dart';
@@ -21,6 +22,7 @@
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_constants.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
 
 /**
  * Instances of the class [CompletionDomainHandler] implement a [RequestHandler]
@@ -33,6 +35,11 @@
   static const int performanceListMaxLength = 50;
 
   /**
+   * The completion services that the client is currently subscribed.
+   */
+  final Set<CompletionService> _subscriptions = Set<CompletionService>();
+
+  /**
    * The next completion response id.
    */
   int _nextCompletionId = 0;
@@ -66,8 +73,12 @@
    * Subclasses should override this method, append at least one result
    * to the [controller], and close the controller stream once complete.
    */
-  Future<CompletionResult> computeSuggestions(CompletionRequestImpl request,
-      CompletionGetSuggestionsParams params) async {
+  Future<CompletionResult> computeSuggestions(
+    CompletionRequestImpl request,
+    CompletionGetSuggestionsParams params,
+    Set<ElementKind> includedSuggestionKinds,
+    List<IncludedSuggestionRelevanceTag> includedSuggestionRelevanceTags,
+  ) async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
     //
@@ -91,11 +102,15 @@
       const COMPUTE_SUGGESTIONS_TAG = 'computeSuggestions';
       performance.logStartTime(COMPUTE_SUGGESTIONS_TAG);
 
-      CompletionContributor contributor = new DartCompletionManager();
-      String contributorTag = 'computeSuggestions - ${contributor.runtimeType}';
+      var manager = new DartCompletionManager(
+        includedSuggestionKinds: includedSuggestionKinds,
+        includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
+      );
+
+      String contributorTag = 'computeSuggestions - ${manager.runtimeType}';
       performance.logStartTime(contributorTag);
       try {
-        suggestions.addAll(await contributor.computeSuggestions(request));
+        suggestions.addAll(await manager.computeSuggestions(request));
       } on AbortCompletion {
         suggestions.clear();
       }
@@ -136,13 +151,78 @@
         request.replacementOffset, request.replacementLength, suggestions);
   }
 
+  /**
+   * 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 library = server.declarationsTracker.getLibrary(libraryId);
+    if (library == null) {
+      server.sendResponse(Response.invalidParameter(
+        request,
+        'libraryId',
+        'No such library: $libraryId',
+      ));
+      return;
+    }
+
+    var analysisDriver = server.getAnalysisDriver(file);
+    var session = analysisDriver.currentSession;
+    var resolvedLibrary = await session.getResolvedLibrary(file);
+    var requestedLibraryElement = await session.getLibraryByUri(library.uriStr);
+
+    // The label might be `MyEnum.myValue`, but we import only `MyEnum`.
+    var requestedName = params.label;
+    if (requestedName.contains('.')) {
+      requestedName = requestedName.substring(
+        0,
+        requestedName.indexOf('.'),
+      );
+    }
+
+    var completion = params.label;
+    var builder = DartChangeBuilder(session);
+    await builder.addFileEdit(file, (builder) {
+      var result = builder.importLibraryElement(
+        targetLibrary: resolvedLibrary,
+        targetPath: file,
+        targetOffset: params.offset,
+        requestedLibrary: requestedLibraryElement,
+        requestedName: requestedName,
+      );
+      if (result.prefix != null) {
+        completion = '${result.prefix}.$completion';
+      }
+    });
+
+    server.sendResponse(
+      CompletionGetSuggestionDetailsResult(
+        completion,
+        change: builder.sourceChange,
+      ).toResponse(request.id),
+    );
+  }
+
   @override
   Response handleRequest(Request request) {
     return runZoned(() {
       String requestName = request.method;
-      if (requestName == COMPLETION_REQUEST_GET_SUGGESTIONS) {
+
+      if (requestName == COMPLETION_REQUEST_GET_SUGGESTION_DETAILS) {
+        getSuggestionDetails(request);
+        return Response.DELAYED_RESPONSE;
+      } else if (requestName == COMPLETION_REQUEST_GET_SUGGESTIONS) {
         processRequest(request);
         return Response.DELAYED_RESPONSE;
+      } else if (requestName == COMPLETION_REQUEST_SET_SUBSCRIPTIONS) {
+        return setSubscriptions(request);
       }
       return null;
     }, onError: (exception, stackTrace) {
@@ -177,10 +257,9 @@
       return;
     }
 
-    ResolvedUnitResult result = await server.getResolvedUnit(file);
-
-    if (result?.state == ResultState.VALID) {
-      if (offset < 0 || offset > result.content.length) {
+    ResolvedUnitResult resolvedUnit = await server.getResolvedUnit(file);
+    if (resolvedUnit?.state == ResultState.VALID) {
+      if (offset < 0 || offset > resolvedUnit.content.length) {
         server.sendResponse(new Response.invalidParameter(
             request,
             'params.offset',
@@ -189,10 +268,10 @@
         return;
       }
 
-      recordRequest(performance, file, result.content, offset);
+      recordRequest(performance, file, resolvedUnit.content, offset);
     }
     CompletionRequestImpl completionRequest =
-        new CompletionRequestImpl(result, offset, performance);
+        new CompletionRequestImpl(resolvedUnit, offset, performance);
 
     String completionId = (_nextCompletionId++).toString();
 
@@ -202,14 +281,45 @@
     server.sendResponse(new CompletionGetSuggestionsResult(completionId)
         .toResponse(request.id));
 
+    // If the client opted into using available suggestion sets,
+    // create the kinds set, so signal the completion manager about opt-in.
+    Set<ElementKind> includedSuggestionKinds;
+    List<IncludedSuggestionRelevanceTag> includedSuggestionRelevanceTags;
+    if (_subscriptions.contains(CompletionService.AVAILABLE_SUGGESTION_SETS)) {
+      includedSuggestionKinds = Set<ElementKind>();
+      includedSuggestionRelevanceTags = <IncludedSuggestionRelevanceTag>[];
+    }
+
     // Compute suggestions in the background
-    computeSuggestions(completionRequest, params)
-        .then((CompletionResult result) {
+    computeSuggestions(
+      completionRequest,
+      params,
+      includedSuggestionKinds,
+      includedSuggestionRelevanceTags,
+    ).then((CompletionResult result) {
+      List<IncludedSuggestionSet> includedSuggestionSets;
+      if (includedSuggestionKinds != null && resolvedUnit != null) {
+        includedSuggestionSets = computeIncludedSetList(
+          server.declarationsTracker,
+          resolvedUnit,
+        );
+      } else {
+        includedSuggestionSets = [];
+      }
+
       const SEND_NOTIFICATION_TAG = 'send notification';
       performance.logStartTime(SEND_NOTIFICATION_TAG);
-      sendCompletionNotification(completionId, result.replacementOffset,
-          result.replacementLength, result.suggestions);
+      sendCompletionNotification(
+        completionId,
+        result.replacementOffset,
+        result.replacementLength,
+        result.suggestions,
+        includedSuggestionSets,
+        includedSuggestionKinds?.toList(),
+        includedSuggestionRelevanceTags,
+      );
       performance.logElapseTime(SEND_NOTIFICATION_TAG);
+
       performance.notificationCount = 1;
       performance.logFirstNotificationComplete('notification 1 complete');
       performance.suggestionCountFirst = result.suggestions.length;
@@ -237,11 +347,27 @@
   /**
    * Send completion notification results.
    */
-  void sendCompletionNotification(String completionId, int replacementOffset,
-      int replacementLength, Iterable<CompletionSuggestion> results) {
-    server.sendNotification(new CompletionResultsParams(
-            completionId, replacementOffset, replacementLength, results, true)
-        .toNotification());
+  void sendCompletionNotification(
+    String completionId,
+    int replacementOffset,
+    int replacementLength,
+    Iterable<CompletionSuggestion> results,
+    List<IncludedSuggestionSet> includedSuggestionSets,
+    List<ElementKind> includedSuggestionKinds,
+    List<IncludedSuggestionRelevanceTag> includedSuggestionRelevanceTags,
+  ) {
+    server.sendNotification(
+      new CompletionResultsParams(
+        completionId,
+        replacementOffset,
+        replacementLength,
+        results,
+        true,
+        includedSuggestionSets: includedSuggestionSets,
+        includedSuggestionKinds: includedSuggestionKinds,
+        includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
+      ).toNotification(),
+    );
   }
 
   void setNewRequest(CompletionRequest completionRequest) {
@@ -250,6 +376,28 @@
   }
 
   /**
+   * Implement the 'completion.setSubscriptions' request.
+   */
+  Response setSubscriptions(Request request) {
+    var params = CompletionSetSubscriptionsParams.fromRequest(request);
+
+    _subscriptions.clear();
+    _subscriptions.addAll(params.subscriptions);
+
+    if (_subscriptions.contains(CompletionService.AVAILABLE_SUGGESTION_SETS)) {
+      server.createDeclarationsTracker((change) {
+        server.sendNotification(
+          createCompletionAvailableSuggestionsNotification(change),
+        );
+      });
+    } else {
+      server.disposeDeclarationsTracker();
+    }
+
+    return CompletionSetSubscriptionsResult().toResponse(request.id);
+  }
+
+  /**
    * Abort the current completion request, if any.
    */
   void _abortCurrentRequest() {
diff --git a/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart b/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart
index 2885948..6139967 100644
--- a/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart
+++ b/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart
@@ -18,26 +18,11 @@
   ImplementedComputer(this.searchEngine, this.unitElement);
 
   compute() async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
-    for (ClassElement type in unitElement.types) {
-      // Always include Object and its members.
-      if (type.supertype == null) {
-        _addImplementedClass(type);
-        type.accessors.forEach(_addImplementedMember);
-        type.fields.forEach(_addImplementedMember);
-        type.methods.forEach(_addImplementedMember);
-        continue;
-      }
-
-      // Analyze subtypes.
-      subtypeMembers = await searchEngine.membersOfSubtypes(type);
-      if (subtypeMembers != null) {
-        _addImplementedClass(type);
-        type.accessors.forEach(_addMemberIfImplemented);
-        type.fields.forEach(_addMemberIfImplemented);
-        type.methods.forEach(_addMemberIfImplemented);
-      }
+    for (var element in unitElement.mixins) {
+      await _computeForClassElement(element);
+    }
+    for (var element in unitElement.types) {
+      await _computeForClassElement(element);
     }
   }
 
@@ -62,6 +47,26 @@
     }
   }
 
+  Future<void> _computeForClassElement(ClassElement element) async {
+    // Always include Object and its members.
+    if (element.supertype == null && !element.isMixin) {
+      _addImplementedClass(element);
+      element.accessors.forEach(_addImplementedMember);
+      element.fields.forEach(_addImplementedMember);
+      element.methods.forEach(_addImplementedMember);
+      return;
+    }
+
+    // Analyze subtypes.
+    subtypeMembers = await searchEngine.membersOfSubtypes(element);
+    if (subtypeMembers != null) {
+      _addImplementedClass(element);
+      element.accessors.forEach(_addMemberIfImplemented);
+      element.fields.forEach(_addMemberIfImplemented);
+      element.methods.forEach(_addMemberIfImplemented);
+    }
+  }
+
   bool _hasOverride(Element element) {
     String name = element.displayName;
     return subtypeMembers.contains(name);
diff --git a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
new file mode 100644
index 0000000..69f51f6
--- /dev/null
+++ b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
@@ -0,0 +1,173 @@
+// Copyright (c) 2019, 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/protocol_server.dart' as protocol;
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/src/services/available_declarations.dart';
+
+/// Compute which suggestion sets should be included into completion inside
+/// the given [resolvedUnit] of a file.  Depending on the file path, it might
+/// include different sets, e.g. inside the `lib/` directory of a `Pub` package
+/// only regular dependencies can be referenced, but `test/` can reference
+/// both regular and "dev" dependencies.
+List<protocol.IncludedSuggestionSet> computeIncludedSetList(
+  DeclarationsTracker tracker,
+  ResolvedUnitResult resolvedUnit,
+) {
+  var analysisContext = resolvedUnit.session.analysisContext;
+  var context = tracker.getContext(analysisContext);
+  if (context == null) return const [];
+
+  var librariesObject = context.getLibraries(resolvedUnit.path);
+  var includedSetList = <protocol.IncludedSuggestionSet>[];
+
+  var importedUriSet = resolvedUnit.libraryElement.importedLibraries
+      .map((importedLibrary) => importedLibrary.source.uri)
+      .toSet();
+
+  void includeLibrary(
+    Library library,
+    int importedRelevance,
+    int deprecatedRelevance,
+    int otherwiseRelevance,
+  ) {
+    int relevance;
+    if (importedUriSet.contains(library.uri)) {
+      relevance = importedRelevance;
+    } else if (library.isDeprecated) {
+      relevance = deprecatedRelevance;
+    } else {
+      relevance = otherwiseRelevance;
+    }
+
+    includedSetList.add(
+      protocol.IncludedSuggestionSet(library.id, relevance),
+    );
+  }
+
+  for (var library in librariesObject.context) {
+    includeLibrary(library, 8, 2, 5);
+  }
+
+  for (var library in librariesObject.dependencies) {
+    includeLibrary(library, 7, 1, 4);
+  }
+
+  for (var library in librariesObject.sdk) {
+    includeLibrary(library, 6, 0, 3);
+  }
+
+  return includedSetList;
+}
+
+/// Convert the [LibraryChange] into the corresponding protocol notification.
+protocol.Notification createCompletionAvailableSuggestionsNotification(
+  LibraryChange change,
+) {
+  return protocol.CompletionAvailableSuggestionsParams(
+    changedLibraries: change.changed.map((library) {
+      return protocol.AvailableSuggestionSet(
+        library.id,
+        library.uriStr,
+        library.declarations.map((declaration) {
+          return _protocolAvailableSuggestion(declaration);
+        }).toList(),
+      );
+    }).toList(),
+    removedLibraries: change.removed,
+  ).toNotification();
+}
+
+protocol.AvailableSuggestion _protocolAvailableSuggestion(
+    Declaration declaration) {
+  var label = declaration.name;
+  if (declaration.kind == DeclarationKind.ENUM_CONSTANT) {
+    label = '${declaration.name2}.${declaration.name}';
+  }
+
+  return protocol.AvailableSuggestion(
+    label,
+    _protocolElement(declaration),
+    docComplete: declaration.docComplete,
+    docSummary: declaration.docSummary,
+    parameterNames: declaration.parameterNames,
+    parameterTypes: declaration.parameterTypes,
+    requiredParameterCount: declaration.requiredParameterCount,
+    relevanceTags: declaration.relevanceTags,
+  );
+}
+
+protocol.Element _protocolElement(Declaration declaration) {
+  return protocol.Element(
+    _protocolElementKind(declaration.kind),
+    declaration.name,
+    _protocolElementFlags(declaration),
+    location: protocol.Location(
+      declaration.locationPath,
+      declaration.locationOffset,
+      0, // length
+      declaration.locationStartLine,
+      declaration.locationStartColumn,
+    ),
+    parameters: declaration.parameters,
+    returnType: declaration.returnType,
+    typeParameters: declaration.typeParameters,
+  );
+}
+
+int _protocolElementFlags(Declaration declaration) {
+  return protocol.Element.makeFlags(
+    isAbstract: declaration.isAbstract,
+    isConst: declaration.isConst,
+    isFinal: declaration.isFinal,
+    isDeprecated: declaration.isDeprecated,
+  );
+}
+
+protocol.ElementKind _protocolElementKind(DeclarationKind kind) {
+  switch (kind) {
+    case DeclarationKind.CLASS:
+      return protocol.ElementKind.CLASS;
+    case DeclarationKind.CLASS_TYPE_ALIAS:
+      return protocol.ElementKind.CLASS_TYPE_ALIAS;
+    case DeclarationKind.ENUM:
+      return protocol.ElementKind.ENUM;
+    case DeclarationKind.ENUM_CONSTANT:
+      return protocol.ElementKind.ENUM_CONSTANT;
+    case DeclarationKind.FUNCTION:
+      return protocol.ElementKind.FUNCTION;
+    case DeclarationKind.FUNCTION_TYPE_ALIAS:
+      return protocol.ElementKind.FUNCTION_TYPE_ALIAS;
+    case DeclarationKind.GETTER:
+      return protocol.ElementKind.GETTER;
+    case DeclarationKind.MIXIN:
+      return protocol.ElementKind.MIXIN;
+    case DeclarationKind.SETTER:
+      return protocol.ElementKind.SETTER;
+    case DeclarationKind.VARIABLE:
+      return protocol.ElementKind.TOP_LEVEL_VARIABLE;
+  }
+  return protocol.ElementKind.UNKNOWN;
+}
+
+class CompletionLibrariesWorker implements SchedulerWorker {
+  final DeclarationsTracker tracker;
+
+  CompletionLibrariesWorker(this.tracker);
+
+  @override
+  AnalysisDriverPriority get workPriority {
+    if (tracker.hasWork) {
+      return AnalysisDriverPriority.priority;
+    } else {
+      return AnalysisDriverPriority.nothing;
+    }
+  }
+
+  @override
+  Future<void> performWork() async {
+    tracker.doWork();
+  }
+}
diff --git a/pkg/analysis_server/lib/src/domains/execution/completion.dart b/pkg/analysis_server/lib/src/domains/execution/completion.dart
index dbba4ea..594580d 100644
--- a/pkg/analysis_server/lib/src/domains/execution/completion.dart
+++ b/pkg/analysis_server/lib/src/domains/execution/completion.dart
@@ -15,21 +15,19 @@
 import 'package:analysis_server/src/services/completion/completion_performance.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
 import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/overlay_file_system.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
-import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
 
 class RuntimeCompletionComputer {
-  final ResourceProvider resourceProvider;
-  final FileContentOverlay fileContentOverlay;
+  final OverlayResourceProvider resourceProvider;
   final AnalysisDriver analysisDriver;
 
   final String code;
   final int offset;
 
-  final String contextFile;
+  final String contextPath;
   final int contextOffset;
 
   final List<RuntimeCompletionVariable> variables;
@@ -37,11 +35,10 @@
 
   RuntimeCompletionComputer(
       this.resourceProvider,
-      this.fileContentOverlay,
       this.analysisDriver,
       this.code,
       this.offset,
-      this.contextFile,
+      this.contextPath,
       this.contextOffset,
       this.variables,
       this.expressions);
@@ -49,7 +46,7 @@
   Future<RuntimeCompletionResult> compute() async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
-    var contextResult = await analysisDriver.getResult(contextFile);
+    var contextResult = await analysisDriver.getResult(contextPath);
     var session = contextResult.session;
 
     const codeMarker = '__code_\_';
@@ -57,7 +54,7 @@
     // Insert the code being completed at the context offset.
     var changeBuilder = new DartChangeBuilder(session);
     int nextImportPrefixIndex = 0;
-    await changeBuilder.addFileEdit(contextFile, (builder) {
+    await changeBuilder.addFileEdit(contextPath, (builder) {
       builder.addInsertion(contextOffset, (builder) {
         builder.writeln('{');
 
@@ -83,15 +80,9 @@
     // Update the context file content to include the code being completed.
     // Then resolve it, and restore the file to its initial state.
     ResolvedUnitResult targetResult;
-    String contentFileOverlay = fileContentOverlay[contextFile];
-    try {
-      fileContentOverlay[contextFile] = targetCode;
-      analysisDriver.changeFile(contextFile);
-      targetResult = await analysisDriver.getResult(contextFile);
-    } finally {
-      fileContentOverlay[contextFile] = contentFileOverlay;
-      analysisDriver.changeFile(contextFile);
-    }
+    await _withContextFileContent(targetCode, () async {
+      targetResult = await analysisDriver.getResult(contextPath);
+    });
 
     CompletionContributor contributor = new DartCompletionManager();
     CompletionRequestImpl request = new CompletionRequestImpl(
@@ -108,6 +99,44 @@
     var expressions = <RuntimeCompletionExpression>[];
     return new RuntimeCompletionResult(expressions, suggestions);
   }
+
+  Future<void> _withContextFileContent(
+      String newContent, Future<void> Function() f) async {
+    if (resourceProvider.hasOverlay(contextPath)) {
+      var contextFile = resourceProvider.getFile(contextPath);
+      var prevOverlayContent = contextFile.readAsStringSync();
+      var prevOverlayStamp = contextFile.modificationStamp;
+      try {
+        resourceProvider.setOverlay(
+          contextPath,
+          content: newContent,
+          modificationStamp: 0,
+        );
+        analysisDriver.changeFile(contextPath);
+        await f();
+      } finally {
+        resourceProvider.setOverlay(
+          contextPath,
+          content: prevOverlayContent,
+          modificationStamp: prevOverlayStamp,
+        );
+        analysisDriver.changeFile(contextPath);
+      }
+    } else {
+      try {
+        resourceProvider.setOverlay(
+          contextPath,
+          content: newContent,
+          modificationStamp: 0,
+        );
+        analysisDriver.changeFile(contextPath);
+        await f();
+      } finally {
+        resourceProvider.removeOverlay(contextPath);
+        analysisDriver.changeFile(contextPath);
+      }
+    }
+  }
 }
 
 /// The result of performing runtime completion.
diff --git a/pkg/analysis_server/lib/src/edit/edit_dartfix.dart b/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
index c9b2ff1..9b5c226 100644
--- a/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
@@ -2,74 +2,69 @@
 // 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/plugin/edit/fix/fix_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/edit/fix/non_nullable_fix.dart';
-import 'package:analysis_server/src/edit/fix/prefer_int_literals_fix.dart';
-import 'package:analysis_server/src/edit/fix/prefer_mixin_fix.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_internal.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_info.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_listener.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_registrar.dart';
+import 'package:analysis_server/src/edit/fix/fix_code_task.dart';
+import 'package:analysis_server/src/edit/fix/fix_error_task.dart';
+import 'package:analysis_server/src/edit/fix/fix_lint_task.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/error/error.dart';
-import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/dart/ast/utilities.dart';
-import 'package:analyzer/src/error/codes.dart';
-import 'package:analyzer/src/generated/source.dart';
-import 'package:analyzer/src/lint/linter.dart';
-import 'package:analyzer/src/lint/linter_visitor.dart';
-import 'package:analyzer/src/lint/registry.dart';
-import 'package:analyzer/src/services/lint.dart';
-import 'package:analyzer_plugin/protocol/protocol_common.dart'
-    show Location, SourceChange, SourceEdit, SourceFileEdit;
-import 'package:front_end/src/fasta/fasta_codes.dart';
-import 'package:front_end/src/scanner/token.dart';
-import 'package:source_span/src/span.dart';
 
-class EditDartFix {
+class EditDartFix
+    with FixCodeProcessor, FixErrorProcessor, FixLintProcessor
+    implements DartFixRegistrar {
   final AnalysisServer server;
+
   final Request request;
   final fixFolders = <Folder>[];
   final fixFiles = <File>[];
 
-  List<DartFixSuggestion> suggestions;
-  List<DartFixSuggestion> otherSuggestions;
-  SourceChange sourceChange;
+  DartFixListener listener;
 
-  EditDartFix(this.server, this.request);
-
-  void addSourceChange(
-      String description, Location location, SourceChange change) {
-    suggestions.add(new DartFixSuggestion(description, location: location));
-    for (SourceFileEdit fileEdit in change.edits) {
-      for (SourceEdit sourceEdit in fileEdit.edits) {
-        sourceChange.addEdit(fileEdit.file, fileEdit.fileStamp, sourceEdit);
-      }
-    }
-  }
-
-  void addSourceFileEdit(
-      String description, Location location, SourceFileEdit fileEdit) {
-    suggestions.add(new DartFixSuggestion(description, location: location));
-    for (SourceEdit sourceEdit in fileEdit.edits) {
-      sourceChange.addEdit(fileEdit.file, fileEdit.fileStamp, sourceEdit);
-    }
-  }
-
-  void addRecommendation(String description, [Location location]) {
-    otherSuggestions
-        .add(new DartFixSuggestion(description, location: location));
+  EditDartFix(this.server, this.request) {
+    listener = new DartFixListener(server);
   }
 
   Future<Response> compute() async {
     final params = new EditDartfixParams.fromRequest(request);
 
+    // Determine the fixes to be applied
+    final fixInfo = <DartFixInfo>[];
+    if (params.includeRequiredFixes == true) {
+      fixInfo.addAll(allFixes.where((i) => i.isRequired));
+    }
+    if (params.includedFixes != null) {
+      for (String key in params.includedFixes) {
+        var info = allFixes.firstWhere((i) => i.key == key, orElse: () => null);
+        if (info != null) {
+          fixInfo.add(info);
+        } else {
+          // TODO(danrubel): Report unknown fix to the user
+        }
+      }
+    }
+    if (fixInfo.isEmpty) {
+      fixInfo.addAll(allFixes.where((i) => i.isDefault));
+    }
+    if (params.excludedFixes != null) {
+      for (String key in params.excludedFixes) {
+        var info = allFixes.firstWhere((i) => i.key == key, orElse: () => null);
+        if (info != null) {
+          fixInfo.remove(info);
+        } else {
+          // TODO(danrubel): Report unknown fix to the user
+        }
+      }
+    }
+    for (DartFixInfo info in fixInfo) {
+      info.setup(this, listener);
+    }
+
     // Validate each included file and directory.
     final resourceProvider = server.resourceProvider;
     final contextManager = server.contextManager;
@@ -90,156 +85,52 @@
       }
     }
 
-    // Get the desired lints
-    final lintRules = Registry.ruleRegistry;
-
-    final preferMixin = lintRules['prefer_mixin'];
-    final preferMixinFix = new PreferMixinFix(this);
-    preferMixin.reporter = preferMixinFix;
-
-    final preferIntLiterals = lintRules['prefer_int_literals'];
-    final preferIntLiteralsFix = new PreferIntLiteralsFix(this);
-    final nonNullableFix = new NonNullableFix(this);
-    preferIntLiterals?.reporter = preferIntLiteralsFix;
-
-    // Setup
-    final linters = <Linter>[
-      preferMixin,
-      preferIntLiterals,
-    ];
-    final fixes = <LinterFix>[
-      preferMixinFix,
-      preferIntLiteralsFix,
-    ];
-    final lintVisitorsBySession = <AnalysisSession, _LintVisitors>{};
-
-    // TODO(danrubel): Determine if a lint is configured to run as part of
-    // standard analysis and use those results if available instead of
-    // running the lint again.
-
-    // Analyze each source file.
-    final resources = <Resource>[];
-    for (String rootPath in contextManager.includedPaths) {
-      resources.add(resourceProvider.getResource(rootPath));
-    }
-    suggestions = <DartFixSuggestion>[];
-    otherSuggestions = <DartFixSuggestion>[];
-    sourceChange = new SourceChange('dartfix');
+    // Process each source file.
     bool hasErrors = false;
-    while (resources.isNotEmpty) {
-      Resource res = resources.removeLast();
-      if (res is Folder) {
-        for (Resource child in res.getChildren()) {
-          if (!child.shortName.startsWith('.') &&
-              contextManager.isInAnalysisRoot(child.path) &&
-              !contextManager.isIgnored(child.path)) {
-            resources.add(child);
-          }
+    String changedPath;
+    server.contextManager.driverMap.values
+        .forEach((d) => d.onCurrentSessionAboutToBeDiscarded = (String path) {
+              // Remember the resource that changed during analysis
+              changedPath = path;
+            });
+
+    try {
+      await processResources((ResolvedUnitResult result) async {
+        if (await processErrors(result)) {
+          hasErrors = true;
         }
-        continue;
+        await processLints(result);
+        await processCodeTasks(result);
+      });
+      if (needsSecondPass) {
+        await processResources((ResolvedUnitResult result) async {
+          await processCodeTasks2(result);
+        });
       }
-
-      const maxAttempts = 3;
-      int attempt = 0;
-      while (attempt < maxAttempts) {
-        ResolvedUnitResult result = await server.getResolvedUnit(res.path);
-
-        // TODO(danrubel): Investigate why InconsistentAnalysisException occurs
-        // and whether this is an appropriate way to handle the situation
-        ++attempt;
-        try {
-          CompilationUnit unit = result?.unit;
-          if (unit != null) {
-            if (!hasErrors) {
-              for (AnalysisError error in result.errors) {
-                if (!(await fixError(result, error))) {
-                  if (error.errorCode.type == ErrorType.SYNTACTIC_ERROR) {
-                    hasErrors = true;
-                  }
-                }
-              }
-            }
-            Source source = result.unit.declaredElement.source;
-            for (Linter linter in linters) {
-              if (linter != null) {
-                linter.reporter.source = source;
-              }
-            }
-            var lintVisitors = lintVisitorsBySession[result.session] ??=
-                await _setupLintVisitors(result, linters);
-            if (lintVisitors.astVisitor != null) {
-              unit.accept(lintVisitors.astVisitor);
-            }
-            unit.accept(lintVisitors.linterVisitor);
-            for (LinterFix fix in fixes) {
-              await fix.applyLocalFixes(result);
-            }
-            if (isIncluded(source.fullName)) {
-              nonNullableFix.applyLocalFixes(result);
-            }
-          }
-          break;
-        } on InconsistentAnalysisException catch (_) {
-          if (attempt == maxAttempts) {
-            // TODO(danrubel): Consider improving the edit.dartfix protocol
-            // to gracefully report inconsistent results for a particular
-            // file rather than aborting the entire operation.
-            rethrow;
-          }
-          // try again
-        }
-      }
-    }
-
-    // Cleanup
-    for (Linter linter in linters) {
-      if (linter != null) {
-        linter.reporter.source = null;
-        linter.reporter = null;
-      }
-    }
-
-    // Apply distributed fixes
-    if (preferIntLiterals == null) {
-      // TODO(danrubel): Remove this once linter rolled into sdk/third_party.
-      addRecommendation('*** Convert double literal not available'
-          ' because prefer_int_literal not found. May need to roll linter');
-    }
-    for (LinterFix fix in fixes) {
-      await fix.applyRemainingFixes();
+      await finishLints();
+      await finishCodeTasks();
+    } on InconsistentAnalysisException catch (_) {
+      // If a resource changed, report the problem without suggesting fixes
+      var changedMessage = changedPath != null
+          ? 'resource changed during analysis: $changedPath'
+          : 'multiple resources changed during analysis.';
+      return new EditDartfixResult(
+        [new DartFixSuggestion('Analysis canceled because $changedMessage')],
+        listener.otherSuggestions,
+        hasErrors,
+        listener.sourceChange.edits,
+      ).toResponse(request.id);
+    } finally {
+      server.contextManager.driverMap.values
+          .forEach((d) => d.onCurrentSessionAboutToBeDiscarded = null);
     }
 
     return new EditDartfixResult(
-            suggestions, otherSuggestions, hasErrors, sourceChange.edits)
-        .toResponse(request.id);
-  }
-
-  Future<bool> fixError(ResolvedUnitResult result, AnalysisError error) async {
-    if (error.errorCode ==
-        StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR) {
-      // TODO(danrubel): Rather than comparing the error codes individually,
-      // it would be better if each error code could specify
-      // whether or not it could be fixed automatically.
-
-      // Fall through to calculate and apply the fix
-    } else {
-      // This error cannot be automatically fixed
-      return false;
-    }
-
-    final workspace = DartChangeWorkspace(server.currentSessions);
-    final dartContext = new DartFixContextImpl(workspace, result, error);
-    final processor = new FixProcessor(dartContext);
-    Fix fix = await processor.computeFix();
-    final location = locationFor(result, error.offset, error.length);
-    if (fix != null) {
-      addSourceChange(fix.change.message, location, fix.change);
-    } else {
-      // TODO(danrubel): Determine why the fix could not be applied
-      // and report that in the description.
-      addRecommendation('Could not fix "${error.message}"', location);
-    }
-    return true;
+      listener.suggestions,
+      listener.otherSuggestions,
+      hasErrors,
+      listener.sourceChange.edits,
+    ).toResponse(request.id);
   }
 
   /// Return `true` if the path in within the set of `included` files
@@ -260,122 +151,35 @@
     return false;
   }
 
-  Location locationFor(ResolvedUnitResult result, int offset, int length) {
-    final locInfo = result.unit.lineInfo.getLocation(offset);
-    final location = new Location(
-        result.path, offset, length, locInfo.lineNumber, locInfo.columnNumber);
-    return location;
-  }
-
-  Future<_LintVisitors> _setupLintVisitors(
-      ResolvedUnitResult result, List<Linter> linters) async {
-    final visitors = <AstVisitor>[];
-    final registry = new NodeLintRegistry(false);
-    // TODO(paulberry): use an API that provides this information more readily
-    var unitElement = result.unit.declaredElement;
-    var session = result.session;
-    var currentUnit = LinterContextUnit(result.content, result.unit);
-    var allUnits = <LinterContextUnit>[];
-    for (var cu in unitElement.library.units) {
-      if (identical(cu, unitElement)) {
-        allUnits.add(currentUnit);
-      } else {
-        Source source = cu.source;
-        if (source != null) {
-          var result = await session.getResolvedUnit(source.fullName);
-          allUnits.add(LinterContextUnit(result.content, result.unit));
-        }
-      }
+  /// Call the supplied [process] function to process each compilation unit.
+  Future processResources(
+      Future<void> Function(ResolvedUnitResult result) process) async {
+    final contextManager = server.contextManager;
+    final resourceProvider = server.resourceProvider;
+    final resources = <Resource>[];
+    for (String rootPath in contextManager.includedPaths) {
+      resources.add(resourceProvider.getResource(rootPath));
     }
-    var context = LinterContextImpl(allUnits, currentUnit,
-        session.declaredVariables, result.typeProvider, result.typeSystem);
-    for (Linter linter in linters) {
-      if (linter != null) {
-        final visitor = linter.getVisitor();
-        if (visitor != null) {
-          visitors.add(visitor);
+    while (resources.isNotEmpty) {
+      Resource res = resources.removeLast();
+      if (res is Folder) {
+        for (Resource child in res.getChildren()) {
+          if (!child.shortName.startsWith('.') &&
+              contextManager.isInAnalysisRoot(child.path) &&
+              !contextManager.isIgnored(child.path)) {
+            resources.add(child);
+          }
         }
-        if (linter is NodeLintRule) {
-          (linter as NodeLintRule).registerNodeProcessors(registry, context);
-        }
+        continue;
       }
+      if (!isIncluded(res.path)) {
+        continue;
+      }
+      ResolvedUnitResult result = await server.getResolvedUnit(res.path);
+      if (result == null || result.unit == null) {
+        continue;
+      }
+      await process(result);
     }
-    final AstVisitor astVisitor = visitors.isNotEmpty
-        ? new ExceptionHandlingDelegatingAstVisitor(
-            visitors, ExceptionHandlingDelegatingAstVisitor.logException)
-        : null;
-    final AstVisitor linterVisitor = new LinterVisitor(
-        registry, ExceptionHandlingDelegatingAstVisitor.logException);
-    return _LintVisitors(astVisitor, linterVisitor);
   }
 }
-
-abstract class LinterFix implements ErrorReporter {
-  final EditDartFix dartFix;
-
-  @override
-  Source source;
-
-  LinterFix(this.dartFix);
-
-  /// Apply fixes for the current compilation unit.
-  Future<void> applyLocalFixes(ResolvedUnitResult result);
-
-  /// Apply any fixes remaining after analysis is complete.
-  Future<void> applyRemainingFixes();
-
-  @override
-  void reportError(AnalysisError error) {
-    // ignored
-  }
-
-  @override
-  void reportErrorForElement(ErrorCode errorCode, Element element,
-      [List<Object> arguments]) {
-    // ignored
-  }
-
-  @override
-  void reportErrorForNode(ErrorCode errorCode, AstNode node,
-      [List<Object> arguments]) {
-    // ignored
-  }
-
-  @override
-  void reportErrorForOffset(ErrorCode errorCode, int offset, int length,
-      [List<Object> arguments]) {
-    // ignored
-  }
-
-  @override
-  void reportErrorForSpan(ErrorCode errorCode, SourceSpan span,
-      [List<Object> arguments]) {
-    // ignored
-  }
-
-  @override
-  void reportErrorForToken(ErrorCode errorCode, Token token,
-      [List<Object> arguments]) {
-    // ignored
-  }
-
-  @override
-  void reportErrorMessage(
-      ErrorCode errorCode, int offset, int length, Message message) {
-    // ignored
-  }
-
-  @override
-  void reportTypeErrorForNode(
-      ErrorCode errorCode, AstNode node, List<Object> arguments) {
-    // ignored
-  }
-}
-
-class _LintVisitors {
-  final AstVisitor astVisitor;
-
-  final AstVisitor linterVisitor;
-
-  _LintVisitors(this.astVisitor, this.linterVisitor);
-}
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index f692c76..efccaeb 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -11,7 +11,8 @@
 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/edit/edit_dartfix.dart';
+import 'package:analysis_server/src/edit/edit_dartfix.dart' show EditDartFix;
+import 'package:analysis_server/src/edit/fix/dartfix_info.dart' show allFixes;
 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' hide Element;
@@ -226,6 +227,10 @@
         .sendResponse(new EditGetAssistsResult(changes).toResponse(request.id));
   }
 
+  Response getDartfixInfo(Request request) =>
+      new EditGetDartfixInfoResult(allFixes.map((i) => i.asDartFix()).toList())
+          .toResponse(request.id);
+
   Future getFixes(Request request) async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
@@ -356,6 +361,8 @@
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS) {
         return _getAvailableRefactorings(request);
+      } else if (requestName == EDIT_REQUEST_GET_DARTFIX_INFO) {
+        return getDartfixInfo(request);
       } else if (requestName == EDIT_REQUEST_GET_FIXES) {
         getFixes(request);
         return Response.DELAYED_RESPONSE;
diff --git a/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart b/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart
new file mode 100644
index 0000000..3e6c9c8
--- /dev/null
+++ b/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2019, 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/protocol/protocol_generated.dart' show DartFix;
+import 'package:analysis_server/src/edit/edit_dartfix.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_listener.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_registrar.dart';
+import 'package:analysis_server/src/edit/fix/fix_error_task.dart';
+import 'package:analysis_server/src/edit/fix/non_nullable_fix.dart';
+import 'package:analysis_server/src/edit/fix/prefer_int_literals_fix.dart';
+import 'package:analysis_server/src/edit/fix/prefer_mixin_fix.dart';
+
+const allFixes = <DartFixInfo>[
+  //
+  // Required fixes
+  //
+  const DartFixInfo(
+    'fix-named-constructor-type-arguments',
+    'Move named constructor type arguments from the name to the type.',
+    FixErrorTask.fixNamedConstructorTypeArgs,
+    isRequired: true,
+  ),
+  const DartFixInfo(
+    'use-mixin',
+    'Convert classes used as a mixin to the new mixin syntax.',
+    PreferMixinFix.task,
+    isRequired: true,
+  ),
+  //
+  // Suggested fixes
+  //
+  const DartFixInfo(
+    'double-to-int',
+    'Find double literals ending in .0 and remove the .0\n'
+        'wherever double context can be inferred.',
+    PreferIntLiteralsFix.task,
+  ),
+  //
+  // Expermimental fixes
+  //
+  const DartFixInfo(
+    'non-nullable',
+    // TODO(danrubel) update description and make default/required
+    // when NNBD fix is ready
+    'Experimental: Update sources to be non-nullable by default.\n'
+        'Requires the experimental non-nullable flag to be enabled.\n'
+        'This is not applied unless explicitly included.',
+    NonNullableFix.task,
+    isDefault: false,
+  ),
+];
+
+/// [DartFixInfo] represents a fix that can be applied by [EditDartFix].
+class DartFixInfo {
+  final String key;
+  final String description;
+  final bool isDefault;
+  final bool isRequired;
+  final void Function(DartFixRegistrar dartfix, DartFixListener listener) setup;
+
+  const DartFixInfo(this.key, this.description, this.setup,
+      {this.isDefault = true, this.isRequired = false});
+
+  DartFix asDartFix() =>
+      new DartFix(key, description: description, isRequired: isRequired);
+}
diff --git a/pkg/analysis_server/lib/src/edit/fix/dartfix_listener.dart b/pkg/analysis_server/lib/src/edit/fix/dartfix_listener.dart
new file mode 100644
index 0000000..73805d4
--- /dev/null
+++ b/pkg/analysis_server/lib/src/edit/fix/dartfix_listener.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2019, 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/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart'
+    show Location, SourceChange, SourceEdit, SourceFileEdit;
+
+/// Tasks use this API to report results.
+class DartFixListener {
+  final AnalysisServer server;
+
+  final List<DartFixSuggestion> suggestions = <DartFixSuggestion>[];
+  final List<DartFixSuggestion> otherSuggestions = <DartFixSuggestion>[];
+  final SourceChange sourceChange = new SourceChange('dartfix');
+
+  DartFixListener(this.server);
+
+  /// Record a source change to be sent to the client.
+  void addSourceChange(
+      String description, Location location, SourceChange change) {
+    suggestions.add(new DartFixSuggestion(description, location: location));
+    for (SourceFileEdit fileEdit in change.edits) {
+      for (SourceEdit sourceEdit in fileEdit.edits) {
+        sourceChange.addEdit(fileEdit.file, fileEdit.fileStamp, sourceEdit);
+      }
+    }
+  }
+
+  /// Record edits for a single source to be sent to the client.
+  void addSourceEdits(String description, Location location, Source source,
+      Iterable<SourceEdit> edits) {
+    suggestions.add(new DartFixSuggestion(description, location: location));
+    for (SourceEdit edit in edits) {
+      sourceChange.addEdit(source.fullName, -1, edit);
+    }
+  }
+
+  /// Record a source change to be sent to the client.
+  void addSourceFileEdit(
+      String description, Location location, SourceFileEdit fileEdit) {
+    suggestions.add(new DartFixSuggestion(description, location: location));
+    for (SourceEdit sourceEdit in fileEdit.edits) {
+      sourceChange.addEdit(fileEdit.file, fileEdit.fileStamp, sourceEdit);
+    }
+  }
+
+  /// Record a recommendation to be sent to the client.
+  void addRecommendation(String description, [Location location]) {
+    otherSuggestions
+        .add(new DartFixSuggestion(description, location: location));
+  }
+
+  /// Return the [Location] representing the specified offset and length
+  /// in the given compilation unit.
+  Location locationFor(ResolvedUnitResult result, int offset, int length) {
+    final locInfo = result.unit.lineInfo.getLocation(offset);
+    final location = new Location(
+        result.path, offset, length, locInfo.lineNumber, locInfo.columnNumber);
+    return location;
+  }
+}
diff --git a/pkg/analysis_server/lib/src/edit/fix/dartfix_registrar.dart b/pkg/analysis_server/lib/src/edit/fix/dartfix_registrar.dart
new file mode 100644
index 0000000..07652c0
--- /dev/null
+++ b/pkg/analysis_server/lib/src/edit/fix/dartfix_registrar.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2019, 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/edit/fix/dartfix_info.dart';
+import 'package:analysis_server/src/edit/fix/fix_code_task.dart';
+import 'package:analysis_server/src/edit/fix/fix_error_task.dart';
+import 'package:analysis_server/src/edit/fix/fix_lint_task.dart';
+import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/lint/linter.dart';
+
+/// Fixes use this API to register tasks. See [DartFixInfo.setup].
+abstract class DartFixRegistrar {
+  /// Register the specified task to analyze and fix problems.
+  void registerCodeTask(FixCodeTask task);
+
+  /// Register the specified task to fix the given error condition.
+  void registerErrorTask(ErrorCode errorCode, FixErrorTask task);
+
+  /// Register the specified task to fix the given lint.
+  void registerLintTask(LintRule ruleRegistry, FixLintTask task);
+}
diff --git a/pkg/analysis_server/lib/src/edit/fix/fix_code_task.dart b/pkg/analysis_server/lib/src/edit/fix/fix_code_task.dart
new file mode 100644
index 0000000..71d491b
--- /dev/null
+++ b/pkg/analysis_server/lib/src/edit/fix/fix_code_task.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2019, 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/edit/edit_dartfix.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+
+/// A general task for performing a fix.
+abstract class FixCodeTask {
+  /// [processUnit] is called for each compilation unit.
+  Future<void> processUnit(ResolvedUnitResult result);
+
+  /// [finish] is called after [processUnit] (and [processUnit2] if this
+  /// is a FixCodeTask2) has been called for each compilation unit.
+  Future<void> finish();
+}
+
+/// A general task for performing a fix which needs a 2nd pass.
+abstract class FixCodeTask2 extends FixCodeTask {
+  /// [processUnit2] is called for each compilation unit
+  /// after [processUnit] has been called for each compilation unit.
+  Future<void> processUnit2(ResolvedUnitResult result);
+}
+
+/// A processor used by [EditDartFix] to manage [FixCodeTask]s.
+mixin FixCodeProcessor {
+  final codeTasks = <FixCodeTask>[];
+  final codeTasks2 = <FixCodeTask2>[];
+
+  Future<void> finishCodeTasks() async {
+    for (FixCodeTask task in codeTasks) {
+      await task.finish();
+    }
+  }
+
+  bool get needsSecondPass => codeTasks2.isNotEmpty;
+
+  Future<void> processCodeTasks(ResolvedUnitResult result) async {
+    for (FixCodeTask task in codeTasks) {
+      await task.processUnit(result);
+    }
+  }
+
+  Future<void> processCodeTasks2(ResolvedUnitResult result) async {
+    for (FixCodeTask2 task in codeTasks) {
+      await task.processUnit2(result);
+    }
+  }
+
+  void registerCodeTask(FixCodeTask task) {
+    codeTasks.add(task);
+    if (task is FixCodeTask2) {
+      codeTasks2.add(task);
+    }
+  }
+}
diff --git a/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart b/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
new file mode 100644
index 0000000..e71fd1d
--- /dev/null
+++ b/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2019, 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/plugin/edit/fix/fix_core.dart';
+import 'package:analysis_server/src/edit/edit_dartfix.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_listener.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_registrar.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_internal.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/error/codes.dart';
+
+/// A task for fixing a particular error
+class FixErrorTask {
+  static void fixNamedConstructorTypeArgs(
+      DartFixRegistrar registrar, DartFixListener listener) {
+    registrar.registerErrorTask(
+        StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
+        new FixErrorTask(listener));
+  }
+
+  final DartFixListener listener;
+
+  FixErrorTask(this.listener);
+
+  Future<void> fixError(ResolvedUnitResult result, AnalysisError error) async {
+    final workspace = DartChangeWorkspace(listener.server.currentSessions);
+    final dartContext = new DartFixContextImpl(workspace, result, error);
+    final processor = new FixProcessor(dartContext);
+    Fix fix = await processor.computeFix();
+    final location = listener.locationFor(result, error.offset, error.length);
+    if (fix != null) {
+      listener.addSourceChange(fix.change.message, location, fix.change);
+    } else {
+      // TODO(danrubel): Determine why the fix could not be applied
+      // and report that in the description.
+      listener.addRecommendation('Could not fix "${error.message}"', location);
+    }
+  }
+}
+
+/// A processor used by [EditDartFix] to manage [FixErrorTask]s.
+mixin FixErrorProcessor {
+  /// A mapping from [ErrorCode] to the fix that should be applied.
+  final errorTaskMap = <ErrorCode, FixErrorTask>{};
+
+  Future<bool> processErrors(ResolvedUnitResult result) async {
+    bool foundError = false;
+    for (AnalysisError error in result.errors) {
+      final task = errorTaskMap[error.errorCode];
+      if (task != null) {
+        await task.fixError(result, error);
+      } else if (error.errorCode.type == ErrorType.SYNTACTIC_ERROR) {
+        foundError = true;
+      }
+    }
+    return foundError;
+  }
+
+  Future<bool> fixError(ResolvedUnitResult result, AnalysisError error) async {
+    return true;
+  }
+
+  void registerErrorTask(ErrorCode errorCode, FixErrorTask task) {
+    errorTaskMap[errorCode] = task;
+  }
+}
diff --git a/pkg/analysis_server/lib/src/edit/fix/fix_lint_task.dart b/pkg/analysis_server/lib/src/edit/fix/fix_lint_task.dart
new file mode 100644
index 0000000..0885046
--- /dev/null
+++ b/pkg/analysis_server/lib/src/edit/fix/fix_lint_task.dart
@@ -0,0 +1,170 @@
+// Copyright (c) 2019, 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/edit/edit_dartfix.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_listener.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/error/error.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/lint/linter.dart';
+import 'package:analyzer/src/lint/linter_visitor.dart';
+import 'package:analyzer/src/services/lint.dart';
+import 'package:front_end/src/fasta/fasta_codes.dart';
+import 'package:front_end/src/scanner/token.dart';
+import 'package:source_span/src/span.dart';
+
+/// A task for fixing a particular lint.
+/// Subclasses should implement [applyLocalFixes] and [applyRemainingFixes]
+/// and may override any of the reportSomething() methods as needed.
+abstract class FixLintTask implements ErrorReporter {
+  final DartFixListener listener;
+
+  @override
+  Source source;
+
+  FixLintTask(this.listener);
+
+  /// Apply fixes for the current compilation unit.
+  Future<void> applyLocalFixes(ResolvedUnitResult result);
+
+  /// Apply any fixes remaining after all local changes have been applied.
+  Future<void> applyRemainingFixes();
+
+  @override
+  void reportError(AnalysisError error) {
+    // ignored
+  }
+
+  @override
+  void reportErrorForElement(ErrorCode errorCode, Element element,
+      [List<Object> arguments]) {
+    // ignored
+  }
+
+  @override
+  void reportErrorForNode(ErrorCode errorCode, AstNode node,
+      [List<Object> arguments]) {
+    // ignored
+  }
+
+  @override
+  void reportErrorForOffset(ErrorCode errorCode, int offset, int length,
+      [List<Object> arguments]) {
+    // ignored
+  }
+
+  @override
+  void reportErrorForSpan(ErrorCode errorCode, SourceSpan span,
+      [List<Object> arguments]) {
+    // ignored
+  }
+
+  @override
+  void reportErrorForToken(ErrorCode errorCode, Token token,
+      [List<Object> arguments]) {
+    // ignored
+  }
+
+  @override
+  void reportErrorMessage(
+      ErrorCode errorCode, int offset, int length, Message message) {
+    // ignored
+  }
+
+  @override
+  void reportTypeErrorForNode(
+      ErrorCode errorCode, AstNode node, List<Object> arguments) {
+    // ignored
+  }
+}
+
+/// A processor used by [EditDartFix] to manage [FixLintTask]s.
+mixin FixLintProcessor {
+  final linters = <Linter>[];
+  final lintTasks = <FixLintTask>[];
+
+  Future<void> finishLints() async {
+    for (Linter linter in linters) {
+      linter.reporter = null;
+    }
+    for (FixLintTask fix in lintTasks) {
+      fix.source = null;
+      await fix.applyRemainingFixes();
+    }
+  }
+
+  Future<void> processLints(ResolvedUnitResult result) async {
+    // TODO(danrubel): Determine if a lint is configured to run as part of
+    // standard analysis and use those results if available instead of
+    // running the lint again.
+
+    Source source = result.unit.declaredElement.source;
+    for (Linter linter in linters) {
+      if (linter != null) {
+        linter.reporter.source = source;
+      }
+    }
+
+    // TODO(paulberry): use an API that provides this information more readily
+
+    var unitElement = result.unit.declaredElement;
+    var session = result.session;
+    var currentUnit = LinterContextUnit(result.content, result.unit);
+    var allUnits = <LinterContextUnit>[];
+    for (var cu in unitElement.library.units) {
+      if (identical(cu, unitElement)) {
+        allUnits.add(currentUnit);
+      } else {
+        Source source = cu.source;
+        if (source != null) {
+          var result = await session.getResolvedUnit(source.fullName);
+          allUnits.add(LinterContextUnit(result.content, result.unit));
+        }
+      }
+    }
+
+    final visitors = <AstVisitor>[];
+    final registry = new NodeLintRegistry(false);
+    var context = LinterContextImpl(
+        allUnits,
+        currentUnit,
+        session.declaredVariables,
+        result.typeProvider,
+        result.typeSystem,
+        result.session.analysisContext.analysisOptions);
+    for (Linter linter in linters) {
+      if (linter != null) {
+        final visitor = linter.getVisitor();
+        if (visitor != null) {
+          visitors.add(visitor);
+        }
+        if (linter is NodeLintRule) {
+          (linter as NodeLintRule).registerNodeProcessors(registry, context);
+        }
+      }
+    }
+
+    CompilationUnit unit = result.unit;
+    if (visitors.isNotEmpty) {
+      unit.accept(new ExceptionHandlingDelegatingAstVisitor(
+          visitors, ExceptionHandlingDelegatingAstVisitor.logException));
+    }
+    unit.accept(new LinterVisitor(
+        registry, ExceptionHandlingDelegatingAstVisitor.logException));
+
+    for (FixLintTask fix in lintTasks) {
+      await fix.applyLocalFixes(result);
+    }
+  }
+
+  void registerLintTask(LintRule lint, FixLintTask task) {
+    linters.add(lint);
+    lintTasks.add(task);
+    lint.reporter = task;
+  }
+}
diff --git a/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart b/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart
index fc623af..c80cf39 100644
--- a/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart
@@ -2,118 +2,58 @@
 // 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/edit/edit_dartfix.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_listener.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_registrar.dart';
+import 'package:analysis_server/src/edit/fix/fix_code_task.dart';
+import 'package:analysis_server/src/nullability/provisional_api.dart';
 import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/visitor.dart';
-import 'package:analyzer/src/generated/engine.dart';
-import 'package:analyzer/src/generated/source.dart';
-import 'package:analyzer_plugin/protocol/protocol_common.dart';
 
 /// [NonNullableFix] visits each named type in a resolved compilation unit
 /// and determines whether the associated variable or parameter can be null
 /// then adds or removes a '?' trailing the named type as appropriate.
-class NonNullableFix {
-  final EditDartFix dartFix;
+class NonNullableFix extends FixCodeTask2 {
+  /// TODO(paulberry): stop using permissive mode once the migration logic is
+  /// mature enough.
+  static const bool _usePermissiveMode = true;
 
-  /// The current source being "fixed"
-  Source source;
-
-  /// The source file change or `null` if none
-  SourceFileEdit fileEdit;
-
-  int firstOffset;
-  int firstLength;
-
-  NonNullableFix(this.dartFix);
-
-  void addEdit(int offset, int length, String replacementText) {
-    fileEdit ??= new SourceFileEdit(source.fullName, 0);
-    fileEdit.edits.add(new SourceEdit(offset, length, replacementText));
+  static void task(DartFixRegistrar registrar, DartFixListener listener) {
+    registrar.registerCodeTask(new NonNullableFix(listener));
   }
 
-  /// Update the source to be non-nullable by
-  /// 1) adding trailing '?' to type references of nullable variables, and
-  /// 2) removing trailing '?' from type references of non-nullable variables.
-  void applyLocalFixes(ResolvedUnitResult result) {
-    final context = result.session.analysisContext;
-    AnalysisOptionsImpl options = context.analysisOptions;
-    if (!options.experimentStatus.non_nullable) {
-      return;
-    }
+  final DartFixListener listener;
 
-    final unit = result.unit;
-    source = unit.declaredElement.source;
+  final NullabilityMigration migration;
 
-    // find and fix types
-    unit.accept(new _NonNullableTypeVisitor(this));
+  NonNullableFix(this.listener)
+      : migration = new NullabilityMigration(
+            new NullabilityMigrationAdapter(listener),
+            permissive: _usePermissiveMode);
 
-    // add source changes to the collection of fixes
-    source = null;
-    if (fileEdit != null) {
-      dartFix.addSourceFileEdit('Update non-nullable type references',
-          dartFix.locationFor(result, firstOffset, firstLength), fileEdit);
-    }
+  @override
+  Future<void> finish() async {
+    migration.finish();
+  }
+
+  @override
+  Future<void> processUnit(ResolvedUnitResult result) async {
+    migration.prepareInput(result);
+  }
+
+  @override
+  Future<void> processUnit2(ResolvedUnitResult result) async {
+    migration.processInput(result);
   }
 }
 
-class _NonNullableTypeVisitor extends RecursiveAstVisitor<void> {
-  final NonNullableFix fix;
+class NullabilityMigrationAdapter implements NullabilityMigrationListener {
+  final DartFixListener listener;
 
-  _NonNullableTypeVisitor(this.fix);
+  NullabilityMigrationAdapter(this.listener);
 
   @override
-  void visitConstructorName(ConstructorName node) {
-    // skip the type name associated with the constructor
-    node.type?.typeArguments?.accept(this);
-  }
-
-  @override
-  void visitExtendsClause(ExtendsClause node) {
-    // skip the type name associated with the extends clause
-    node.superclass?.typeArguments?.accept(this);
-  }
-
-  @override
-  void visitImplementsClause(ImplementsClause node) {
-    // skip the type names in the implements clause
-    for (TypeName typeName in node.interfaces) {
-      typeName.typeArguments?.accept(this);
-    }
-  }
-
-  @override
-  void visitOnClause(OnClause node) {
-    // skip the type name in the clause
-    for (TypeName typeName in node.superclassConstraints) {
-      typeName.typeArguments?.accept(this);
-    }
-  }
-
-  @override
-  void visitTypeName(TypeName node) {
-    // TODO(danrubel): Replace this braindead implementation
-    // with something that determines whether or not the type should be nullable
-    // and adds or removes the trailing `?` to match.
-    if (node.question == null) {
-      final identifier = node.name;
-      if (identifier is SimpleIdentifier) {
-        if (identifier.name == 'void') {
-          return;
-        }
-      }
-      fix.addEdit(node.end, 0, '?');
-      fix.firstOffset ??= node.offset;
-      fix.firstLength ??= node.length;
-    }
-    super.visitTypeName(node);
-  }
-
-  @override
-  void visitWithClause(WithClause node) {
-    // skip the type names associated with this clause
-    for (TypeName typeName in node.mixinTypes) {
-      typeName.typeArguments?.accept(this);
-    }
+  void addFix(SingleNullabilityFix fix) {
+    // TODO(danrubel): Update the description based upon the [fix.kind]
+    listener.addSourceEdits(
+        fix.kind.appliedMessage, fix.location, fix.source, fix.sourceEdits);
   }
 }
diff --git a/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart b/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart
index 871eb2b..e483d05 100644
--- a/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart
@@ -3,18 +3,28 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/plugin/edit/assist/assist_core.dart';
-import 'package:analysis_server/src/edit/edit_dartfix.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_listener.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_registrar.dart';
+import 'package:analysis_server/src/edit/fix/fix_lint_task.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:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/lint/registry.dart';
 
-class PreferIntLiteralsFix extends LinterFix {
+class PreferIntLiteralsFix extends FixLintTask {
+  static void task(DartFixRegistrar registrar, DartFixListener listener) {
+    registrar.registerLintTask(
+      Registry.ruleRegistry['prefer_int_literals'],
+      new PreferIntLiteralsFix(listener),
+    );
+  }
+
   final literalsToConvert = <DoubleLiteral>[];
 
-  PreferIntLiteralsFix(EditDartFix dartFix) : super(dartFix);
+  PreferIntLiteralsFix(DartFixListener listener) : super(listener);
 
   @override
   Future<void> applyLocalFixes(ResolvedUnitResult result) async {
@@ -22,7 +32,7 @@
       DoubleLiteral literal = literalsToConvert.removeLast();
       AssistProcessor processor = new AssistProcessor(
         new DartAssistContextImpl(
-          DartChangeWorkspace(dartFix.server.currentSessions),
+          DartChangeWorkspace(listener.server.currentSessions),
           result,
           literal.offset,
           0,
@@ -31,10 +41,10 @@
       List<Assist> assists =
           await processor.computeAssist(DartAssistKind.CONVERT_TO_INT_LITERAL);
       final location =
-          dartFix.locationFor(result, literal.offset, literal.length);
+          listener.locationFor(result, literal.offset, literal.length);
       if (assists.isNotEmpty) {
         for (Assist assist in assists) {
-          dartFix.addSourceChange(
+          listener.addSourceChange(
               'Replace a double literal with an int literal',
               location,
               assist.change);
@@ -42,7 +52,7 @@
       } else {
         // TODO(danrubel): If assists is empty, then determine why
         // assist could not be performed and report that in the description.
-        dartFix.addRecommendation(
+        listener.addRecommendation(
             'Could not replace a double literal with an int literal', location);
       }
     }
@@ -57,8 +67,7 @@
   @override
   void reportErrorForNode(ErrorCode errorCode, AstNode node,
       [List<Object> arguments]) {
-    String filePath = source.fullName;
-    if (filePath != null && dartFix.isIncluded(filePath)) {
+    if (source.fullName != null) {
       literalsToConvert.add(node);
     }
   }
diff --git a/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart b/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
index 2972f49..2fec097 100644
--- a/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
@@ -3,7 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/plugin/edit/assist/assist_core.dart';
-import 'package:analysis_server/src/edit/edit_dartfix.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_listener.dart';
+import 'package:analysis_server/src/edit/fix/dartfix_registrar.dart';
+import 'package:analysis_server/src/edit/fix/fix_lint_task.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';
@@ -11,11 +13,19 @@
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/lint/registry.dart';
 
-class PreferMixinFix extends LinterFix {
+class PreferMixinFix extends FixLintTask {
+  static void task(DartFixRegistrar registrar, DartFixListener listener) {
+    registrar.registerLintTask(
+      Registry.ruleRegistry['prefer_mixin'],
+      new PreferMixinFix(listener),
+    );
+  }
+
   final classesToConvert = new Set<Element>();
 
-  PreferMixinFix(EditDartFix dartFix) : super(dartFix);
+  PreferMixinFix(DartFixListener listener) : super(listener);
 
   @override
   Future<void> applyLocalFixes(ResolvedUnitResult result) {
@@ -32,14 +42,14 @@
 
   Future<void> convertClassToMixin(Element elem) async {
     ResolvedUnitResult result =
-        await dartFix.server.getResolvedUnit(elem.source?.fullName);
+        await listener.server.getResolvedUnit(elem.source?.fullName);
 
     for (CompilationUnitMember declaration in result.unit.declarations) {
       if (declaration is ClassOrMixinDeclaration &&
           declaration.name.name == elem.name) {
         AssistProcessor processor = new AssistProcessor(
           new DartAssistContextImpl(
-              DartChangeWorkspace(dartFix.server.currentSessions),
+              DartChangeWorkspace(listener.server.currentSessions),
               result,
               declaration.name.offset,
               0),
@@ -47,16 +57,16 @@
         List<Assist> assists = await processor
             .computeAssist(DartAssistKind.CONVERT_CLASS_TO_MIXIN);
         final location =
-            dartFix.locationFor(result, elem.nameOffset, elem.nameLength);
+            listener.locationFor(result, elem.nameOffset, elem.nameLength);
         if (assists.isNotEmpty) {
           for (Assist assist in assists) {
-            dartFix.addSourceChange('Convert ${elem.displayName} to a mixin',
+            listener.addSourceChange('Convert ${elem.displayName} to a mixin',
                 location, assist.change);
           }
         } else {
           // TODO(danrubel): If assists is empty, then determine why
           // assist could not be performed and report that in the description.
-          dartFix.addRecommendation(
+          listener.addRecommendation(
               'Could not convert ${elem.displayName} to a mixin'
               ' because the class contains a constructor',
               location);
@@ -70,8 +80,7 @@
       [List<Object> arguments]) {
     TypeName type = node;
     Element element = type.name.staticElement;
-    String filePath = element.source?.fullName;
-    if (filePath != null && dartFix.isIncluded(filePath)) {
+    if (element.source?.fullName != null) {
       classesToConvert.add(element);
     }
   }
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
index 67ab08f..a67c282 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
@@ -29,11 +29,11 @@
     }
     if (params.rootUri != null) {
       openWorkspacePaths.add(Uri.parse(params.rootUri).toFilePath());
-      // ignore: deprecated_member_use
+      // ignore: deprecated_member_use_from_same_package
     } else if (params.rootPath != null) {
       // This is deprecated according to LSP spec, but we still want to support
       // it in case older clients send us it.
-      // ignore: deprecated_member_use
+      // ignore: deprecated_member_use_from_same_package
       openWorkspacePaths.add(params.rootPath);
     }
 
@@ -92,7 +92,7 @@
         true, // referencesProvider
         true, // documentHighlightProvider
         true, // documentSymbolProvider
-        null,
+        true, // workspaceSymbolProvider
         // "The `CodeActionOptions` return type is only valid if the client
         // signals code action literal support via the property
         // `textDocument.codeAction.codeActionLiteralSupport`."
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 36eb607..7594631 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
@@ -25,6 +25,7 @@
 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_change_workspace_folders.dart';
+import 'package:analysis_server/src/lsp/handlers/handler_workspace_symbols.dart';
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 
@@ -68,6 +69,7 @@
     registerHandler(new RenameHandler(server));
     registerHandler(new FoldingHandler(server));
     registerHandler(new DiagnosticServerHandler(server));
+    registerHandler(new WorkspaceSymbolHandler(server));
   }
 }
 
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
new file mode 100644
index 0000000..3d41c0e
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
@@ -0,0 +1,106 @@
+// Copyright (c) 2019, 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 'dart:collection';
+
+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/handler_document_symbols.dart'
+    show defaultSupportedSymbolKinds;
+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/src/dart/analysis/search.dart' as search;
+
+class WorkspaceSymbolHandler
+    extends MessageHandler<WorkspaceSymbolParams, List<SymbolInformation>> {
+  WorkspaceSymbolHandler(LspAnalysisServer server) : super(server);
+  Method get handlesMessage => Method.workspace_symbol;
+
+  @override
+  WorkspaceSymbolParams convertParams(Map<String, dynamic> json) =>
+      WorkspaceSymbolParams.fromJson(json);
+
+  Future<ErrorOr<List<SymbolInformation>>> handle(
+      WorkspaceSymbolParams params) async {
+    // Respond to empty queries with an empty list. The spec says this should
+    // be non-empty, however VS Code's client sends empty requests (but then
+    // appears to not render the results we supply anyway).
+    final query = params?.query ?? '';
+    if (query == '') {
+      return success([]);
+    }
+
+    final symbolCapabilities = server?.clientCapabilities?.workspace?.symbol;
+
+    final clientSupportedSymbolKinds =
+        symbolCapabilities?.symbolKind?.valueSet != null
+            ? new HashSet<SymbolKind>.of(symbolCapabilities.symbolKind.valueSet)
+            : defaultSupportedSymbolKinds;
+
+    // Convert the string input into a case-insensitive regex that has wildcards
+    // between every character and at start/end to allow for fuzzy matching.
+    final fuzzyQuery = query.split('').map(RegExp.escape).join('.*');
+    final partialFuzzyQuery = '.*$fuzzyQuery.*';
+    final regex = new RegExp(partialFuzzyQuery, caseSensitive: false);
+
+    // Cap the number of results we'll return because short queries may match
+    // huge numbers on large projects.
+    var remainingResults = 500;
+
+    final declarations = <search.Declaration>[];
+    final filePathsHashSet = new LinkedHashSet<String>();
+    for (var driver in server.driverMap.values) {
+      final driverResults = await driver.search
+          .declarations(regex, remainingResults, filePathsHashSet);
+      declarations.addAll(driverResults);
+      remainingResults -= driverResults.length;
+    }
+
+    // Convert the file paths to something we can quickly index into since
+    // we'll be looking things up by index a lot.
+    final filePaths = filePathsHashSet.toList();
+
+    // Map the results to SymbolInformations and flatten the list of lists.
+    final symbols = declarations
+        .map((declaration) => _asSymbolInformation(
+              declaration,
+              clientSupportedSymbolKinds,
+              filePaths,
+            ))
+        .toList();
+
+    return success(symbols);
+  }
+
+  SymbolInformation _asSymbolInformation(
+    search.Declaration declaration,
+    HashSet<SymbolKind> clientSupportedSymbolKinds,
+    List<String> filePaths,
+  ) {
+    final filePath = filePaths[declaration.fileIndex];
+
+    final kind = declarationKindToSymbolKind(
+      clientSupportedSymbolKinds,
+      declaration.kind,
+    );
+    final range = toRange(
+      declaration.lineInfo,
+      declaration.codeOffset,
+      declaration.codeLength,
+    );
+    final location = new Location(
+      Uri.file(filePath).toString(),
+      range,
+    );
+
+    return new SymbolInformation(
+        declaration.name,
+        kind,
+        null, // We don't have easy access to isDeprecated here.
+        location,
+        declaration.className ?? declaration.mixinName);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index edf1327..2f9dd65 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -15,6 +15,8 @@
 import 'package:analyzer/dart/analysis/results.dart' as server;
 import 'package:analyzer/error/error.dart' as server;
 import 'package:analyzer/source/line_info.dart' as server;
+import 'package:analyzer/src/dart/analysis/search.dart' as server
+    show DeclarationKind;
 import 'package:analyzer/src/generated/source.dart' as server;
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart' as server;
 
@@ -47,6 +49,48 @@
           .toList());
 }
 
+lsp.SymbolKind declarationKindToSymbolKind(
+  HashSet<lsp.SymbolKind> clientSupportedSymbolKinds,
+  server.DeclarationKind kind,
+) {
+  bool isSupported(lsp.SymbolKind kind) =>
+      clientSupportedSymbolKinds.contains(kind);
+
+  List<lsp.SymbolKind> getKindPreferences() {
+    switch (kind) {
+      case server.DeclarationKind.CLASS:
+      case server.DeclarationKind.CLASS_TYPE_ALIAS:
+        return const [lsp.SymbolKind.Class];
+      case server.DeclarationKind.CONSTRUCTOR:
+        return const [lsp.SymbolKind.Constructor];
+      case server.DeclarationKind.ENUM:
+      case server.DeclarationKind.ENUM_CONSTANT:
+        return const [lsp.SymbolKind.Enum];
+      case server.DeclarationKind.FIELD:
+        return const [lsp.SymbolKind.Field];
+      case server.DeclarationKind.FUNCTION:
+        return const [lsp.SymbolKind.Function];
+      case server.DeclarationKind.FUNCTION_TYPE_ALIAS:
+        return const [lsp.SymbolKind.Class];
+      case server.DeclarationKind.GETTER:
+        return const [lsp.SymbolKind.Property];
+      case server.DeclarationKind.METHOD:
+        return const [lsp.SymbolKind.Method];
+      case server.DeclarationKind.MIXIN:
+        return const [lsp.SymbolKind.Class];
+      case server.DeclarationKind.SETTER:
+        return const [lsp.SymbolKind.Property];
+      case server.DeclarationKind.VARIABLE:
+        return const [lsp.SymbolKind.Variable];
+      default:
+        assert(false, 'Unexpected declaration kind $kind');
+        return null;
+    }
+  }
+
+  return getKindPreferences().firstWhere(isSupported, orElse: () => null);
+}
+
 lsp.CompletionItemKind elementKindToCompletionItemKind(
   HashSet<lsp.CompletionItemKind> clientSupportedCompletionKinds,
   server.ElementKind kind,
diff --git a/pkg/analysis_server/lib/src/nullability/conditional_discard.dart b/pkg/analysis_server/lib/src/nullability/conditional_discard.dart
new file mode 100644
index 0000000..4b47370
--- /dev/null
+++ b/pkg/analysis_server/lib/src/nullability/conditional_discard.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2019, 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/nullability/unit_propagation.dart';
+
+/// Container for information gathered during nullability migration about a
+/// conditional check that might need to be discarded.
+///
+/// This information will be associated with an Expression in the input program
+/// whose boolean value influences control flow (e.g. the condition of an `if`
+/// statement).
+class ConditionalDiscard {
+  /// Constraint variable whose value will be `true` if the code path that
+  /// results from the condition evaluating to `true` will be reachable after
+  /// nullability migration, and therefore should be kept.
+  final ConstraintVariable keepTrue;
+
+  /// Constraint variable whose value will be `false` if the code path that
+  /// results from the condition evaluating to `false` will be reachable after
+  /// nullability migration, and therefore should be kept.
+  final ConstraintVariable keepFalse;
+
+  /// Indicates whether the condition is pure (free from side effects).
+  ///
+  /// For example, a condition like `x == null` is pure (assuming `x` is a local
+  /// variable or static variable), because evaluating it has no user-visible
+  /// effect other than returning a boolean value.
+  ///
+  /// If [pureCondition] is `false`, and either [keepTrue] or [keepFalse] is
+  /// `false`, that it is safe to delete the condition expression as well as the
+  /// dead code branch (e.g. it means that `if (x == null) f(); else g();` could
+  /// be changed to simply `g();`).
+  final bool pureCondition;
+
+  ConditionalDiscard(this.keepTrue, this.keepFalse, this.pureCondition);
+}
diff --git a/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart b/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
new file mode 100644
index 0000000..ec6f294
--- /dev/null
+++ b/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
@@ -0,0 +1,548 @@
+// Copyright (c) 2019, 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/nullability/conditional_discard.dart';
+import 'package:analysis_server/src/nullability/constraint_variable_gatherer.dart';
+import 'package:analysis_server/src/nullability/decorated_type.dart';
+import 'package:analysis_server/src/nullability/expression_checks.dart';
+import 'package:analysis_server/src/nullability/transitional_api.dart';
+import 'package:analysis_server/src/nullability/unit_propagation.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/member.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:meta/meta.dart';
+
+/// Visitor that gathers nullability migration constraints from code to be
+/// migrated.
+///
+/// The return type of each `visit...` method is a [DecoratedType] indicating
+/// the static type of the visited expression, along with the constraint
+/// variables that will determine its nullability.  For `visit...` methods that
+/// don't visit expressions, `null` will be returned.
+class ConstraintGatherer extends GeneralizingAstVisitor<DecoratedType> {
+  /// The repository of constraint variables and decorated types (from a
+  /// previous pass over the source code).
+  final VariableRepository _variables;
+
+  final bool _permissive;
+
+  final NullabilityMigrationAssumptions assumptions;
+
+  /// Constraints gathered by the visitor are stored here.
+  final Constraints _constraints;
+
+  /// The file being analyzed.
+  final Source _source;
+
+  /// For convenience, a [DecoratedType] representing non-nullable `Object`.
+  final DecoratedType _notNullType;
+
+  /// For convenience, a [DecoratedType] representing non-nullable `bool`.
+  final DecoratedType _nonNullableBoolType;
+
+  /// For convenience, a [DecoratedType] representing non-nullable `Type`.
+  final DecoratedType _nonNullableTypeType;
+
+  /// For convenience, a [DecoratedType] representing `Null`.
+  final DecoratedType _nullType;
+
+  /// The [DecoratedType] of the innermost function or method being visited, or
+  /// `null` if the visitor is not inside any function or method.
+  ///
+  /// This is needed to construct the appropriate nullability constraints for
+  /// return statements.
+  DecoratedType _currentFunctionType;
+
+  /// Information about the most recently visited binary expression whose
+  /// boolean value could possibly affect nullability analysis.
+  _ConditionInfo _conditionInfo;
+
+  /// The set of constraint variables that would have to be assigned the value
+  /// of `true` for the code currently being visited to be reachable.
+  ///
+  /// Guard variables are attached to the left hand side of any generated
+  /// constraints, so that constraints do not take effect if they come from
+  /// code that can be proven unreachable by the migration tool.
+  final _guards = <ConstraintVariable>[];
+
+  /// Indicates whether the statement or expression being visited is within
+  /// conditional control flow.  If `true`, this means that the enclosing
+  /// function might complete normally without executing the current statement
+  /// or expression.
+  bool _inConditionalControlFlow = false;
+
+  ConstraintGatherer(TypeProvider typeProvider, this._variables,
+      this._constraints, this._source, this._permissive, this.assumptions)
+      : _notNullType = DecoratedType(typeProvider.objectType, null),
+        _nonNullableBoolType = DecoratedType(typeProvider.boolType, null),
+        _nonNullableTypeType = DecoratedType(typeProvider.typeType, null),
+        _nullType =
+            DecoratedType(typeProvider.nullType, ConstraintVariable.always);
+
+  /// Gets the decorated type of [element] from [_variables], performing any
+  /// necessary substitutions.
+  DecoratedType getOrComputeElementType(Element element,
+      {DecoratedType targetType}) {
+    Map<TypeParameterElement, DecoratedType> substitution;
+    Element baseElement;
+    if (element is Member) {
+      assert(targetType != null);
+      baseElement = element.baseElement;
+      var targetTypeType = targetType.type;
+      if (targetTypeType is InterfaceType &&
+          baseElement is ClassMemberElement) {
+        var enclosingClass = baseElement.enclosingElement;
+        assert(targetTypeType.element == enclosingClass); // TODO(paulberry)
+        substitution = <TypeParameterElement, DecoratedType>{};
+        assert(enclosingClass.typeParameters.length ==
+            targetTypeType.typeArguments.length); // TODO(paulberry)
+        for (int i = 0; i < enclosingClass.typeParameters.length; i++) {
+          substitution[enclosingClass.typeParameters[i]] =
+              targetType.typeArguments[i];
+        }
+      }
+    } else {
+      baseElement = element;
+    }
+    var decoratedBaseType =
+        _variables.decoratedElementType(baseElement, create: true);
+    if (substitution != null) {
+      DartType elementType;
+      if (element is MethodElement) {
+        elementType = element.type;
+      } else {
+        throw element.runtimeType; // TODO(paulberry)
+      }
+      return decoratedBaseType.substitute(
+          _constraints, substitution, elementType);
+    } else {
+      return decoratedBaseType;
+    }
+  }
+
+  @override
+  DecoratedType visitAssertStatement(AssertStatement node) {
+    _handleAssignment(_notNullType, node.condition);
+    if (identical(_conditionInfo?.condition, node.condition)) {
+      if (!_inConditionalControlFlow &&
+          _conditionInfo.trueDemonstratesNonNullIntent != null) {
+        _recordFact(_conditionInfo.trueDemonstratesNonNullIntent);
+      }
+    }
+    node.message?.accept(this);
+    return null;
+  }
+
+  @override
+  DecoratedType visitBinaryExpression(BinaryExpression node) {
+    switch (node.operator.type) {
+      case TokenType.EQ_EQ:
+      case TokenType.BANG_EQ:
+        assert(node.leftOperand is! NullLiteral); // TODO(paulberry)
+        var leftType = node.leftOperand.accept(this);
+        node.rightOperand.accept(this);
+        if (node.rightOperand is NullLiteral) {
+          // TODO(paulberry): figure out what the rules for isPure should be.
+          // TODO(paulberry): only set falseChecksNonNull in unconditional
+          // control flow
+          bool isPure = node.leftOperand is SimpleIdentifier;
+          var conditionInfo = _ConditionInfo(node,
+              isPure: isPure,
+              trueGuard: leftType.nullable,
+              falseDemonstratesNonNullIntent: leftType.nonNullIntent);
+          _conditionInfo = node.operator.type == TokenType.EQ_EQ
+              ? conditionInfo
+              : conditionInfo.not(node);
+        }
+        return _nonNullableBoolType;
+      case TokenType.PLUS:
+        _handleAssignment(_notNullType, node.leftOperand);
+        var callee = node.staticElement;
+        assert(!(callee is ClassMemberElement &&
+            callee.enclosingElement.typeParameters
+                .isNotEmpty)); // TODO(paulberry)
+        assert(callee != null); // TODO(paulberry)
+        var calleeType = getOrComputeElementType(callee);
+        // TODO(paulberry): substitute if necessary
+        assert(calleeType.positionalParameters.length > 0); // TODO(paulberry)
+        _handleAssignment(
+            calleeType.positionalParameters[0], node.rightOperand);
+        return calleeType.returnType;
+      default:
+        assert(false); // TODO(paulberry)
+        return null;
+    }
+  }
+
+  @override
+  DecoratedType visitClassDeclaration(ClassDeclaration node) {
+    node.members.accept(this);
+    return null;
+  }
+
+  @override
+  DecoratedType visitConditionalExpression(ConditionalExpression node) {
+    _handleAssignment(_notNullType, node.condition);
+    // TODO(paulberry): guard anything inside the true and false branches
+    var thenType = node.thenExpression.accept(this);
+    assert(_isSimple(thenType)); // TODO(paulberry)
+    var elseType = node.elseExpression.accept(this);
+    assert(_isSimple(elseType)); // TODO(paulberry)
+    var overallType = DecoratedType(node.staticType,
+        _joinNullabilities(node, thenType.nullable, elseType.nullable));
+    _variables.recordDecoratedExpressionType(node, overallType);
+    return overallType;
+  }
+
+  @override
+  DecoratedType visitDefaultFormalParameter(DefaultFormalParameter node) {
+    var defaultValue = node.defaultValue;
+    if (defaultValue == null) {
+      if (node.declaredElement.hasRequired) {
+        // Nothing to do; the implicit default value of `null` will never be
+        // reached.
+      } else if (node.declaredElement.isOptionalPositional ||
+          assumptions.namedNoDefaultParameterHeuristic ==
+              NamedNoDefaultParameterHeuristic.assumeNullable) {
+        _recordFact(getOrComputeElementType(node.declaredElement).nullable);
+      } else {
+        assert(assumptions.namedNoDefaultParameterHeuristic ==
+            NamedNoDefaultParameterHeuristic.assumeRequired);
+      }
+    } else {
+      _handleAssignment(
+          getOrComputeElementType(node.declaredElement), defaultValue);
+    }
+    return null;
+  }
+
+  @override
+  DecoratedType visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    _handleAssignment(_currentFunctionType.returnType, node.expression);
+    return null;
+  }
+
+  @override
+  DecoratedType visitFunctionDeclaration(FunctionDeclaration node) {
+    node.functionExpression.parameters.accept(this);
+    assert(_currentFunctionType == null);
+    _currentFunctionType =
+        _variables.decoratedElementType(node.declaredElement);
+    node.functionExpression.body.accept(this);
+    _currentFunctionType = null;
+    return null;
+  }
+
+  @override
+  DecoratedType visitIfStatement(IfStatement node) {
+    // TODO(paulberry): should the use of a boolean in an if-statement be
+    // treated like an implicit `assert(b != null)`?  Probably.
+    _handleAssignment(_notNullType, node.condition);
+    _inConditionalControlFlow = true;
+    ConstraintVariable trueGuard;
+    ConstraintVariable falseGuard;
+    if (identical(_conditionInfo?.condition, node.condition)) {
+      trueGuard = _conditionInfo.trueGuard;
+      falseGuard = _conditionInfo.falseGuard;
+      _variables.recordConditionalDiscard(
+          _source,
+          node,
+          ConditionalDiscard(trueGuard ?? ConstraintVariable.always,
+              falseGuard ?? ConstraintVariable.always, _conditionInfo.isPure));
+    }
+    if (trueGuard != null) {
+      _guards.add(trueGuard);
+    }
+    node.thenStatement.accept(this);
+    if (trueGuard != null) {
+      _guards.removeLast();
+    }
+    if (falseGuard != null) {
+      _guards.add(falseGuard);
+    }
+    node.elseStatement?.accept(this);
+    if (falseGuard != null) {
+      _guards.removeLast();
+    }
+    return null;
+  }
+
+  @override
+  DecoratedType visitIntegerLiteral(IntegerLiteral node) {
+    return DecoratedType(node.staticType, null);
+  }
+
+  @override
+  DecoratedType visitMethodDeclaration(MethodDeclaration node) {
+    node.parameters.accept(this);
+    assert(_currentFunctionType == null);
+    _currentFunctionType =
+        _variables.decoratedElementType(node.declaredElement);
+    node.body.accept(this);
+    _currentFunctionType = null;
+    return null;
+  }
+
+  @override
+  DecoratedType visitMethodInvocation(MethodInvocation node) {
+    DecoratedType targetType;
+    if (node.target != null) {
+      assert(node.operator.type == TokenType.PERIOD);
+      _checkNonObjectMember(node.methodName.name); // TODO(paulberry)
+      targetType = _handleAssignment(_notNullType, node.target);
+    }
+    var callee = node.methodName.staticElement;
+    assert(callee != null); // TODO(paulberry)
+    var calleeType = getOrComputeElementType(callee, targetType: targetType);
+    // TODO(paulberry): substitute if necessary
+    var arguments = node.argumentList.arguments;
+    int i = 0;
+    var suppliedNamedParameters = Set<String>();
+    for (var expression in arguments) {
+      if (expression is NamedExpression) {
+        var name = expression.name.label.name;
+        var parameterType = calleeType.namedParameters[name];
+        assert(parameterType != null); // TODO(paulberry)
+        _handleAssignment(parameterType, expression.expression);
+        suppliedNamedParameters.add(name);
+      } else {
+        assert(calleeType.positionalParameters.length > i); // TODO(paulberry)
+        _handleAssignment(calleeType.positionalParameters[i++], expression);
+      }
+    }
+    // Any parameters not supplied must be optional.
+    for (var entry in calleeType.namedParameterOptionalVariables.entries) {
+      assert(entry.value != null);
+      if (suppliedNamedParameters.contains(entry.key)) continue;
+      _recordFact(entry.value);
+    }
+    return calleeType.returnType;
+  }
+
+  @override
+  DecoratedType visitNode(AstNode node) {
+    if (_permissive) {
+      try {
+        return super.visitNode(node);
+      } catch (_) {
+        return null;
+      }
+    } else {
+      return super.visitNode(node);
+    }
+  }
+
+  @override
+  DecoratedType visitNullLiteral(NullLiteral node) {
+    return _nullType;
+  }
+
+  @override
+  DecoratedType visitParenthesizedExpression(ParenthesizedExpression node) {
+    return node.expression.accept(this);
+  }
+
+  @override
+  DecoratedType visitReturnStatement(ReturnStatement node) {
+    if (node.expression == null) {
+      _checkAssignment(_currentFunctionType.returnType, _nullType, null);
+    } else {
+      _handleAssignment(_currentFunctionType.returnType, node.expression);
+    }
+    return null;
+  }
+
+  @override
+  DecoratedType visitSimpleIdentifier(SimpleIdentifier node) {
+    var staticElement = node.staticElement;
+    if (staticElement is ParameterElement) {
+      return getOrComputeElementType(staticElement);
+    } else if (staticElement is ClassElement) {
+      return _nonNullableTypeType;
+    } else {
+      // TODO(paulberry)
+      throw new UnimplementedError('${staticElement.runtimeType}');
+    }
+  }
+
+  @override
+  DecoratedType visitStringLiteral(StringLiteral node) {
+    return DecoratedType(node.staticType, null);
+  }
+
+  @override
+  DecoratedType visitThisExpression(ThisExpression node) {
+    return DecoratedType(node.staticType, null);
+  }
+
+  @override
+  DecoratedType visitThrowExpression(ThrowExpression node) {
+    node.expression.accept(this);
+    // TODO(paulberry): do we need to check the expression type?  I think not.
+    return DecoratedType(node.staticType, null);
+  }
+
+  @override
+  DecoratedType visitTypeName(TypeName typeName) {
+    return DecoratedType(typeName.type, null);
+  }
+
+  /// Creates the necessary constraint(s) for an assignment from [sourceType] to
+  /// [destinationType].  [expression] is the expression whose type is
+  /// [sourceType]; it is the expression we will have to null-check in the case
+  /// where a nullable source is assigned to a non-nullable destination.
+  void _checkAssignment(DecoratedType destinationType, DecoratedType sourceType,
+      Expression expression) {
+    if (sourceType.nullable != null) {
+      if (destinationType.nullable != null) {
+        _recordConstraint(sourceType.nullable, destinationType.nullable);
+      } else {
+        assert(expression != null); // TODO(paulberry)
+        var checkNotNull = CheckExpression(expression);
+        _recordConstraint(sourceType.nullable, checkNotNull);
+        _variables.recordExpressionChecks(
+            expression, ExpressionChecks(_source, checkNotNull));
+      }
+    }
+    // TODO(paulberry): it's a cheat to pass in expression=null for the
+    // recursive checks.  Really we want to unify all the checks in a single
+    // ExpressionChecks object.
+    expression = null;
+    // TODO(paulberry): generalize this.
+    if ((_isSimple(sourceType) || destinationType.type.isObject) &&
+        _isSimple(destinationType)) {
+      // Ok; nothing further to do.
+    } else if (sourceType.type is InterfaceType &&
+        destinationType.type is InterfaceType &&
+        sourceType.type.element == destinationType.type.element) {
+      assert(sourceType.typeArguments.length ==
+          destinationType.typeArguments.length);
+      for (int i = 0; i < sourceType.typeArguments.length; i++) {
+        _checkAssignment(destinationType.typeArguments[i],
+            sourceType.typeArguments[i], expression);
+      }
+    } else if (destinationType.type.isDynamic || sourceType.type.isDynamic) {
+      // ok; nothing further to do.
+    } else {
+      throw '$destinationType <= $sourceType'; // TODO(paulberry)
+    }
+  }
+
+  /// Double checks that [name] is not the name of a method or getter declared
+  /// on [Object].
+  ///
+  /// TODO(paulberry): get rid of this method and put the correct logic into the
+  /// call sites.
+  void _checkNonObjectMember(String name) {
+    assert(name != 'toString');
+    assert(name != 'hashCode');
+    assert(name != 'noSuchMethod');
+    assert(name != 'runtimeType');
+  }
+
+  /// Creates the necessary constraint(s) for an assignment of the given
+  /// [expression] to a destination whose type is [destinationType].
+  DecoratedType _handleAssignment(
+      DecoratedType destinationType, Expression expression) {
+    var sourceType = expression.accept(this);
+    _checkAssignment(destinationType, sourceType, expression);
+    return sourceType;
+  }
+
+  /// Double checks that [type] is sufficiently simple for this naive prototype
+  /// implementation.
+  ///
+  /// TODO(paulberry): get rid of this method and put the correct logic into the
+  /// call sites.
+  bool _isSimple(DecoratedType type) {
+    if (type.type.isBottom) return true;
+    if (type.type.isVoid) return true;
+    if (type.type is! InterfaceType) return false;
+    if ((type.type as InterfaceType).typeParameters.isNotEmpty) return false;
+    return true;
+  }
+
+  /// Creates a constraint variable (if necessary) representing the nullability
+  /// of [node], which is the disjunction of the nullabilities [a] and [b].
+  ConstraintVariable _joinNullabilities(
+      ConditionalExpression node, ConstraintVariable a, ConstraintVariable b) {
+    if (a == null) return b;
+    if (b == null) return a;
+    if (identical(a, ConstraintVariable.always) ||
+        identical(b, ConstraintVariable.always)) {
+      return ConstraintVariable.always;
+    }
+    var result = TypeIsNullable(node.offset);
+    _recordConstraint(a, result);
+    _recordConstraint(b, result);
+    _recordConstraint(result, ConstraintVariable.or(_constraints, a, b));
+    return result;
+  }
+
+  /// Records a constraint having [condition] as its left hand side and
+  /// [consequence] as its right hand side.  Any [_guards] are included in the
+  /// left hand side.
+  void _recordConstraint(
+      ConstraintVariable condition, ConstraintVariable consequence) {
+    _guards.add(condition);
+    _recordFact(consequence);
+    _guards.removeLast();
+  }
+
+  /// Records a constraint having [consequence] as its right hand side.  Any
+  /// [_guards] are used as the right hand side.
+  void _recordFact(ConstraintVariable consequence) {
+    _constraints.record(_guards, consequence);
+  }
+}
+
+/// Information about a binary expression whose boolean value could possibly
+/// affect nullability analysis.
+class _ConditionInfo {
+  /// The [expression] of interest.
+  final Expression condition;
+
+  /// Indicates whether [condition] is pure (free from side effects).
+  ///
+  /// For example, a condition like `x == null` is pure (assuming `x` is a local
+  /// variable or static variable), because evaluating it has no user-visible
+  /// effect other than returning a boolean value.
+  final bool isPure;
+
+  /// If not `null`, the [ConstraintVariable] whose value must be `true` in
+  /// order for [condition] to evaluate to `true`.
+  final ConstraintVariable trueGuard;
+
+  /// If not `null`, the [ConstraintVariable] whose value must be `true` in
+  /// order for [condition] to evaluate to `false`.
+  final ConstraintVariable falseGuard;
+
+  /// If not `null`, the [ConstraintVariable] whose value should be set to
+  /// `true` if [condition] is asserted to be `true`.
+  final ConstraintVariable trueDemonstratesNonNullIntent;
+
+  /// If not `null`, the [ConstraintVariable] whose value should be set to
+  /// `true` if [condition] is asserted to be `false`.
+  final ConstraintVariable falseDemonstratesNonNullIntent;
+
+  _ConditionInfo(this.condition,
+      {@required this.isPure,
+      this.trueGuard,
+      this.falseGuard,
+      this.trueDemonstratesNonNullIntent,
+      this.falseDemonstratesNonNullIntent});
+
+  /// Returns a new [_ConditionInfo] describing the boolean "not" of `this`.
+  _ConditionInfo not(Expression condition) => _ConditionInfo(condition,
+      isPure: isPure,
+      trueGuard: falseGuard,
+      falseGuard: trueGuard,
+      trueDemonstratesNonNullIntent: falseDemonstratesNonNullIntent,
+      falseDemonstratesNonNullIntent: trueDemonstratesNonNullIntent);
+}
diff --git a/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart b/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart
new file mode 100644
index 0000000..d3a89538
--- /dev/null
+++ b/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart
@@ -0,0 +1,233 @@
+// Copyright (c) 2019, 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/nullability/conditional_discard.dart';
+import 'package:analysis_server/src/nullability/decorated_type.dart';
+import 'package:analysis_server/src/nullability/expression_checks.dart';
+import 'package:analysis_server/src/nullability/transitional_api.dart';
+import 'package:analysis_server/src/nullability/unit_propagation.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/generated/source.dart';
+
+/// Visitor that gathers constraint variables for nullability migration from
+/// code to be migrated.
+///
+/// The return type of each `visit...` method is a [DecoratedType] indicating
+/// the static type of the element declared by the visited node, along with the
+/// constraint variables that will determine its nullability.  For `visit...`
+/// methods that don't visit declarations, `null` will be returned.
+class ConstraintVariableGatherer extends GeneralizingAstVisitor<DecoratedType> {
+  /// Constraint variables and decorated types are stored here.
+  final VariableRecorder _variables;
+
+  /// The file being analyzed.
+  final Source _source;
+
+  /// If the parameters of a function or method are being visited, the
+  /// [DecoratedType] of the corresponding function or method type.
+  ///
+  /// TODO(paulberry): should this be updated when we visit generic function
+  /// type syntax?  How about when we visit old-style function-typed formal
+  /// parameters?
+  DecoratedType _currentFunctionType;
+
+  final bool _permissive;
+
+  final NullabilityMigrationAssumptions assumptions;
+
+  ConstraintVariableGatherer(
+      this._variables, this._source, this._permissive, this.assumptions);
+
+  /// Creates and stores a [DecoratedType] object corresponding to the given
+  /// [type] AST, and returns it.
+  DecoratedType decorateType(TypeAnnotation type) {
+    return type == null
+        // TODO(danrubel): Return something other than this
+        // to indicate that we should insert a type for the declaration
+        // that is missing a type reference.
+        ? new DecoratedType(DynamicTypeImpl.instance, ConstraintVariable.always)
+        : type.accept(this);
+  }
+
+  @override
+  DecoratedType visitDefaultFormalParameter(DefaultFormalParameter node) {
+    var decoratedType = node.parameter.accept(this);
+    ConstraintVariable optional;
+    if (node.declaredElement.hasRequired) {
+      optional = null;
+    } else if (node.defaultValue != null) {
+      optional = ConstraintVariable.always;
+    } else {
+      optional = decoratedType.nullable;
+      _variables.recordPossiblyOptional(_source, node, optional);
+    }
+    if (optional != null) {
+      _currentFunctionType
+              .namedParameterOptionalVariables[node.declaredElement.name] =
+          optional;
+    }
+    return null;
+  }
+
+  @override
+  DecoratedType visitFormalParameter(FormalParameter node) {
+    // Do not visit children
+    // TODO(paulberry): handle all types of formal parameters
+    // - NormalFormalParameter
+    // - SimpleFormalParameter
+    // - FieldFormalParameter
+    // - FunctionTypedFormalParameter
+    // - DefaultFormalParameter
+    return null;
+  }
+
+  @override
+  DecoratedType visitFunctionDeclaration(FunctionDeclaration node) {
+    _handleExecutableDeclaration(node.declaredElement, node.returnType,
+        node.functionExpression.parameters);
+    return null;
+  }
+
+  @override
+  DecoratedType visitMethodDeclaration(MethodDeclaration node) {
+    _handleExecutableDeclaration(
+        node.declaredElement, node.returnType, node.parameters);
+    return null;
+  }
+
+  @override
+  DecoratedType visitNode(AstNode node) {
+    if (_permissive) {
+      try {
+        return super.visitNode(node);
+      } catch (_) {
+        return null;
+      }
+    } else {
+      return super.visitNode(node);
+    }
+  }
+
+  @override
+  DecoratedType visitSimpleFormalParameter(SimpleFormalParameter node) {
+    var type = decorateType(node.type);
+    var declaredElement = node.declaredElement;
+    assert(type.nonNullIntent == null);
+    type.nonNullIntent = NonNullIntent(node.offset);
+    _variables.recordDecoratedElementType(declaredElement, type);
+    if (declaredElement.isNamed) {
+      _currentFunctionType.namedParameters[declaredElement.name] = type;
+    } else {
+      _currentFunctionType.positionalParameters.add(type);
+    }
+    return type;
+  }
+
+  @override
+  DecoratedType visitTypeAnnotation(TypeAnnotation node) {
+    assert(node != null); // TODO(paulberry)
+    assert(node is NamedType); // TODO(paulberry)
+    var type = node.type;
+    if (type.isVoid) return DecoratedType(type, ConstraintVariable.always);
+    assert(
+        type is InterfaceType || type is TypeParameterType); // TODO(paulberry)
+    var typeArguments = const <DecoratedType>[];
+    if (type is InterfaceType && type.typeParameters.isNotEmpty) {
+      if (node is TypeName) {
+        assert(node.typeArguments != null);
+        typeArguments =
+            node.typeArguments.arguments.map((t) => t.accept(this)).toList();
+      } else {
+        assert(false); // TODO(paulberry): is this possible?
+      }
+    }
+    var nullable = node.question == null
+        ? TypeIsNullable(node.end)
+        : ConstraintVariable.always;
+    var decoratedType = DecoratedTypeAnnotation(
+        type, nullable, _source, node.end,
+        typeArguments: typeArguments);
+    _variables.recordDecoratedTypeAnnotation(node, decoratedType);
+    return decoratedType;
+  }
+
+  @override
+  DecoratedType visitTypeName(TypeName node) => visitTypeAnnotation(node);
+
+  /// Common handling of function and method declarations.
+  void _handleExecutableDeclaration(ExecutableElement declaredElement,
+      TypeAnnotation returnType, FormalParameterList parameters) {
+    var decoratedReturnType = decorateType(returnType);
+    var previousFunctionType = _currentFunctionType;
+    // TODO(paulberry): test that it's correct to use `null` for the nullability
+    // of the function type
+    var functionType = DecoratedType(declaredElement.type, null,
+        returnType: decoratedReturnType,
+        positionalParameters: [],
+        namedParameters: {},
+        namedParameterOptionalVariables: {});
+    _currentFunctionType = functionType;
+    parameters.accept(this);
+    _currentFunctionType = previousFunctionType;
+    _variables.recordDecoratedElementType(declaredElement, functionType);
+  }
+}
+
+/// Repository of constraint variables and decorated types corresponding to the
+/// code being migrated.
+///
+/// This data structure records the results of the first pass of migration
+/// ([ConstraintVariableGatherer], which finds all the variables that need to be
+/// constrained).
+abstract class VariableRecorder {
+  /// Associates decorated type information with the given [element].
+  void recordDecoratedElementType(Element element, DecoratedType type);
+
+  /// Associates decorated type information with the given [type] node.
+  void recordDecoratedTypeAnnotation(
+      TypeAnnotation node, DecoratedTypeAnnotation type);
+
+  /// Associates a constraint variable with the question of whether the given
+  /// named parameter should be optional (should not have a `required`
+  /// annotation added to it).
+  void recordPossiblyOptional(Source source, DefaultFormalParameter parameter,
+      ConstraintVariable variable);
+}
+
+/// Repository of constraint variables and decorated types corresponding to the
+/// code being migrated.
+///
+/// This data structure allows the second pass of migration
+/// ([ConstraintGatherer], which builds all the constraints) to access the
+/// results of the first ([ConstraintVariableGatherer], which finds all the
+/// variables that need to be constrained).
+abstract class VariableRepository {
+  /// Retrieves the [DecoratedType] associated with the static type of the given
+  /// [element].
+  ///
+  /// If [create] is `true`, and no decorated type is found for the given
+  /// element, one is synthesized using [DecoratedType.forElement].
+  DecoratedType decoratedElementType(Element element, {bool create: false});
+
+  /// Records conditional discard information for the given AST node (which is
+  /// an `if` statement or a conditional (`?:`) expression).
+  void recordConditionalDiscard(
+      Source source, AstNode node, ConditionalDiscard conditionalDiscard);
+
+  /// Associates decorated type information with the given [element].
+  ///
+  /// TODO(paulberry): why is this in both [VariableRecorder] and
+  /// [VariableRepository]?
+  void recordDecoratedElementType(Element element, DecoratedType type);
+
+  /// Associates decorated type information with the given expression [node].
+  void recordDecoratedExpressionType(Expression node, DecoratedType type);
+
+  /// Associates a set of nullability checks with the given expression [node].
+  void recordExpressionChecks(Expression expression, ExpressionChecks checks);
+}
diff --git a/pkg/analysis_server/lib/src/nullability/decorated_type.dart b/pkg/analysis_server/lib/src/nullability/decorated_type.dart
new file mode 100644
index 0000000..53c023b
--- /dev/null
+++ b/pkg/analysis_server/lib/src/nullability/decorated_type.dart
@@ -0,0 +1,220 @@
+// Copyright (c) 2019, 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/nullability/transitional_api.dart';
+import 'package:analysis_server/src/nullability/unit_propagation.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart' show SourceEdit;
+
+/// Representation of a type in the code to be migrated.  In addition to
+/// tracking the (unmigrated) [DartType], we track the [ConstraintVariable]s
+/// indicating whether the type, and the types that compose it, are nullable.
+class DecoratedType {
+  final DartType type;
+
+  /// [ConstraintVariable] whose value will be set to `true` if this type needs
+  /// to be nullable.
+  ///
+  /// If `null`, that means that an external constraint (outside the code being
+  /// migrated) forces this type to be non-nullable.
+  final ConstraintVariable nullable;
+
+  /// [ConstraintVariable] whose value will be set to `true` if the usage of
+  /// this type suggests that it is intended to be non-null (because of the
+  /// presence of a statement or expression that would unconditionally lead to
+  /// an exception being thrown in the case of a `null` value at runtime).
+  ConstraintVariable nonNullIntent;
+
+  /// If `this` is a function type, the [DecoratedType] of its return type.
+  final DecoratedType returnType;
+
+  /// If `this` is a function type, the [DecoratedType] of each of its
+  /// positional parameters (including both required and optional positional
+  /// parameters).
+  final List<DecoratedType> positionalParameters;
+
+  /// If `this` is a function type, the [DecoratedType] of each of its named
+  /// parameters.
+  final Map<String, DecoratedType> namedParameters;
+
+  /// If `this` is a function type, [ConstraintVariable] for each of its named
+  /// parameters indicating whether the given named parameter needs to be
+  /// optional (no `required` annotation).
+  ///
+  /// If there is no entry in this map corresponding to a given named parameter,
+  /// that means that it has already been decided (prior to migration) that the
+  /// given named parameter is required.  TODO(paulberry): test that this works
+  /// for already-migrated code.
+  final Map<String, ConstraintVariable> namedParameterOptionalVariables;
+
+  /// If `this` is a parameterized type, the [DecoratedType] of each of its
+  /// type parameters.
+  ///
+  /// TODO(paulberry): how should we handle generic typedefs?
+  final List<DecoratedType> typeArguments;
+
+  DecoratedType(this.type, this.nullable,
+      {this.returnType,
+      this.positionalParameters = const [],
+      this.namedParameters = const {},
+      this.namedParameterOptionalVariables = const {},
+      this.typeArguments = const []}) {
+    // The type system doesn't have a non-nullable version of `dynamic`.  So if
+    // the type is `dynamic`, verify that `nullable` is `always`.
+    assert(!type.isDynamic || identical(nullable, ConstraintVariable.always));
+  }
+
+  /// Creates a [DecoratedType] corresponding to the given [element], which is
+  /// presumed to have come from code that is already migrated.
+  factory DecoratedType.forElement(Element element) {
+    DecoratedType decorate(DartType type) {
+      assert((type as TypeImpl).nullability ==
+          Nullability.indeterminate); // TODO(paulberry)
+      if (type is FunctionType) {
+        var decoratedType = DecoratedType(type, null,
+            returnType: decorate(type.returnType), positionalParameters: []);
+        for (var parameter in type.parameters) {
+          assert(parameter.isPositional); // TODO(paulberry)
+          decoratedType.positionalParameters.add(decorate(parameter.type));
+        }
+        return decoratedType;
+      } else if (type is InterfaceType) {
+        assert(type.typeParameters.isEmpty); // TODO(paulberry)
+        return DecoratedType(type, null);
+      } else {
+        throw type.runtimeType; // TODO(paulberry)
+      }
+    }
+
+    DecoratedType decoratedType;
+    if (element is MethodElement) {
+      decoratedType = decorate(element.type);
+    } else {
+      throw element.runtimeType; // TODO(paulberry)
+    }
+    return decoratedType;
+  }
+
+  /// Apply the given [substitution] to this type.
+  ///
+  /// [undecoratedResult] is the result of the substitution, as determined by
+  /// the normal type system.
+  DecoratedType substitute(
+      Constraints constraints,
+      Map<TypeParameterElement, DecoratedType> substitution,
+      DartType undecoratedResult) {
+    if (substitution.isEmpty) return this;
+    return _substitute(constraints, substitution, undecoratedResult);
+  }
+
+  @override
+  String toString() {
+    var trailing = nullable == null ? '' : '?($nullable)';
+    var type = this.type;
+    if (type is TypeParameterType || type is VoidType) {
+      return '$type$trailing';
+    } else if (type is InterfaceType) {
+      var name = type.element.name;
+      var args = '';
+      if (type.typeArguments.isNotEmpty) {
+        args = '<${type.typeArguments.join(', ')}>';
+      }
+      return '$name$args$trailing';
+    } else if (type is FunctionType) {
+      assert(type.typeFormals.isEmpty); // TODO(paulberry)
+      assert(type.namedParameterTypes.isEmpty &&
+          namedParameters.isEmpty); // TODO(paulberry)
+      var args = positionalParameters.map((p) => p.toString()).join(', ');
+      return '$returnType Function($args)$trailing';
+    } else if (type is DynamicTypeImpl) {
+      return 'dynamic';
+    } else {
+      throw '$type'; // TODO(paulberry)
+    }
+  }
+
+  /// Internal implementation of [_substitute], used as a recursion target.
+  DecoratedType _substitute(
+      Constraints constraints,
+      Map<TypeParameterElement, DecoratedType> substitution,
+      DartType undecoratedResult) {
+    var type = this.type;
+    if (type is FunctionType && undecoratedResult is FunctionType) {
+      assert(type.typeFormals.isEmpty); // TODO(paulberry)
+      var newPositionalParameters = <DecoratedType>[];
+      for (int i = 0; i < positionalParameters.length; i++) {
+        var numRequiredParameters =
+            undecoratedResult.normalParameterTypes.length;
+        var undecoratedParameterType = i < numRequiredParameters
+            ? undecoratedResult.normalParameterTypes[i]
+            : undecoratedResult
+                .optionalParameterTypes[i - numRequiredParameters];
+        newPositionalParameters.add(positionalParameters[i]
+            ._substitute(constraints, substitution, undecoratedParameterType));
+      }
+      return DecoratedType(undecoratedResult, nullable,
+          returnType: returnType._substitute(
+              constraints, substitution, undecoratedResult.returnType),
+          positionalParameters: newPositionalParameters);
+    } else if (type is TypeParameterType) {
+      var inner = substitution[type.element];
+      return DecoratedType(undecoratedResult,
+          ConstraintVariable.or(constraints, inner?.nullable, nullable));
+    } else if (type is VoidType) {
+      return this;
+    }
+    throw '$type.substitute($substitution)'; // TODO(paulberry)
+  }
+}
+
+/// A [DecoratedType] based on a type annotation appearing explicitly in the
+/// source code.
+///
+/// This class implements [PotentialModification] because it knows how to update
+/// the source code to reflect its nullability.
+class DecoratedTypeAnnotation extends DecoratedType
+    implements PotentialModification {
+  @override
+  final Source source;
+
+  final int _offset;
+
+  DecoratedTypeAnnotation(
+      DartType type, ConstraintVariable nullable, this.source, this._offset,
+      {List<DecoratedType> typeArguments = const []})
+      : super(type, nullable, typeArguments: typeArguments);
+
+  @override
+  bool get isEmpty =>
+      identical(nullable, ConstraintVariable.always) || !nullable.value;
+
+  @override
+  Iterable<SourceEdit> get modifications =>
+      isEmpty ? [] : [SourceEdit(_offset, 0, '?')];
+}
+
+/// Type of a [ConstraintVariable] representing the fact that a type is intended
+/// to be non-null.
+class NonNullIntent extends ConstraintVariable {
+  final int _offset;
+
+  NonNullIntent(this._offset);
+
+  @override
+  toString() => 'nonNullIntent($_offset)';
+}
+
+/// Type of a [ConstraintVariable] representing the fact that a type is
+/// nullable.
+class TypeIsNullable extends ConstraintVariable {
+  final int _offset;
+
+  TypeIsNullable(this._offset);
+
+  @override
+  toString() => 'nullable($_offset)';
+}
diff --git a/pkg/analysis_server/lib/src/nullability/decorated_type_operations.dart b/pkg/analysis_server/lib/src/nullability/decorated_type_operations.dart
new file mode 100644
index 0000000..59164ce
--- /dev/null
+++ b/pkg/analysis_server/lib/src/nullability/decorated_type_operations.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2019, 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/nullability/constraint_variable_gatherer.dart';
+import 'package:analysis_server/src/nullability/decorated_type.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type_system.dart';
+import 'package:analyzer/src/dart/resolver/flow_analysis.dart';
+
+/// [TypeOperations] that works with [DecoratedType]s.
+class DecoratedTypeOperations implements TypeOperations<DecoratedType> {
+  final TypeSystem _typeSystem;
+  final VariableRepository _variableRepository;
+
+  DecoratedTypeOperations(this._typeSystem, this._variableRepository);
+
+  @override
+  DecoratedType elementType(VariableElement element) {
+    return _variableRepository.decoratedElementType(element);
+  }
+
+  @override
+  bool isSubtypeOf(DecoratedType leftType, DecoratedType rightType) {
+    return _typeSystem.isSubtypeOf(leftType.type, rightType.type);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/nullability/expression_checks.dart b/pkg/analysis_server/lib/src/nullability/expression_checks.dart
new file mode 100644
index 0000000..3c4af10
--- /dev/null
+++ b/pkg/analysis_server/lib/src/nullability/expression_checks.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2019, 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/nullability/transitional_api.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+
+/// Container for information gathered during nullability migration about the
+/// set of runtime checks that might need to be performed on the value of an
+/// expression.
+///
+/// TODO(paulberry): the only check we support now is [nullCheck], which checks
+/// that the expression is not null.  We need to add other checks, e.g. to check
+/// that a List<int?> is actually a List<int>.
+class ExpressionChecks extends PotentialModification {
+  @override
+  final Source source;
+
+  /// Constraint variable whose value will be `true` if this expression requires
+  /// a null check.
+  final CheckExpression nullCheck;
+
+  ExpressionChecks(this.source, this.nullCheck);
+
+  @override
+  bool get isEmpty => !nullCheck.value;
+
+  @override
+  Iterable<SourceEdit> get modifications =>
+      nullCheck.value ? [SourceEdit(nullCheck.offset, 0, '!')] : [];
+}
diff --git a/pkg/analysis_server/lib/src/nullability/provisional_api.dart b/pkg/analysis_server/lib/src/nullability/provisional_api.dart
new file mode 100644
index 0000000..6352b4d
--- /dev/null
+++ b/pkg/analysis_server/lib/src/nullability/provisional_api.dart
@@ -0,0 +1,159 @@
+// Copyright (c) 2019, 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/nullability/decorated_type.dart'
+    as analyzer;
+import 'package:analysis_server/src/nullability/expression_checks.dart'
+    as analyzer;
+import 'package:analysis_server/src/nullability/transitional_api.dart'
+    as analyzer;
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:meta/meta.dart';
+
+export 'package:analysis_server/src/nullability/transitional_api.dart'
+    show NamedNoDefaultParameterHeuristic, NullabilityMigrationAssumptions;
+
+/// Kinds of fixes that might be performed by nullability migration.
+class NullabilityFixKind {
+  /// A formal parameter needs to have a required annotation added.
+  static const addRequired =
+      const NullabilityFixKind._(appliedMessage: 'Add a required annotation');
+
+  /// An expression's value needs to be null-checked.
+  static const checkExpression = const NullabilityFixKind._(
+    appliedMessage: 'Added a null check to an expression',
+  );
+
+  /// An explicit type mentioned in the source program needs to be made
+  /// nullable.
+  static const makeTypeNullable = const NullabilityFixKind._(
+    appliedMessage: 'Changed a type to be nullable',
+  );
+
+  /// An if-test or conditional expression needs to have its "then" branch
+  /// discarded.
+  static const discardThen = const NullabilityFixKind._(
+    appliedMessage: 'Discarded an unreachable conditional then branch',
+  );
+
+  /// An if-test or conditional expression needs to have its "else" branch
+  /// discarded.
+  static const discardElse = const NullabilityFixKind._(
+    appliedMessage: 'Discarded an unreachable conditional else branch',
+  );
+
+  /// A message used by dartfix to indicate a fix has been applied.
+  final String appliedMessage;
+
+  const NullabilityFixKind._({@required this.appliedMessage});
+}
+
+/// Provisional API for DartFix to perform nullability migration.
+///
+/// Usage: pass each input source file to [prepareInput].  Then pass each input
+/// source file to [processInput].  Then call [finish] to obtain the
+/// modifications that need to be made to each source file.
+///
+/// TODO(paulberry): figure out whether this API is what we want, and figure out
+/// what file/folder it belongs in.
+class NullabilityMigration {
+  final analyzer.NullabilityMigration _analyzerMigration;
+  final NullabilityMigrationListener listener;
+
+  /// Prepares to perform nullability migration.
+  ///
+  /// If [permissive] is `true`, exception handling logic will try to proceed
+  /// as far as possible even though the migration algorithm is not yet
+  /// complete.  TODO(paulberry): remove this mode once the migration algorithm
+  /// is fully implemented.
+  NullabilityMigration(this.listener,
+      {bool permissive: false,
+      analyzer.NullabilityMigrationAssumptions assumptions:
+          const analyzer.NullabilityMigrationAssumptions()})
+      : _analyzerMigration = analyzer.NullabilityMigration(
+            permissive: permissive, assumptions: assumptions);
+
+  void finish() {
+    _analyzerMigration.finish().forEach((pm) {
+      listener.addFix(_SingleNullabilityFix(pm));
+    });
+  }
+
+  void prepareInput(ResolvedUnitResult result) {
+    _analyzerMigration.prepareInput(result.unit);
+  }
+
+  void processInput(ResolvedUnitResult result) {
+    _analyzerMigration.processInput(result.unit, result.typeProvider);
+  }
+}
+
+/// [NullabilityMigrationListener] is used by [NullabilityMigration]
+/// to communicate source changes or "fixes" to the client.
+abstract class NullabilityMigrationListener {
+  /// [addFix] is called once for each source change.
+  void addFix(SingleNullabilityFix fix);
+}
+
+/// Representation of a single conceptual change made by the nullability
+/// migration algorithm.  This change might require multiple source edits to
+/// achieve.
+abstract class SingleNullabilityFix {
+  /// What kind of fix this is.
+  NullabilityFixKind get kind;
+
+  /// Location of the change, for reporting to the user.
+  Location get location;
+
+  /// File to change.
+  Source get source;
+
+  /// Individual source edits to achieve the change.  May be returned in any
+  /// order.
+  Iterable<SourceEdit> get sourceEdits;
+}
+
+/// Implementation of [SingleNullabilityFix] used internally by
+/// [NullabilityMigration].
+class _SingleNullabilityFix extends SingleNullabilityFix {
+  @override
+  final List<SourceEdit> sourceEdits;
+
+  @override
+  final Source source;
+
+  @override
+  final NullabilityFixKind kind;
+
+  factory _SingleNullabilityFix(
+      analyzer.PotentialModification potentialModification) {
+    // TODO(paulberry): once everything is migrated into the analysis server,
+    // the migration engine can just create SingleNullabilityFix objects
+    // directly and set their kind appropriately; we won't need to translate the
+    // kinds using a bunch of `is` checks.
+    NullabilityFixKind kind;
+    if (potentialModification is analyzer.ExpressionChecks) {
+      kind = NullabilityFixKind.checkExpression;
+    } else if (potentialModification is analyzer.DecoratedTypeAnnotation) {
+      kind = NullabilityFixKind.makeTypeNullable;
+    } else if (potentialModification is analyzer.ConditionalModification) {
+      kind = potentialModification.discard.keepFalse.value
+          ? NullabilityFixKind.discardThen
+          : NullabilityFixKind.discardElse;
+    } else if (potentialModification is analyzer.PotentiallyAddRequired) {
+      kind = NullabilityFixKind.addRequired;
+    } else {
+      throw new UnimplementedError('TODO(paulberry)');
+    }
+    return _SingleNullabilityFix._(potentialModification.modifications.toList(),
+        potentialModification.source, kind);
+  }
+
+  _SingleNullabilityFix._(this.sourceEdits, this.source, this.kind);
+
+  /// TODO(paulberry): do something better
+  Location get location => null;
+}
diff --git a/pkg/analysis_server/lib/src/nullability/transitional_api.dart b/pkg/analysis_server/lib/src/nullability/transitional_api.dart
new file mode 100644
index 0000000..4f0c81a
--- /dev/null
+++ b/pkg/analysis_server/lib/src/nullability/transitional_api.dart
@@ -0,0 +1,310 @@
+// Copyright (c) 2019, 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/nullability/conditional_discard.dart';
+import 'package:analysis_server/src/nullability/constraint_gatherer.dart';
+import 'package:analysis_server/src/nullability/constraint_variable_gatherer.dart';
+import 'package:analysis_server/src/nullability/decorated_type.dart';
+import 'package:analysis_server/src/nullability/expression_checks.dart';
+import 'package:analysis_server/src/nullability/unit_propagation.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart' show SourceEdit;
+
+/// Type of a [ConstraintVariable] representing the addition of a null check.
+class CheckExpression extends ConstraintVariable {
+  final int offset;
+
+  CheckExpression(Expression expression) : offset = expression.end;
+
+  @override
+  toString() => 'checkNotNull($offset)';
+}
+
+/// Records information about how a conditional expression or statement might
+/// need to be modified.
+class ConditionalModification extends PotentialModification {
+  final int offset;
+
+  final int end;
+
+  final bool isStatement;
+
+  final ConditionalDiscard discard;
+
+  final _KeepNode condition;
+
+  final _KeepNode thenStatement;
+
+  final _KeepNode elseStatement;
+
+  @override
+  final Source source;
+
+  factory ConditionalModification(
+      Source source, AstNode node, ConditionalDiscard discard) {
+    if (node is IfStatement) {
+      return ConditionalModification._(
+          node.offset,
+          node.end,
+          node is Statement,
+          discard,
+          _KeepNode(node.condition),
+          _KeepNode(node.thenStatement),
+          _KeepNode(node.elseStatement),
+          source);
+    } else {
+      throw new UnimplementedError('TODO(paulberry)');
+    }
+  }
+
+  ConditionalModification._(
+      this.offset,
+      this.end,
+      this.isStatement,
+      this.discard,
+      this.condition,
+      this.thenStatement,
+      this.elseStatement,
+      this.source);
+
+  @override
+  bool get isEmpty => discard.keepTrue.value && discard.keepFalse.value;
+
+  @override
+  Iterable<SourceEdit> get modifications {
+    if (isEmpty) return const [];
+    // TODO(paulberry): move the following logic into DartEditBuilder (see
+    // dartbug.com/35872).
+    var result = <SourceEdit>[];
+    var keepNodes = <_KeepNode>[];
+    if (!discard.pureCondition) {
+      keepNodes.add(condition); // TODO(paulberry): test
+    }
+    if (discard.keepTrue.value) {
+      keepNodes.add(thenStatement); // TODO(paulberry): test
+    }
+    if (discard.keepFalse.value) {
+      keepNodes.add(elseStatement); // TODO(paulberry): test
+    }
+    // TODO(paulberry): test thoroughly
+    for (int i = 0; i < keepNodes.length; i++) {
+      var keepNode = keepNodes[i];
+      if (i == 0 && keepNode.offset != offset) {
+        result.add(SourceEdit(offset, 0, '/* '));
+      }
+      if (i != 0 || keepNode.offset != offset) {
+        result.add(SourceEdit(keepNode.offset, 0, '*/ '));
+      }
+      if (i != keepNodes.length - 1 || keepNode.end != end) {
+        result.add(SourceEdit(keepNode.end, 0,
+            keepNode.isExpression && isStatement ? '; /*' : ' /*'));
+      }
+      if (i == keepNodes.length - 1 && keepNode.end != end) {
+        result.add(SourceEdit(end, 0, ' */'));
+      }
+    }
+    return result;
+  }
+}
+
+/// Enum encapsulating the various options proposed at
+/// https://github.com/dart-lang/language/issues/156#issuecomment-460525075
+enum DefaultParameterHandling {
+  /// Option 2: Add required named parameters
+  ///
+  /// - `{int x}` implicitly means `x` is required
+  ///   - required-ness goes into the function type:
+  ///     `int Function({required int x})`
+  /// - `{required int? x}` is allowed
+  ///   - means that something must be passed
+  ///   - passing null is allowed
+  /// - `{int x = 3}` is allowed
+  ///   - `x` is optional
+  ///   - passing null to it is an error
+  ///   - passing nothing to it results in it getting the default value
+  /// - `[int x]` is an error
+  /// - `[int x = 3]` is allowed
+  option2_addRequiredNamedParameters,
+}
+
+/// Enum representing the possible heuristics for handling named parameters with
+/// no default value.
+enum NamedNoDefaultParameterHeuristic {
+  /// Assume that the parameter should be considered nullable, unless the user
+  /// has explicitly marked it as `@required`.
+  assumeNullable,
+
+  /// Assume that the parameter should be considered required, unless the user
+  /// has explicitly marked it as nullable.
+  assumeRequired,
+}
+
+/// Transitional migration API.
+///
+/// Usage: pass each input source file to [prepareInput].  Then pass each input
+/// source file to [processInput].  Then call [finish] to obtain the
+/// modifications that need to be made to each source file.
+///
+/// TODO(paulberry): this implementation keeps a lot of CompilationUnit objects
+/// around.  Can we do better?
+class NullabilityMigration {
+  final bool _permissive;
+
+  final NullabilityMigrationAssumptions assumptions;
+
+  final _variables = Variables();
+
+  final _constraints = Solver();
+
+  /// Prepares to perform nullability migration.
+  ///
+  /// If [permissive] is `true`, exception handling logic will try to proceed
+  /// as far as possible even though the migration algorithm is not yet
+  /// complete.  TODO(paulberry): remove this mode once the migration algorithm
+  /// is fully implemented.
+  NullabilityMigration(
+      {bool permissive: false,
+      this.assumptions: const NullabilityMigrationAssumptions()})
+      : _permissive = permissive;
+
+  List<PotentialModification> finish() {
+    _constraints.applyHeuristics();
+    return _variables.getPotentialModifications();
+  }
+
+  void prepareInput(CompilationUnit unit) {
+    unit.accept(ConstraintVariableGatherer(
+        _variables, unit.declaredElement.source, _permissive, assumptions));
+  }
+
+  void processInput(CompilationUnit unit, TypeProvider typeProvider) {
+    unit.accept(ConstraintGatherer(typeProvider, _variables, _constraints,
+        unit.declaredElement.source, _permissive, assumptions));
+  }
+}
+
+/// Assumptions affecting the behavior of the nullability migration tool.
+///
+/// These options generally reflect design decisions that have not yet been
+/// made.  They don't reflect behavioral differences we would want to expose to
+/// the user.
+///
+/// TODO(paulberry): hardcode these assumptions once decisions have been made.
+class NullabilityMigrationAssumptions {
+  /// Handling of default parameters.
+  final DefaultParameterHandling defaultParameterHandling;
+
+  /// Heuristic for handling named parameters with no default value.
+  final NamedNoDefaultParameterHeuristic namedNoDefaultParameterHeuristic;
+
+  const NullabilityMigrationAssumptions(
+      {this.defaultParameterHandling:
+          DefaultParameterHandling.option2_addRequiredNamedParameters,
+      this.namedNoDefaultParameterHeuristic:
+          NamedNoDefaultParameterHeuristic.assumeRequired});
+}
+
+/// Records information about the possible addition of a `@required` annotation
+/// to the source code.
+class PotentiallyAddRequired extends PotentialModification {
+  @override
+  final Source source;
+
+  final ConstraintVariable _optionalVariable;
+
+  final int _offset;
+
+  PotentiallyAddRequired(
+      this.source, DefaultFormalParameter parameter, this._optionalVariable)
+      : _offset = parameter.offset;
+
+  @override
+  bool get isEmpty => _optionalVariable.value;
+
+  @override
+  Iterable<SourceEdit> get modifications =>
+      isEmpty ? const [] : [SourceEdit(_offset, 0, '@required ')];
+}
+
+/// Interface used by data structures representing potential modifications to
+/// the code being migrated.
+abstract class PotentialModification {
+  bool get isEmpty;
+
+  /// Gets the individual migrations that need to be done, considering the
+  /// solution to the constraint equations.
+  Iterable<SourceEdit> get modifications;
+
+  Source get source;
+}
+
+class Variables implements VariableRecorder, VariableRepository {
+  final _decoratedElementTypes = <Element, DecoratedType>{};
+
+  final _potentialModifications = <PotentialModification>[];
+
+  @override
+  DecoratedType decoratedElementType(Element element, {bool create: false}) =>
+      _decoratedElementTypes[element] ??= create
+          ? DecoratedType.forElement(element)
+          : throw StateError('No element found');
+
+  List<PotentialModification> getPotentialModifications() =>
+      _potentialModifications.where((m) => !m.isEmpty).toList();
+
+  @override
+  void recordConditionalDiscard(
+      Source source, AstNode node, ConditionalDiscard conditionalDiscard) {
+    _potentialModifications
+        .add(ConditionalModification(source, node, conditionalDiscard));
+  }
+
+  void recordDecoratedElementType(Element element, DecoratedType type) {
+    _decoratedElementTypes[element] = type;
+  }
+
+  void recordDecoratedExpressionType(Expression node, DecoratedType type) {}
+
+  void recordDecoratedTypeAnnotation(
+      TypeAnnotation node, DecoratedTypeAnnotation type) {
+    _potentialModifications.add(type);
+  }
+
+  @override
+  void recordExpressionChecks(Expression expression, ExpressionChecks checks) {
+    _potentialModifications.add(checks);
+  }
+
+  @override
+  void recordPossiblyOptional(Source source, DefaultFormalParameter parameter,
+      ConstraintVariable variable) {
+    _potentialModifications
+        .add(PotentiallyAddRequired(source, parameter, variable));
+  }
+}
+
+/// Helper object used by [ConditionalModification] to keep track of AST nodes
+/// within the conditional expression.
+class _KeepNode {
+  final int offset;
+
+  final int end;
+
+  final bool isExpression;
+
+  factory _KeepNode(AstNode node) {
+    int offset = node.offset;
+    int end = node.end;
+    if (node is Block && node.statements.isNotEmpty) {
+      offset = node.statements.beginToken.offset;
+      end = node.statements.endToken.end;
+    }
+    return _KeepNode._(offset, end, node is Expression);
+  }
+
+  _KeepNode._(this.offset, this.end, this.isExpression);
+}
diff --git a/pkg/analysis_server/lib/src/nullability/unit_propagation.dart b/pkg/analysis_server/lib/src/nullability/unit_propagation.dart
new file mode 100644
index 0000000..1add712
--- /dev/null
+++ b/pkg/analysis_server/lib/src/nullability/unit_propagation.dart
@@ -0,0 +1,187 @@
+// Copyright (c) 2019, 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.
+
+/// Repository of constraints corresponding to the code being migrated.
+///
+/// This data structure carries information from the second pass of migration
+/// ([ConstraintGatherer]) to the third (which creates individual code
+/// modifications from each constraint).
+abstract class Constraints {
+  /// Records a new constraint equation.
+  void record(
+      Iterable<ConstraintVariable> conditions, ConstraintVariable consequence);
+}
+
+/// Representation of a single boolean variable in the constraint solver.
+class ConstraintVariable {
+  /// A special boolean variable whose value is known to be `true`.
+  static final ConstraintVariable always = _Always();
+
+  /// A list of all constraints containing this variable on their left hand side
+  /// that may not have been satisfied yet.
+  final _dependencies = <_Clause>[];
+
+  /// The value assigned to this constraint variable by the solution currently
+  /// being computed.
+  bool _value = false;
+
+  /// If this variable represents a disjunction ("or") of several other
+  /// variables, the variables in the disjunction.  Otherwise a singleton list
+  /// containing `this`.
+  final List<ConstraintVariable> _disjunctionParts;
+
+  ConstraintVariable() : _disjunctionParts = List.filled(1, null) {
+    _disjunctionParts[0] = this;
+  }
+
+  /// Creates a [ConstraintVariable] representing a disjunction ("or") of
+  /// several other variables.
+  ///
+  /// Additional constraints will be recorded in [constraints] to ensure that
+  /// the solution will be consistent.
+  factory ConstraintVariable.or(
+      Constraints constraints, ConstraintVariable a, ConstraintVariable b) {
+    if (a == null) return b;
+    if (b == null) return a;
+    var parts = a.disjunctionParts.toList();
+    parts.addAll(b.disjunctionParts);
+    assert(parts.length > 1);
+    var result = ConstraintVariable._(parts);
+    constraints.record([a], result);
+    constraints.record([b], result);
+    return result;
+  }
+
+  ConstraintVariable._(this._disjunctionParts);
+
+  /// If this variable represents a disjunction ("or") of several other
+  /// variables, the variables in the disjunction.  Otherwise a singleton list
+  /// containing `this`.
+  Iterable<ConstraintVariable> get disjunctionParts => _disjunctionParts;
+
+  /// Indicates whether this variable represents a disjunction ("or") of several
+  /// other variables.
+  bool get isDisjunction => _disjunctionParts.length > 1;
+
+  /// The value assigned to this constraint variable by the solution currently
+  /// being computed.
+  get value => _value;
+
+  @override
+  String toString() =>
+      isDisjunction ? '(${_disjunctionParts.join(' | ')})' : super.toString();
+}
+
+/// The core of the migration tool's constraint solver.  This class implements
+/// unit propagation (see https://en.wikipedia.org/wiki/Unit_propagation),
+/// extended to support disjunctions.
+///
+/// The extension works approximately as follows: first we perform ordinary unit
+/// propagation, accumulating a list of any disjunction variables that need to
+/// be assigned a value of `true`.  Once this finishes, we heuristically choose
+/// one of these disjunction variables and ensure that it is assigned a value of
+/// `true` by setting one of its constituent variables to `true` and propagating
+/// again.  Once all disjunctions have been resolved, we have a final solution.
+class Solver extends Constraints {
+  /// Clauses that should be evaluated as part of ordinary unit propagation.
+  final _pending = <_Clause>[];
+
+  /// Disjunction variables that have been determined by unit propagation to be
+  /// `true`, but for which we have not yet propagated the `true` value to one
+  /// of the constituent variables.
+  final _pendingDisjunctions = <ConstraintVariable>[];
+
+  /// Heuristically resolves any pending disjunctions.
+  void applyHeuristics() {
+    while (_pendingDisjunctions.isNotEmpty) {
+      var disjunction = _pendingDisjunctions.removeLast();
+      if (disjunction.disjunctionParts.any((v) => v.value)) continue;
+      // TODO(paulberry): smarter heuristics
+      var choice = disjunction.disjunctionParts.first;
+      record([], choice);
+    }
+  }
+
+  @override
+  void record(Iterable<ConstraintVariable> conditions,
+      covariant ConstraintVariable consequence) {
+    var _conditions = List<ConstraintVariable>.from(conditions);
+    var clause = _Clause(_conditions, consequence);
+    int i = 0;
+    while (i < _conditions.length) {
+      ConstraintVariable variable = _conditions[i];
+      if (variable._value) {
+        int j = _conditions.length - 1;
+        _conditions[i] = _conditions[j];
+        _conditions.removeLast();
+        continue;
+      }
+      variable._dependencies.add(clause);
+      i++;
+    }
+    if (i == 0) {
+      if (!consequence._value) {
+        consequence._value = true;
+        if (consequence.isDisjunction) {
+          _pendingDisjunctions.add(consequence);
+        }
+        _pending.addAll(consequence._dependencies);
+        _propagate();
+      }
+    } else {
+      consequence._dependencies.add(clause);
+    }
+  }
+
+  /// Performs ordinary unit propagation, recording any disjunctions encountered
+  /// in [_pendingDisjunctions].
+  void _propagate() {
+    while (_pending.isNotEmpty) {
+      var clause = _pending.removeLast();
+      var conditions = clause.conditions;
+      int i = 0;
+      while (i < conditions.length) {
+        ConstraintVariable variable = conditions[i];
+        if (variable._value) {
+          int j = conditions.length - 1;
+          conditions[i] = conditions[j];
+          conditions.removeLast();
+          continue;
+        }
+        i++;
+      }
+      if (i == 0) {
+        var consequence = clause.consequence;
+        if (!consequence._value) {
+          consequence._value = true;
+          if (consequence.isDisjunction) {
+            _pendingDisjunctions.add(consequence);
+          }
+          _pending.addAll(consequence._dependencies);
+        }
+      }
+    }
+  }
+}
+
+/// The special singleton [ConstraintVariable] whose value is always `true`.
+class _Always extends ConstraintVariable {
+  _Always() {
+    _value = true;
+  }
+
+  @override
+  String toString() => 'always';
+}
+
+/// A single equation in a system of constraints.
+class _Clause {
+  /// The conditions on the left hand side of the equation.
+  final List<ConstraintVariable> conditions;
+
+  /// The single variable on the right hand side of the equation.
+  final ConstraintVariable consequence;
+
+  _Clause(this.conditions, this.consequence);
+}
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 85c9fa6..4c4b692 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
@@ -8,11 +8,11 @@
     hide Element, ElementKind;
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/utilities.dart';
-import 'package:analysis_server/src/utilities/documentation.dart';
 import 'package:analysis_server/src/utilities/flutter.dart' as flutter;
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/util/comment.dart';
 
 /**
  * Determine the number of arguments.
@@ -32,52 +32,6 @@
 }
 
 /**
- * If the containing [node] is an argument list
- * or named expression in an argument list
- * then return the simple identifier for the method, constructor, or annotation
- * to which the argument list is associated
- */
-SimpleIdentifier _getTargetId(AstNode node) {
-  if (node is NamedExpression) {
-    return _getTargetId(node.parent);
-  }
-  if (node is ArgumentList) {
-    AstNode parent = node.parent;
-    if (parent is MethodInvocation) {
-      return parent.methodName;
-    }
-    if (parent is InstanceCreationExpression) {
-      ConstructorName constructorName = parent.constructorName;
-      if (constructorName != null) {
-        if (constructorName.name != null) {
-          return constructorName.name;
-        }
-        Identifier typeName = constructorName.type.name;
-        if (typeName is SimpleIdentifier) {
-          return typeName;
-        }
-        if (typeName is PrefixedIdentifier) {
-          return typeName.identifier;
-        }
-      }
-    }
-    if (parent is Annotation) {
-      SimpleIdentifier name = parent.constructorName;
-      if (name == null) {
-        Identifier parentName = parent.name;
-        if (parentName is SimpleIdentifier) {
-          return parentName;
-        } else if (parentName is PrefixedIdentifier) {
-          return parentName.identifier;
-        }
-      }
-      return name;
-    }
-  }
-  return null;
-}
-
-/**
  * Determine if the completion target is at the end of the list of arguments.
  */
 bool _isAppendingToArgList(DartCompletionRequest request) {
@@ -193,35 +147,13 @@
     this.request = request;
     this.suggestions = <CompletionSuggestion>[];
 
-    // Determine if the target is in an argument list
-    // for a method or a constructor or an annotation
-    SimpleIdentifier targetId = _getTargetId(request.target.containingNode);
-    if (targetId == null) {
-      return const <CompletionSuggestion>[];
-    }
-    Element elem = targetId.staticElement;
-    if (elem == null) {
+    var executable = request.target.executableElement;
+    if (executable == null) {
       return const <CompletionSuggestion>[];
     }
 
-    // Generate argument list suggestion based upon the type of element
-    if (elem is ClassElement) {
-      _addSuggestions(elem.unnamedConstructor?.parameters);
-      return suggestions;
-    }
-    if (elem is ConstructorElement) {
-      _addSuggestions(elem.parameters);
-      return suggestions;
-    }
-    if (elem is FunctionElement) {
-      _addSuggestions(elem.parameters);
-      return suggestions;
-    }
-    if (elem is MethodElement) {
-      _addSuggestions(elem.parameters);
-      return suggestions;
-    }
-    return const <CompletionSuggestion>[];
+    _addSuggestions(executable.parameters);
+    return suggestions;
   }
 
   void _addDefaultParamSuggestions(Iterable<ParameterElement> parameters,
@@ -342,7 +274,7 @@
   static void _setDocumentation(
       CompletionSuggestion suggestion, String comment) {
     if (comment != null) {
-      String doc = removeDartDocDelimiters(comment);
+      String doc = getDartDocPlainText(comment);
       suggestion.docComplete = doc;
       suggestion.docSummary = getDartDocSummary(doc);
     }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index 0989365..7661185 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/provisional/completion/completion_core.dart'
     show AbortCompletion, CompletionContributor, CompletionRequest;
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
@@ -32,13 +33,13 @@
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
 import 'package:analyzer/src/generated/engine.dart' hide AnalysisResult;
 import 'package:analyzer/src/generated/source.dart';
-import 'package:analyzer/src/task/api/model.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol;
 import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart';
@@ -55,6 +56,22 @@
    */
   static DartContributionSorter contributionSorter = new CommonUsageSorter();
 
+  /// If not `null`, then instead of using [ImportedReferenceContributor],
+  /// fill this set with kinds of elements that are applicable at the
+  /// completion location, so should be suggested from available suggestion
+  /// sets.
+  final Set<protocol.ElementKind> includedSuggestionKinds;
+
+  /// If [includedSuggestionKinds] is not null, must be also not `null`, and
+  /// will be filled with tags for suggestions that should be given higher
+  /// relevance than other included suggestions.
+  final List<IncludedSuggestionRelevanceTag> includedSuggestionRelevanceTags;
+
+  DartCompletionManager({
+    this.includedSuggestionKinds,
+    this.includedSuggestionRelevanceTags,
+  });
+
   @override
   Future<List<CompletionSuggestion>> computeSuggestions(
       CompletionRequest request) async {
@@ -88,7 +105,6 @@
       new ArgListContributor(),
       new CombinatorContributor(),
       new FieldFormalContributor(),
-      new ImportedReferenceContributor(),
       new InheritedReferenceContributor(),
       new KeywordContributor(),
       new LabelContributor(),
@@ -104,6 +120,14 @@
       new UriContributor(),
       new VariableNameContributor()
     ];
+
+    if (includedSuggestionKinds != null) {
+      _addIncludedSuggestionKinds(dartRequest);
+      _addIncludedSuggestionRelevanceTags(dartRequest);
+    } else {
+      contributors.add(new ImportedReferenceContributor());
+    }
+
     try {
       for (DartCompletionContributor contributor in contributors) {
         String contributorTag =
@@ -153,6 +177,79 @@
     return suggestions;
   }
 
+  void _addIncludedSuggestionKinds(DartCompletionRequestImpl request) {
+    var opType = request.opType;
+
+    if (!opType.includeIdentifiers) return;
+
+    var kinds = includedSuggestionKinds;
+    if (kinds != null) {
+      if (opType.includeTypeNameSuggestions) {
+        kinds.add(protocol.ElementKind.CLASS);
+        kinds.add(protocol.ElementKind.CLASS_TYPE_ALIAS);
+        kinds.add(protocol.ElementKind.ENUM);
+        kinds.add(protocol.ElementKind.FUNCTION_TYPE_ALIAS);
+        kinds.add(protocol.ElementKind.MIXIN);
+      }
+      if (opType.includeReturnValueSuggestions) {
+        kinds.add(protocol.ElementKind.ENUM_CONSTANT);
+        kinds.add(protocol.ElementKind.FUNCTION);
+        kinds.add(protocol.ElementKind.TOP_LEVEL_VARIABLE);
+      }
+    }
+  }
+
+  void _addIncludedSuggestionRelevanceTags(DartCompletionRequestImpl request) {
+    var target = request.target;
+
+    void addTypeTag(DartType type) {
+      if (type is InterfaceType) {
+        var element = type.element;
+        var tag = '${element.librarySource.uri}::${element.name}';
+        if (element.isEnum) {
+          includedSuggestionRelevanceTags.add(
+            IncludedSuggestionRelevanceTag(
+              tag,
+              DART_RELEVANCE_BOOST_AVAILABLE_ENUM,
+            ),
+          );
+        } else {
+          includedSuggestionRelevanceTags.add(
+            IncludedSuggestionRelevanceTag(
+              tag,
+              DART_RELEVANCE_BOOST_AVAILABLE_DECLARATION,
+            ),
+          );
+        }
+      }
+    }
+
+    var parameter = target.parameterElement;
+    if (parameter != null) {
+      addTypeTag(parameter.type);
+    }
+
+    var containingNode = target.containingNode;
+
+    if (containingNode is AssignmentExpression &&
+        containingNode.operator.type == TokenType.EQ &&
+        target.offset >= containingNode.operator.end) {
+      addTypeTag(containingNode.leftHandSide.staticType);
+    }
+
+    if (containingNode is ListLiteral &&
+        target.offset >= containingNode.leftBracket.end &&
+        target.offset <= containingNode.rightBracket.offset) {
+      var type = containingNode.staticType;
+      if (type is InterfaceType) {
+        var typeArguments = type.typeArguments;
+        if (typeArguments.isNotEmpty) {
+          addTypeTag(typeArguments[0]);
+        }
+      }
+    }
+  }
+
   static List<String> _ensureList(Map<String, List<String>> map, String key) {
     List<String> list = map[key];
     if (list == null) {
@@ -303,8 +400,7 @@
    * based on the given [request]. This method will throw [AbortCompletion]
    * if the completion request has been aborted.
    */
-  static Future<DartCompletionRequest> from(CompletionRequest request,
-      {ResultDescriptor resultDescriptor}) async {
+  static Future<DartCompletionRequest> from(CompletionRequest request) async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
     request.checkAborted();
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
index c335b79..b1d8459 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
@@ -11,12 +11,12 @@
     show DartCompletionRequestImpl;
 import 'package:analysis_server/src/services/completion/dart/utilities.dart';
 import 'package:analysis_server/src/services/correction/strings.dart';
-import 'package:analysis_server/src/utilities/documentation.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/standard_resolution_map.dart';
 import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/util/comment.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol
     show Element, ElementKind;
 import 'package:analyzer_plugin/src/utilities/completion/optype.dart';
@@ -317,14 +317,30 @@
     }
   }
 
+  @override
+  void declaredTypeParameter(TypeParameter node) {
+    if (optype.includeTypeNameSuggestions) {
+      _addLocalSuggestion(
+        null,
+        node.name,
+        null,
+        protocol.ElementKind.TYPE_PARAMETER,
+        isDeprecated: isDeprecated(node),
+        kind: CompletionSuggestionKind.IDENTIFIER,
+        relevance: DART_RELEVANCE_TYPE_PARAMETER,
+      );
+    }
+  }
+
   void _addLocalSuggestion(Comment documentationComment, SimpleIdentifier id,
       TypeAnnotation typeName, protocol.ElementKind elemKind,
       {bool isAbstract: false,
       bool isDeprecated: false,
       ClassOrMixinDeclaration classDecl,
+      CompletionSuggestionKind kind,
       FormalParameterList param,
       int relevance: DART_RELEVANCE_DEFAULT}) {
-    CompletionSuggestionKind kind = targetIsFunctionalArgument
+    kind ??= targetIsFunctionalArgument
         ? CompletionSuggestionKind.IDENTIFIER
         : optype.suggestKind;
     CompletionSuggestion suggestion = createLocalSuggestion(
@@ -526,7 +542,7 @@
           .map((Token t) => t.toString())
           .join('\n')
           .replaceAll('\r\n', '\n');
-      String doc = removeDartDocDelimiters(text);
+      String doc = getDartDocPlainText(text);
       suggestion.docComplete = doc;
       suggestion.docSummary = getDartDocSummary(doc);
     }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
index 22692c1..e2d567d 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
@@ -83,7 +83,13 @@
       });
     });
 
-    String replacement = builder.sourceChange.edits[0].edits[0].replacement;
+    var fileEdits = builder.sourceChange.edits;
+    if (fileEdits.length != 1) return null;
+
+    var sourceEdits = fileEdits[0].edits;
+    if (sourceEdits.length != 1) return null;
+
+    String replacement = sourceEdits[0].replacement;
     String completion = replacement.trim();
     String overrideAnnotation = '@override';
     if (_hasOverride(request.target.containingNode) &&
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
index 5f66d87..c7186e2 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
@@ -7,11 +7,11 @@
     hide Element, ElementKind;
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/utilities.dart';
-import 'package:analysis_server/src/utilities/documentation.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/dart/element/visitor.dart';
 import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/util/comment.dart';
 import 'package:path/path.dart' as path;
 
 /**
@@ -46,7 +46,7 @@
       false);
 
   // Attach docs.
-  String doc = removeDartDocDelimiters(element.documentationComment);
+  String doc = getDartDocPlainText(element.documentationComment);
   suggestion.docComplete = doc;
   suggestion.docSummary = getDartDocSummary(doc);
 
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
index f3974ce..63faf71 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
@@ -423,6 +423,7 @@
       if (nextType.superclass != null) {
         typesToVisit.add(nextType.superclass);
       }
+      typesToVisit.addAll(nextType.superclassConstraints);
       typesToVisit.addAll(nextType.mixins);
     }
     return result;
diff --git a/pkg/analysis_server/lib/src/services/correction/assist.dart b/pkg/analysis_server/lib/src/services/correction/assist.dart
index 9f3595e..7bc89e0 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist.dart
@@ -32,7 +32,11 @@
  */
 class DartAssistKind {
   static const ADD_TYPE_ANNOTATION = const AssistKind(
-      'dart.assist.addTypeAnnotation', 30, "Add type annotation");
+      'dart.assist.addTypeAnnotation', 30, "Add type annotation",
+      associatedErrorCodes: <String>[
+        'always_specify_types',
+        'type_annotate_public_apis'
+      ]);
   static const ASSIGN_TO_LOCAL_VARIABLE = const AssistKind(
       'dart.assist.assignToVariable', 30, "Assign value to new local variable");
   static const CONVERT_CLASS_TO_MIXIN = const AssistKind(
@@ -44,15 +48,18 @@
   static const CONVERT_DOCUMENTATION_INTO_LINE = const AssistKind(
       'dart.assist.convert.lineComment',
       30,
-      "Convert to line documentation comment");
+      "Convert to line documentation comment",
+      associatedErrorCodes: <String>['slash_for_doc_comments']);
   static const CONVERT_INTO_ASYNC_BODY = const AssistKind(
       'dart.assist.convert.bodyToAsync', 30, "Convert to async function body");
   static const CONVERT_INTO_BLOCK_BODY = const AssistKind(
       'dart.assist.convert.bodyToBlock', 30, "Convert to block body");
   static const CONVERT_INTO_EXPRESSION_BODY = const AssistKind(
-      'dart.assist.convert.bodyToExpression', 30, "Convert to expression body");
+      'dart.assist.convert.bodyToExpression', 30, "Convert to expression body",
+      associatedErrorCodes: <String>['prefer_expression_function_bodies']);
   static const CONVERT_INTO_FINAL_FIELD = const AssistKind(
-      'dart.assist.convert.getterToFinalField', 30, "Convert to final field");
+      'dart.assist.convert.getterToFinalField', 30, "Convert to final field",
+      associatedErrorCodes: <String>['prefer_final_fields']);
   static const CONVERT_INTO_FOR_INDEX = const AssistKind(
       'dart.assist.convert.forEachToForIndex', 30, "Convert to for-index loop");
   static const CONVERT_INTO_GENERIC_FUNCTION_SYNTAX = const AssistKind(
@@ -64,7 +71,8 @@
   static const CONVERT_INTO_IS_NOT =
       const AssistKind('dart.assist.convert.isNot', 30, "Convert to is!");
   static const CONVERT_INTO_IS_NOT_EMPTY = const AssistKind(
-      'dart.assist.convert.isNotEmpty', 30, "Convert to 'isNotEmpty'");
+      'dart.assist.convert.isNotEmpty', 30, "Convert to 'isNotEmpty'",
+      associatedErrorCodes: <String>['prefer_is_not_empty']);
   static const CONVERT_PART_OF_TO_URI = const AssistKind(
       'dart.assist.convert.partOfToPartUri', 30, "Convert to use a URI");
   static const CONVERT_TO_DOUBLE_QUOTED_STRING = const AssistKind(
@@ -75,8 +83,19 @@
       'dart.assist.convert.toConstructorFieldParameter',
       30,
       "Convert to field formal parameter");
+  static const CONVERT_TO_IF_ELEMENT = const AssistKind(
+      'dart.assist.convertToIfElement', 30, "Convert to an 'if' element");
   static const CONVERT_TO_INT_LITERAL = const AssistKind(
-      'dart.assist.convert.toIntLiteral', 30, "Convert to an int literal");
+      'dart.assist.convert.toIntLiteral', 30, "Convert to an int literal",
+      associatedErrorCodes: <String>['prefer_int_literals']);
+  static const CONVERT_TO_LIST_LITERAL = const AssistKind(
+      'dart.assist.convert.toListLiteral', 30, "Convert to list literal",
+      // todo (brianwilkerson): unify w/ fix
+      associatedErrorCodes: <String>['prefer_collection_literals']);
+  static const CONVERT_TO_MAP_LITERAL = const AssistKind(
+      'dart.assist.convert.toMapLiteral', 30, "Convert to map literal",
+      // todo (brianwilkerson): unify w/ fix
+      associatedErrorCodes: <String>['prefer_collection_literals']);
   static const CONVERT_TO_MULTILINE_STRING = const AssistKind(
       'dart.assist.convert.toMultilineString',
       30,
@@ -85,10 +104,17 @@
       'dart.assist.convert.toConstructorNormalParameter',
       30,
       "Convert to normal parameter");
+  static const CONVERT_TO_SET_LITERAL = const AssistKind(
+      'dart.assist.convert.toSetLiteral', 30, "Convert to set literal",
+      // todo (brianwilkerson): unify w/ fix
+      associatedErrorCodes: <String>['prefer_collection_literals']);
   static const CONVERT_TO_SINGLE_QUOTED_STRING = const AssistKind(
       'dart.assist.convert.toSingleQuotedString',
       30,
-      "Convert to single quoted string");
+      "Convert to single quoted string",
+      associatedErrorCodes: <String>['prefer_single_quotes']);
+  static const CONVERT_TO_SPREAD = const AssistKind(
+      'dart.assist.convertToSpread', 30, "Convert to a spread");
   static const ENCAPSULATE_FIELD =
       const AssistKind('dart.assist.encapsulateField', 30, "Encapsulate field");
   static const EXCHANGE_OPERANDS =
@@ -144,7 +170,11 @@
   static const JOIN_VARIABLE_DECLARATION = const AssistKind(
       'dart.assist.joinVariableDeclaration', 30, "Join variable declaration");
   static const REMOVE_TYPE_ANNOTATION = const AssistKind(
-      'dart.assist.removeTypeAnnotation', 29, "Remove type annotation");
+      'dart.assist.removeTypeAnnotation', 29, "Remove type annotation",
+      associatedErrorCodes: <String>[
+        'avoid_return_types_on_setters',
+        'type_init_formals'
+      ]);
   static const REPLACE_CONDITIONAL_WITH_IF_ELSE = const AssistKind(
       'dart.assist.convert.conditionalToIfElse',
       30,
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 001180e..18b210c 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -20,9 +20,11 @@
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/dart/analysis/session_helper.dart';
 import 'package:analyzer/src/dart/ast/token.dart';
 import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
 import 'package:analyzer/src/generated/java_core.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/source.dart';
@@ -71,6 +73,13 @@
    */
   String get eol => utils.endOfLine;
 
+  /**
+   * Return the status of the known experiments.
+   */
+  ExperimentStatus get experimentStatus =>
+      (session.analysisContext.analysisOptions as AnalysisOptionsImpl)
+          .experimentStatus;
+
   Future<List<Assist>> compute() async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
@@ -83,22 +92,26 @@
     await _addProposal_addTypeAnnotation_VariableDeclaration();
     await _addProposal_assignToLocalVariable();
     await _addProposal_convertClassToMixin();
-    await _addProposal_convertIntoFinalField();
-    await _addProposal_convertIntoGetter();
     await _addProposal_convertDocumentationIntoBlock();
     await _addProposal_convertDocumentationIntoLine();
+    await _addProposal_convertIntoFinalField();
+    await _addProposal_convertIntoGetter();
+    await _addProposal_convertListConstructorToListLiteral();
+    await _addProposal_convertListToSetToSetLiteral();
+    await _addProposal_convertMapConstructorToMapLiteral();
+    await _addProposal_convertPartOfToUri();
+    await _addProposal_convertSetConstructorToSetLiteral();
     await _addProposal_convertToAsyncFunctionBody();
     await _addProposal_convertToBlockFunctionBody();
     await _addProposal_convertToDoubleQuotedString();
     await _addProposal_convertToExpressionFunctionBody();
-    await _addProposal_convertPartOfToUri();
+    await _addProposal_convertToFieldParameter();
     await _addProposal_convertToForIndexLoop();
     await _addProposal_convertToGenericFunctionSyntax();
     await _addProposal_convertToIntLiteral();
     await _addProposal_convertToIsNot_onIs();
     await _addProposal_convertToIsNot_onNot();
     await _addProposal_convertToIsNotEmpty();
-    await _addProposal_convertToFieldParameter();
     await _addProposal_convertToMultilineString();
     await _addProposal_convertToNormalParameter();
     await _addProposal_convertToSingleQuotedString();
@@ -130,6 +143,14 @@
     await _addProposal_splitVariableDeclaration();
     await _addProposal_surroundWith();
 
+    if (experimentStatus.control_flow_collections) {
+      await _addProposal_convertConditionalExpressionToIfElement();
+      await _addProposal_convertMapFromIterableToIfLiteral();
+    }
+    if (experimentStatus.spread_collections) {
+      await _addProposal_convertAddAllToSpread();
+    }
+
     return assists;
   }
 
@@ -403,6 +424,63 @@
     }
   }
 
+  Future<void> _addProposal_convertAddAllToSpread() async {
+    AstNode node = this.node;
+    if (node is! SimpleIdentifier || node.parent is! MethodInvocation) {
+      _coverageMarker();
+      return;
+    }
+    SimpleIdentifier name = node;
+    MethodInvocation invocation = node.parent;
+    if (name != invocation.methodName ||
+        name.name != 'addAll' ||
+        !invocation.isCascaded ||
+        invocation.argumentList.arguments.length != 1) {
+      _coverageMarker();
+      return;
+    }
+    CascadeExpression cascade = invocation.thisOrAncestorOfType();
+    NodeList<Expression> sections = cascade.cascadeSections;
+    Expression target = cascade.target;
+    if (target is! ListLiteral2 || sections[0] != invocation) {
+      _coverageMarker();
+      return;
+    }
+
+    bool isEmptyListLiteral(Expression expression) =>
+        expression is ListLiteral2 && expression.elements.isEmpty;
+
+    ListLiteral2 list = target;
+    Expression argument = invocation.argumentList.arguments[0];
+    String elementText;
+    if (argument is BinaryExpression &&
+        argument.operator.type == TokenType.QUESTION_QUESTION) {
+      Expression right = argument.rightOperand;
+      if (isEmptyListLiteral(right)) {
+        // ..addAll(things ?? const [])
+        // ..addAll(things ?? [])
+        elementText = '...?${utils.getNodeText(argument.leftOperand)}';
+      }
+    } else if (experimentStatus.control_flow_collections &&
+        argument is ConditionalExpression) {
+      Expression elseExpression = argument.elseExpression;
+      if (isEmptyListLiteral(elseExpression)) {
+        // ..addAll(condition ? things : const [])
+        // ..addAll(condition ? things : [])
+        String conditionText = utils.getNodeText(argument.condition);
+        String thenText = utils.getNodeText(argument.thenExpression);
+        elementText = 'if ($conditionText) ...$thenText';
+      }
+    }
+    elementText ??= '...${utils.getNodeText(argument)}';
+    DartChangeBuilder changeBuilder = _newDartChangeBuilder();
+    await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+      builder.addSimpleInsertion(list.elements.last.end, ', $elementText');
+      builder.addDeletion(range.node(invocation));
+    });
+    _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_TO_SPREAD);
+  }
+
   Future<void> _addProposal_convertClassToMixin() async {
     ClassDeclaration classDeclaration =
         node.thisOrAncestorOfType<ClassDeclaration>();
@@ -459,6 +537,42 @@
     _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_CLASS_TO_MIXIN);
   }
 
+  Future<void> _addProposal_convertConditionalExpressionToIfElement() async {
+    AstNode node = this.node;
+    if (node is! ConditionalExpression) {
+      _coverageMarker();
+      return;
+    }
+    AstNode nodeToReplace = node;
+    AstNode parent = node.parent;
+    while (parent is ParenthesizedExpression) {
+      nodeToReplace = parent;
+      parent = parent.parent;
+    }
+    // TODO(brianwilkerson) Consider adding support for map literals.
+    if (parent is ListLiteral2 || parent is SetLiteral2) {
+      ConditionalExpression conditional = node;
+      Expression condition = conditional.condition.unParenthesized;
+      Expression thenExpression = conditional.thenExpression.unParenthesized;
+      Expression elseExpression = conditional.elseExpression.unParenthesized;
+
+      DartChangeBuilder changeBuilder = _newDartChangeBuilder();
+      await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+        builder.addReplacement(range.node(nodeToReplace),
+            (DartEditBuilder builder) {
+          builder.write('if (');
+          builder.write(utils.getNodeText(condition));
+          builder.write(') ');
+          builder.write(utils.getNodeText(thenExpression));
+          builder.write(' else ');
+          builder.write(utils.getNodeText(elseExpression));
+        });
+      });
+      _addAssistFromBuilder(
+          changeBuilder, DartAssistKind.CONVERT_TO_IF_ELEMENT);
+    }
+  }
+
   Future<void> _addProposal_convertDocumentationIntoBlock() async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
@@ -535,11 +649,11 @@
           _coverageMarker();
           return;
         }
-        line = line.substring(expectedPrefix.length).trim();
+        line = line.substring(expectedPrefix.length);
         if (line.isEmpty) {
           newLines.add('$linePrefix///');
         } else {
-          newLines.add('$linePrefix/// $line');
+          newLines.add('$linePrefix///$line');
         }
         linePrefix = eol + prefix;
       }
@@ -679,6 +793,289 @@
     _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_INTO_GETTER);
   }
 
+  Future<void> _addProposal_convertListConstructorToListLiteral() async {
+    //
+    // Ensure that this is the default constructor defined on `List`.
+    //
+    InstanceCreationExpression creation = node.thisOrAncestorOfType();
+    if (creation == null ||
+        node.offset > creation.argumentList.offset ||
+        creation.staticType.element != typeProvider.listType.element ||
+        creation.constructorName.name != null ||
+        creation.argumentList.arguments.length > 0) {
+      _coverageMarker();
+      return;
+    }
+    //
+    // Extract the information needed to build the edit.
+    //
+    TypeArgumentList constructorTypeArguments =
+        creation.constructorName.type.typeArguments;
+    //
+    // Build the change and return the assist.
+    //
+    DartChangeBuilder changeBuilder = _newDartChangeBuilder();
+    await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+      builder.addReplacement(range.node(creation), (DartEditBuilder builder) {
+        if (constructorTypeArguments != null) {
+          builder.write(_getNodeText(constructorTypeArguments));
+        }
+        builder.write('[]');
+      });
+    });
+    _addAssistFromBuilder(
+        changeBuilder, DartAssistKind.CONVERT_TO_LIST_LITERAL);
+  }
+
+  Future<void> _addProposal_convertListToSetToSetLiteral() async {
+    //
+    // Ensure that this is an invocation of `toSet` on a list literal.
+    //
+    if (node is! SimpleIdentifier) {
+      _coverageMarker();
+      return;
+    }
+    AstNode parent = node.parent;
+    if (parent is! MethodInvocation) {
+      _coverageMarker();
+      return;
+    }
+    MethodInvocation invocation = parent as MethodInvocation;
+    if (invocation.methodName != node ||
+        invocation.methodName.name != 'toSet') {
+      _coverageMarker();
+      return;
+    }
+    //
+    // Extract the information needed to build the edit.
+    //
+    Expression target = invocation.target;
+    bool hasTypeArgs;
+    SourceRange openRange;
+    SourceRange closeRange;
+    if (target is ListLiteral) {
+      hasTypeArgs = target.typeArguments != null;
+      openRange = range.token(target.leftBracket);
+      closeRange = range.startEnd(target.rightBracket, invocation);
+    } else if (target is ListLiteral2) {
+      hasTypeArgs = target.typeArguments != null;
+      openRange = range.token(target.leftBracket);
+      closeRange = range.startEnd(target.rightBracket, invocation);
+    } else {
+      _coverageMarker();
+      return;
+    }
+    //
+    // Build the change and return the assist.
+    //
+    DartChangeBuilder changeBuilder = _newDartChangeBuilder();
+    await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+      if (hasTypeArgs || _listHasUnambiguousElement(target)) {
+        builder.addSimpleReplacement(openRange, '{');
+      } else {
+        builder.addSimpleReplacement(openRange, '<dynamic>{');
+      }
+      builder.addSimpleReplacement(closeRange, '}');
+    });
+    _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_TO_SET_LITERAL);
+  }
+
+  Future<void> _addProposal_convertMapConstructorToMapLiteral() async {
+    bool isMapClass(Element element) =>
+        element is ClassElement &&
+        (element == typeProvider.mapType.element ||
+            (element.name == 'LinkedHashMap' &&
+                element.library.name == 'dart.collection'));
+    //
+    // Ensure that this is the default constructor defined on either `Map` or
+    // `LinkedHashMap`.
+    //
+    InstanceCreationExpression creation = node.thisOrAncestorOfType();
+    if (creation == null ||
+        node.offset > creation.argumentList.offset ||
+        creation.constructorName.name != null ||
+        creation.argumentList.arguments.isNotEmpty ||
+        !isMapClass(creation.staticType.element)) {
+      _coverageMarker();
+      return;
+    }
+    //
+    // Extract the information needed to build the edit.
+    //
+    TypeArgumentList constructorTypeArguments =
+        creation.constructorName.type.typeArguments;
+    //
+    // Build the change and return the assist.
+    //
+    DartChangeBuilder changeBuilder = _newDartChangeBuilder();
+    await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+      builder.addReplacement(range.node(creation), (DartEditBuilder builder) {
+        if (constructorTypeArguments != null) {
+          builder.write(_getNodeText(constructorTypeArguments));
+        }
+        builder.write('{}');
+      });
+    });
+    _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_TO_MAP_LITERAL);
+  }
+
+  Future<void> _addProposal_convertMapFromIterableToIfLiteral() async {
+    //
+    // Ensure that the selection is inside an invocation of Map.fromIterable.
+    //
+    InstanceCreationExpression creation =
+        node.thisOrAncestorOfType<InstanceCreationExpression>();
+    if (creation == null) {
+      _coverageMarker();
+      return;
+    }
+    ConstructorElement element = creation.staticElement;
+    if (element.name != 'fromIterable' ||
+        element.enclosingElement != typeProvider.mapType.element) {
+      _coverageMarker();
+      return;
+    }
+    //
+    // Ensure that the arguments have the right form.
+    //
+    NodeList<Expression> arguments = creation.argumentList.arguments;
+    if (arguments.length != 3) {
+      _coverageMarker();
+      return;
+    }
+    Expression iterator = arguments[0].unParenthesized;
+    Expression secondArg = arguments[1];
+    Expression thirdArg = arguments[2];
+
+    Expression extractBody(FunctionExpression expression) {
+      FunctionBody body = expression.body;
+      if (body is ExpressionFunctionBody) {
+        return body.expression;
+      } else if (body is BlockFunctionBody) {
+        NodeList<Statement> statements = body.block.statements;
+        if (statements.length == 1) {
+          Statement statement = statements[0];
+          if (statement is ReturnStatement) {
+            return statement.expression;
+          }
+        }
+      }
+      return null;
+    }
+
+    FunctionExpression extractClosure(String name, Expression argument) {
+      if (argument is NamedExpression && argument.name.label.name == name) {
+        Expression expression = argument.expression.unParenthesized;
+        if (expression is FunctionExpression) {
+          NodeList<FormalParameter> parameters =
+              expression.parameters.parameters;
+          if (parameters.length == 1 && parameters[0].isRequired) {
+            if (extractBody(expression) != null) {
+              return expression;
+            }
+          }
+        }
+      }
+      return null;
+    }
+
+    FunctionExpression keyClosure =
+        extractClosure('key', secondArg) ?? extractClosure('key', thirdArg);
+    FunctionExpression valueClosure =
+        extractClosure('value', thirdArg) ?? extractClosure('value', secondArg);
+    if (keyClosure == null || valueClosure == null) {
+      _coverageMarker();
+      return;
+    }
+    //
+    // Compute the loop variable name and convert the key and value closures if
+    // necessary.
+    //
+    SimpleFormalParameter keyParameter = keyClosure.parameters.parameters[0];
+    String keyParameterName = keyParameter.identifier.name;
+    SimpleFormalParameter valueParameter =
+        valueClosure.parameters.parameters[0];
+    String valueParameterName = valueParameter.identifier.name;
+    Expression keyBody = extractBody(keyClosure);
+    String keyExpressionText = utils.getNodeText(keyBody);
+    Expression valueBody = extractBody(valueClosure);
+    String valueExpressionText = utils.getNodeText(valueBody);
+
+    String loopVariableName;
+    if (keyParameterName == valueParameterName) {
+      loopVariableName = keyParameterName;
+    } else {
+      _ParameterReferenceFinder keyFinder =
+          new _ParameterReferenceFinder(keyParameter.declaredElement);
+      keyBody.accept(keyFinder);
+
+      _ParameterReferenceFinder valueFinder =
+          new _ParameterReferenceFinder(valueParameter.declaredElement);
+      valueBody.accept(valueFinder);
+
+      String computeUnusedVariableName() {
+        String candidate = 'e';
+        var index = 1;
+        while (keyFinder.referencesName(candidate) ||
+            valueFinder.referencesName(candidate)) {
+          candidate = 'e${index++}';
+        }
+        return candidate;
+      }
+
+      if (valueFinder.isParameterUnreferenced) {
+        if (valueFinder.referencesName(keyParameterName)) {
+          // The name of the value parameter is not used, but we can't use the
+          // name of the key parameter because doing so would hide a variable
+          // referenced in the value expression.
+          loopVariableName = computeUnusedVariableName();
+          keyExpressionText = keyFinder.replaceName(
+              keyExpressionText, loopVariableName, keyBody.offset);
+        } else {
+          loopVariableName = keyParameterName;
+        }
+      } else if (keyFinder.isParameterUnreferenced) {
+        if (keyFinder.referencesName(valueParameterName)) {
+          // The name of the key parameter is not used, but we can't use the
+          // name of the value parameter because doing so would hide a variable
+          // referenced in the key expression.
+          loopVariableName = computeUnusedVariableName();
+          valueExpressionText = valueFinder.replaceName(
+              valueExpressionText, loopVariableName, valueBody.offset);
+        } else {
+          loopVariableName = valueParameterName;
+        }
+      } else {
+        // The names are different and both are used. We need to find a name
+        // that would not change the resolution of any other identifiers in
+        // either the key or value expressions.
+        loopVariableName = computeUnusedVariableName();
+        keyExpressionText = keyFinder.replaceName(
+            keyExpressionText, loopVariableName, keyBody.offset);
+        valueExpressionText = valueFinder.replaceName(
+            valueExpressionText, loopVariableName, valueBody.offset);
+      }
+    }
+    //
+    // Construct the edit.
+    //
+    DartChangeBuilder changeBuilder = _newDartChangeBuilder();
+    await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+      builder.addReplacement(range.node(creation), (DartEditBuilder builder) {
+        builder.write('{ for (var ');
+        builder.write(loopVariableName);
+        builder.write(' in ');
+        builder.write(utils.getNodeText(iterator));
+        builder.write(') ');
+        builder.write(keyExpressionText);
+        builder.write(' : ');
+        builder.write(valueExpressionText);
+        builder.write(' }');
+      });
+    });
+    _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_TO_IF_ELEMENT);
+  }
+
   Future<void> _addProposal_convertPartOfToUri() async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
@@ -698,11 +1095,111 @@
     _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_PART_OF_TO_URI);
   }
 
+  Future<void> _addProposal_convertSetConstructorToSetLiteral() async {
+    //
+    // Ensure that this is one of the constructors defined on `Set`.
+    //
+    InstanceCreationExpression creation = node.thisOrAncestorOfType();
+    if (creation == null ||
+        node.offset > creation.argumentList.offset ||
+        creation.staticType.element != typeProvider.setType.element) {
+      // TODO(brianwilkerson) Consider also accepting uses of LinkedHashSet.
+      _coverageMarker();
+      return;
+    }
+    //
+    // Extract the information needed to build the edit.
+    //
+    SimpleIdentifier name = creation.constructorName.name;
+    TypeArgumentList constructorTypeArguments =
+        creation.constructorName.type.typeArguments;
+    TypeArgumentList elementTypeArguments;
+    SourceRange elementsRange;
+    if (name == null) {
+      // Handle an invocation of the default constructor `Set()`.
+    } else if (name.name == 'from' || name.name == 'of') {
+      // Handle an invocation of the constructor `Set.from()` or `Set.of()`.
+      NodeList<Expression> arguments = creation.argumentList.arguments;
+      if (arguments.length != 1) {
+        _coverageMarker();
+        return;
+      }
+      if (arguments[0] is ListLiteral) {
+        ListLiteral elements = arguments[0] as ListLiteral;
+        elementTypeArguments = elements.typeArguments;
+        elementsRange =
+            range.endStart(elements.leftBracket, elements.rightBracket);
+      } else if (arguments[0] is ListLiteral2) {
+        ListLiteral2 elements = arguments[0] as ListLiteral2;
+        elementTypeArguments = elements.typeArguments;
+        elementsRange =
+            range.endStart(elements.leftBracket, elements.rightBracket);
+      } else {
+        // TODO(brianwilkerson) Consider handling other iterables. Literal sets
+        //  could be treated like lists, and arbitrary iterables by using a
+        //  spread.
+        _coverageMarker();
+        return;
+      }
+    } else {
+      // Invocation of an unhandled constructor.
+      _coverageMarker();
+      return;
+    }
+    new Map();
+    //
+    // Determine whether type arguments are strictly required in cases where no
+    // type arguments were explicitly provided.
+    //
+    bool hasUnambiguousElement() {
+      NodeList<Expression> arguments = creation.argumentList.arguments;
+      if (arguments == null || arguments.isEmpty) {
+        return false;
+      }
+      return _listHasUnambiguousElement(arguments[0]);
+    }
+
+    bool setWouldBeInferred() {
+      AstNode parent = creation.parent;
+      if (parent is VariableDeclaration) {
+        AstNode parent2 = parent.parent;
+        if (parent2 is VariableDeclarationList &&
+            parent2.type?.type?.element == typeProvider.setType.element) {
+          return true;
+        }
+      }
+      return hasUnambiguousElement();
+    }
+
+    //
+    // Build the change and return the assist.
+    //
+    DartChangeBuilder changeBuilder = _newDartChangeBuilder();
+    await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+      builder.addReplacement(range.node(creation), (DartEditBuilder builder) {
+        if (constructorTypeArguments != null) {
+          builder.write(_getNodeText(constructorTypeArguments));
+        } else if (elementTypeArguments != null) {
+          builder.write(_getNodeText(elementTypeArguments));
+        } else if (!setWouldBeInferred()) {
+          builder.write('<dynamic>');
+        }
+        builder.write('{');
+        if (elementsRange != null) {
+          builder.write(_getRangeText(elementsRange));
+        }
+        builder.write('}');
+      });
+    });
+    _addAssistFromBuilder(changeBuilder, DartAssistKind.CONVERT_TO_SET_LITERAL);
+  }
+
   Future<void> _addProposal_convertToAsyncFunctionBody() async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     FunctionBody body = getEnclosingFunctionBody();
-    if (body == null || body.isAsynchronous || body.isGenerator) {
+    if (body == null ||
+        body is EmptyFunctionBody ||
+        body.isAsynchronous ||
+        body.isGenerator) {
       _coverageMarker();
       return;
     }
@@ -1226,6 +1723,36 @@
         changeBuilder, DartAssistKind.CONVERT_INTO_IS_NOT_EMPTY);
   }
 
+  Future<void> _addProposal_convertToMultilineString() async {
+    var node = this.node;
+    if (node is InterpolationElement) {
+      node = (node as InterpolationElement).parent;
+    }
+    if (node is SingleStringLiteral) {
+      SingleStringLiteral literal = node;
+      if (!literal.isMultiline) {
+        var changeBuilder = _newDartChangeBuilder();
+        await changeBuilder.addFileEdit(file, (builder) {
+          var newQuote = literal.isSingleQuoted ? "'''" : '"""';
+          builder.addReplacement(
+            SourceRange(literal.offset + (literal.isRaw ? 1 : 0), 1),
+            (builder) {
+              builder.writeln(newQuote);
+            },
+          );
+          builder.addSimpleReplacement(
+            SourceRange(literal.end - 1, 1),
+            newQuote,
+          );
+        });
+        _addAssistFromBuilder(
+          changeBuilder,
+          DartAssistKind.CONVERT_TO_MULTILINE_STRING,
+        );
+      }
+    }
+  }
+
   Future<void> _addProposal_convertToNormalParameter() async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
@@ -1267,36 +1794,6 @@
     }
   }
 
-  Future<void> _addProposal_convertToMultilineString() async {
-    var node = this.node;
-    if (node is InterpolationElement) {
-      node = (node as InterpolationElement).parent;
-    }
-    if (node is SingleStringLiteral) {
-      SingleStringLiteral literal = node;
-      if (!literal.isMultiline) {
-        var changeBuilder = _newDartChangeBuilder();
-        await changeBuilder.addFileEdit(file, (builder) {
-          var newQuote = literal.isSingleQuoted ? "'''" : '"""';
-          builder.addReplacement(
-            SourceRange(literal.offset + (literal.isRaw ? 1 : 0), 1),
-            (builder) {
-              builder.writeln(newQuote);
-            },
-          );
-          builder.addSimpleReplacement(
-            SourceRange(literal.end - 1, 1),
-            newQuote,
-          );
-        });
-        _addAssistFromBuilder(
-          changeBuilder,
-          DartAssistKind.CONVERT_TO_MULTILINE_STRING,
-        );
-      }
-    }
-  }
-
   Future<void> _addProposal_convertToSingleQuotedString() async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
@@ -1532,7 +2029,7 @@
     }
 
     String widgetName = widgetClassElement.displayName;
-    String stateName = widgetName + 'State';
+    String stateName = '_${widgetName}State';
 
     // Find fields assigned in constructors.
     var fieldsAssignedInConstructors = new Set<FieldElement>();
@@ -1655,9 +2152,7 @@
                 builder.writeln();
               }
               builder.writeln('  @override');
-              builder.writeln('  $stateName createState() {');
-              builder.writeln('    return $stateName();');
-              builder.writeln('  }');
+              builder.writeln('  $stateName createState() => $stateName();');
               if (hasEmptyLineAfterCreateState) {
                 builder.writeln();
               }
@@ -3391,6 +3886,36 @@
     return utils.getRangeText(range);
   }
 
+  /// Return `true` if the [element] is sufficient to lexically make the
+  /// enclosing literal a set literal rather than a map.
+  bool _isUnambiguousElement(CollectionElement element) {
+    if (element is ForElement) {
+      return _isUnambiguousElement(element.body);
+    } else if (element is IfElement) {
+      return _isUnambiguousElement(element.thenElement) ||
+          _isUnambiguousElement(element.elseElement);
+    } else if (element is Expression) {
+      return true;
+    }
+    return false;
+  }
+
+  /// Return `true` if the given [node] is a list literal whose elements, if
+  /// placed inside curly braces, would lexically make the resulting literal a
+  /// set literal rather than a map literal.
+  bool _listHasUnambiguousElement(AstNode node) {
+    if (node is ListLiteral && node.elements.length > 0) {
+      return true;
+    } else if (node is ListLiteral2) {
+      for (CollectionElement element in node.elements) {
+        if (_isUnambiguousElement(element)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
   DartChangeBuilder _newDartChangeBuilder() {
     return new DartChangeBuilderImpl.forWorkspace(context.workspace);
   }
@@ -3539,6 +4064,70 @@
   }
 }
 
+/**
+ * A visitor that can be used to find references to a parameter.
+ */
+class _ParameterReferenceFinder extends RecursiveAstVisitor<void> {
+  /**
+   * The parameter for which references are being sought, or `null` if we are
+   * just accumulating a list of referenced names.
+   */
+  final ParameterElement parameter;
+
+  /**
+   * A list of the simple identifiers that reference the [parameter].
+   */
+  final List<SimpleIdentifier> references = <SimpleIdentifier>[];
+
+  /**
+   * A collection of the names of other simple identifiers that were found. We
+   * need to know these in order to ensure that the selected loop variable does
+   * not hide a name from an enclosing scope that is already being referenced.
+   */
+  final Set<String> otherNames = new Set<String>();
+
+  /**
+   * Initialize a newly created finder to find references to the [parameter].
+   */
+  _ParameterReferenceFinder(this.parameter) : assert(parameter != null);
+
+  /**
+   * Return `true` if the parameter is unreferenced in the nodes that have been
+   * visited.
+   */
+  bool get isParameterUnreferenced => references.isEmpty;
+
+  /**
+   * Return `true` is the given name (assumed to be different than the name of
+   * the parameter) is references in the nodes that have been visited.
+   */
+  bool referencesName(String name) => otherNames.contains(name);
+
+  /**
+   * Replace all of the references to the parameter in the given [source] with
+   * the [newName]. The [offset] is the offset of the first character of the
+   * [source] relative to the start of the file.
+   */
+  String replaceName(String source, String newName, int offset) {
+    int oldLength = parameter.name.length;
+    for (int i = references.length - 1; i >= 0; i--) {
+      int oldOffset = references[i].offset - offset;
+      source = source.replaceRange(oldOffset, oldOffset + oldLength, newName);
+    }
+    return source;
+  }
+
+  @override
+  void visitSimpleIdentifier(SimpleIdentifier node) {
+    if (node.staticElement == parameter) {
+      references.add(node);
+    } else if (!node.isQualified) {
+      // Only non-prefixed identifiers can be hidden.
+      otherNames.add(node.name);
+    }
+  }
+}
+
 class _SimpleIdentifierRecursiveAstVisitor extends RecursiveAstVisitor {
   final _SimpleIdentifierVisitor visitor;
 
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 2f872621..f193dd1 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -267,8 +267,6 @@
       'REPLACE_WITH_CONDITIONAL_ASSIGNMENT', 50, "Replace with ??=");
   static const REPLACE_WITH_IDENTIFIER =
       const FixKind('REPLACE_WITH_IDENTIFIER', 50, "Replace with identifier");
-  static const REPLACE_WITH_LITERAL =
-      const FixKind('REPLACE_WITH_LITERAL', 50, "Replace with literal");
   static const REPLACE_WITH_NULL_AWARE = const FixKind(
       'REPLACE_WITH_NULL_AWARE',
       50,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 2d8e769..c6600c1 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -584,9 +584,6 @@
       if (name == LintNames.non_constant_identifier_names) {
         await _addFix_renameToCamelCase();
       }
-      if (name == LintNames.prefer_collection_literals) {
-        await _addFix_replaceWithLiteral();
-      }
       if (name == LintNames.prefer_conditional_assignment) {
         await _addFix_replaceWithConditionalAssignment();
       }
@@ -931,9 +928,12 @@
           if (arguments.isNotEmpty) {
             builder.write(', ');
           }
-          String defaultValue =
-              getDefaultStringParameterValue(missingParameter);
-          builder.write('$missingParameterName: $defaultValue');
+
+          builder.write('$missingParameterName: ');
+
+          var defaultValue = getDefaultStringParameterValue(missingParameter);
+          builder.addSimpleLinkedEdit('VALUE', defaultValue);
+
           // Insert a trailing comma after Flutter instance creation params.
           if (!hasTrailingComma && flutter.isWidgetExpression(creation)) {
             builder.write(',');
@@ -3159,30 +3159,6 @@
     }
   }
 
-  Future<void> _addFix_replaceWithLiteral() async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
-    final InstanceCreationExpression instanceCreation =
-        node.thisOrAncestorOfType<InstanceCreationExpression>();
-    final InterfaceType type = instanceCreation.staticType;
-    final generics = instanceCreation.constructorName.type.typeArguments;
-    var changeBuilder = _newDartChangeBuilder();
-    await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
-      builder.addReplacement(range.node(instanceCreation),
-          (DartEditBuilder builder) {
-        if (generics != null) {
-          builder.write(utils.getNodeText(generics));
-        }
-        if (type.name == 'List') {
-          builder.write('[]');
-        } else {
-          builder.write('{}');
-        }
-      });
-    });
-    _addFixFromBuilder(changeBuilder, DartFixKind.REPLACE_WITH_LITERAL);
-  }
-
   Future<void> _addFix_replaceWithTearOff() async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
diff --git a/pkg/analysis_server/lib/src/utilities/documentation.dart b/pkg/analysis_server/lib/src/utilities/documentation.dart
deleted file mode 100644
index b47bb2f..0000000
--- a/pkg/analysis_server/lib/src/utilities/documentation.dart
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2015, 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.
-
-String getDartDocSummary(String str) {
-  if (str == null) {
-    return null;
-  }
-  List<String> lines = str.split('\n');
-  StringBuffer sb = new StringBuffer();
-  bool firstLine = true;
-  for (String line in lines) {
-    if (sb.length != 0 && line.isEmpty) {
-      return sb.toString();
-    }
-    if (!firstLine) {
-      sb.write('\n');
-    }
-    firstLine = false;
-    sb.write(line);
-  }
-  return sb.toString();
-}
-
-/**
- * Converts [str] from a Dart Doc string with slashes and stars to a plain text
- * representation of the comment.
- */
-String removeDartDocDelimiters(String str) {
-  if (str == null) {
-    return null;
-  }
-  // remove /** */
-  if (str.startsWith('/**')) {
-    str = str.substring(3);
-  }
-  if (str.endsWith("*/")) {
-    str = str.substring(0, str.length - 2);
-  }
-  str = str.trim();
-  // remove leading '* ' and '/// '
-  List<String> lines = str.split('\n');
-  StringBuffer sb = new StringBuffer();
-  bool firstLine = true;
-  for (String line in lines) {
-    line = line.trim();
-    if (line.startsWith("*")) {
-      line = line.substring(1);
-      if (line.startsWith(" ")) {
-        line = line.substring(1);
-      }
-    } else if (line.startsWith("///")) {
-      line = line.substring(3);
-      if (line.startsWith(" ")) {
-        line = line.substring(1);
-      }
-    }
-    if (!firstLine) {
-      sb.write('\n');
-    }
-    firstLine = false;
-    sb.write(line);
-  }
-  str = sb.toString();
-  // done
-  return str;
-}
diff --git a/pkg/analysis_server/pubspec.yaml b/pkg/analysis_server/pubspec.yaml
new file mode 100644
index 0000000..d9f2c93
--- /dev/null
+++ b/pkg/analysis_server/pubspec.yaml
@@ -0,0 +1,29 @@
+name: analysis_server
+publish_to: none
+
+environment:
+  sdk: '>=2.1.0-dev.5.0 <3.0.0'
+
+dependencies:
+  analyzer: any
+  analyzer_plugin: any
+  args: any
+  convert: any
+  crypto: any
+  dart_style: any
+  front_end: any
+  linter: any
+  logging: any
+  meta: any
+  source_span: any
+  package_config: any
+  path: any
+  watcher: any
+  yaml: any
+
+dev_dependencies:
+  analysis_tool: any
+  html: any
+  http: any
+  test_reflective_loader: any
+  test: any
diff --git a/pkg/analysis_server/test/abstract_context.dart b/pkg/analysis_server/test/abstract_context.dart
index d9ffdb2..578053b 100644
--- a/pkg/analysis_server/test/abstract_context.dart
+++ b/pkg/analysis_server/test/abstract_context.dart
@@ -11,10 +11,10 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/visitor.dart';
 import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/overlay_file_system.dart';
 import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
-import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine;
 import 'package:analyzer/src/generated/source_io.dart';
 import 'package:analyzer/src/test_utilities/mock_sdk.dart';
@@ -45,11 +45,15 @@
 typedef void _ElementVisitorFunction(Element element);
 
 class AbstractContextTest with ResourceProviderMixin {
-  FileContentOverlay fileContentOverlay = new FileContentOverlay();
+  OverlayResourceProvider overlayResourceProvider;
 
   AnalysisContextCollection _analysisContextCollection;
   AnalysisDriver _driver;
 
+  /// The file system specific `/home/test/analysis_options.yaml` path.
+  String get analysisOptionsPath =>
+      convertPath('/home/test/analysis_options.yaml');
+
   AnalysisDriver get driver => _driver;
 
   AnalysisSession get session => driver.currentSession;
@@ -210,8 +214,7 @@
     _analysisContextCollection = AnalysisContextCollectionImpl(
       includedPaths: [convertPath('/home')],
       enableIndex: true,
-      fileContentOverlay: fileContentOverlay,
-      resourceProvider: resourceProvider,
+      resourceProvider: overlayResourceProvider,
       sdkPath: convertPath('/sdk'),
     );
 
@@ -219,6 +222,22 @@
     _driver = getDriver(testPath);
   }
 
+  /// Create an analysis options file based on the given arguments.
+  void createAnalysisOptionsFile({List<String> experiments}) {
+    StringBuffer buffer = new StringBuffer();
+    if (experiments != null) {
+      buffer.writeln('analyzer:');
+      buffer.writeln('  enable-experiment:');
+      for (String experiment in experiments) {
+        buffer.writeln('    - $experiment');
+      }
+    }
+    newFile(analysisOptionsPath, content: buffer.toString());
+    if (_driver != null) {
+      createAnalysisContexts();
+    }
+  }
+
   /// Return the existing analysis context that should be used to analyze the
   /// given [path], or throw [StateError] if the [path] is not analyzed in any
   /// of the created analysis contexts.
@@ -242,6 +261,7 @@
 
   void setUp() {
     setupResourceProvider();
+    overlayResourceProvider = OverlayResourceProvider(resourceProvider);
 
     new MockSdk(resourceProvider: resourceProvider);
 
diff --git a/pkg/analysis_server/test/abstract_single_unit.dart b/pkg/analysis_server/test/abstract_single_unit.dart
index b0901725..798bedb 100644
--- a/pkg/analysis_server/test/abstract_single_unit.dart
+++ b/pkg/analysis_server/test/abstract_single_unit.dart
@@ -9,6 +9,7 @@
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/dart/ast/element_locator.dart';
 import 'package:analyzer/src/dart/ast/utilities.dart';
 import 'package:analyzer/src/dart/error/hint_codes.dart';
 import 'package:analyzer/src/generated/java_engine.dart';
diff --git a/pkg/analysis_server/test/analysis/notification_analyzedFiles_test.dart b/pkg/analysis_server/test/analysis/notification_analyzedFiles_test.dart
index bd83a7d..9073af2 100644
--- a/pkg/analysis_server/test/analysis/notification_analyzedFiles_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_analyzedFiles_test.dart
@@ -119,7 +119,7 @@
     expect(analyzedFilesReceived, isTrue);
 
     analyzedFilesReceived = false;
-    modifyTestFile('import "${convertAbsolutePathToUri('/foo.dart')}";');
+    modifyTestFile('import "${toUriStr('/foo.dart')}";');
     await prepareAnalyzedFiles();
     assertHasFile(convertPath('/foo.dart'));
   }
diff --git a/pkg/analysis_server/test/analysis/notification_implemented_test.dart b/pkg/analysis_server/test/analysis/notification_implemented_test.dart
index 4401087..539354b 100644
--- a/pkg/analysis_server/test/analysis/notification_implemented_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_implemented_test.dart
@@ -71,7 +71,7 @@
   }
 
   /**
-   * Validates that there is no an [ImplementedClass] at the offset of [search].
+   * Validates that there is no [ImplementedMember] at the offset of [search].
    *
    * If [length] is not specified explicitly, then length of an identifier
    * from [search] is used.
@@ -167,6 +167,21 @@
     assertHasImplementedClass('A {');
   }
 
+  test_class_inMixin() async {
+    addTestFile('''
+class A {} // ref
+class B {} // ref
+class C {} // ref
+class D {} // ref
+mixin M on A, B implements C, D {}
+''');
+    await prepareImplementedElements();
+    assertHasImplementedClass('A {} // ref');
+    assertHasImplementedClass('B {} // ref');
+    assertHasImplementedClass('C {} // ref');
+    assertHasImplementedClass('D {} // ref');
+  }
+
   test_class_mixed() async {
     addTestFile('''
 class A {}
@@ -313,6 +328,40 @@
     assertHasImplementedMember('m(); // A');
   }
 
+  test_mixin_implemented() async {
+    addTestFile('''
+mixin M { // ref
+  void foo() {} // ref
+  void bar() {} // ref
+}
+
+class A implements M {
+  void foo() {}
+}
+''');
+    await prepareImplementedElements();
+    assertHasImplementedClass('M { // ref');
+    assertHasImplementedMember('foo() {} // ref');
+    assertNoImplementedMember('bar() {} // ref');
+  }
+
+  test_mixin_mixed() async {
+    addTestFile('''
+mixin M { // ref
+  void foo() {} // ref
+  void bar() {} // ref
+}
+
+class A extends Object with M {
+  void foo() {}
+}
+''');
+    await prepareImplementedElements();
+    assertHasImplementedClass('M { // ref');
+    assertHasImplementedMember('foo() {} // ref');
+    assertNoImplementedMember('bar() {} // ref');
+  }
+
   test_setter_withField() async {
     addTestFile('''
 class A {
diff --git a/pkg/analysis_server/test/analysis/notification_overrides_test.dart b/pkg/analysis_server/test/analysis/notification_overrides_test.dart
index dfaec37..c840005 100644
--- a/pkg/analysis_server/test/analysis/notification_overrides_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_overrides_test.dart
@@ -305,6 +305,38 @@
     assertHasInterfaceMember('m() {} // in A');
   }
 
+  test_inMixin_interface_method_direct_single() async {
+    addTestFile('''
+class A {
+  m() {} // in A
+}
+
+mixin M implements A {
+  m() {} // in M
+}
+''');
+    await prepareOverrides();
+    assertHasOverride('m() {} // in M');
+    assertNoSuperMember();
+    assertHasInterfaceMember('m() {} // in A');
+  }
+
+  test_inMixin_superclassConstraint_method_direct() async {
+    addTestFile('''
+class A {
+  m() {} // in A
+}
+
+mixin M on A {
+  m() {} // in M
+}
+''');
+    await prepareOverrides();
+    assertHasOverride('m() {} // in M');
+    assertHasSuperElement('m() {} // in A');
+    assertNoInterfaceMembers();
+  }
+
   test_interface_method_direct_multiple() async {
     addTestFile('''
 class IA {
diff --git a/pkg/analysis_server/test/completion_test.dart b/pkg/analysis_server/test/completion_test.dart
index d0cd508..692cbb4 100644
--- a/pkg/analysis_server/test/completion_test.dart
+++ b/pkg/analysis_server/test/completion_test.dart
@@ -163,19 +163,13 @@
         <String>["1+DateTime", "2+List"],
         failingTests: '12');
 
-    buildTests(
-        'testCommentSnippets030',
-        '''
+    buildTests('testCommentSnippets030', '''
 class Bar<T extends Foo> {const Bar(!1T!2 k);T!3 m(T!4 a, T!5 b){}final T!6 f = null;}''',
-        <String>["1+T", "2+T", "3+T", "4+T", "5+T", "6+T"],
-        failingTests: '123456');
+        <String>["1+T", "2+T", "3+T", "4+T", "5+T", "6+T"]);
 
-    buildTests(
-        'testCommentSnippets031',
-        '''
+    buildTests('testCommentSnippets031', '''
 class Bar<T extends Foo> {m(x){if (x is !1) return;if (x is!!!2)}}''',
-        <String>["1+Bar", "1+T", "2+T", "2+Bar"],
-        failingTests: '12');
+        <String>["1+Bar", "1+T", "2+T", "2+Bar"]);
 
     buildTests(
         'testCommentSnippets032',
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 4749ab7..fa74e67 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -252,7 +252,7 @@
 
   test_import_uri_with_trailing() {
     final filePath = '/project/bin/testA.dart';
-    final incompleteImportText = convertAbsolutePathToUri('/project/bin/t');
+    final incompleteImportText = toUriStr('/project/bin/t');
     newFile(filePath, content: 'library libA;');
     addTestFile('''
     import "$incompleteImportText^.dart";
@@ -261,8 +261,7 @@
       expect(replacementOffset,
           equals(completionOffset - incompleteImportText.length));
       expect(replacementLength, equals(5 + incompleteImportText.length));
-      assertHasResult(
-          CompletionSuggestionKind.IMPORT, convertAbsolutePathToUri(filePath));
+      assertHasResult(CompletionSuggestionKind.IMPORT, toUriStr(filePath));
       assertNoResult('test');
     });
   }
@@ -510,7 +509,7 @@
   foo(bar) => 0;''');
     addTestFile('''
   library libA;
-  part "${convertAbsolutePathToUri('/testA.dart')}";
+  part "${toUriStr('/testA.dart')}";
   import "dart:math";
   /// The [^]
   main(aaa, bbb) {}
diff --git a/pkg/analysis_server/test/domain_edit_dartfix_test.dart b/pkg/analysis_server/test/domain_edit_dartfix_test.dart
index 8e568e4..3f322e6 100644
--- a/pkg/analysis_server/test/domain_edit_dartfix_test.dart
+++ b/pkg/analysis_server/test/domain_edit_dartfix_test.dart
@@ -37,15 +37,20 @@
   }
 
   void expectSuggestion(DartFixSuggestion suggestion, String partialText,
-      int offset, int length) {
+      [int offset, int length]) {
     expect(suggestion.description, contains(partialText));
-    expect(suggestion.location.offset, offset);
-    expect(suggestion.location.length, length);
+    if (offset == null) {
+      expect(suggestion.location, isNull);
+    } else {
+      expect(suggestion.location.offset, offset);
+      expect(suggestion.location.length, length);
+    }
   }
 
-  Future<EditDartfixResult> performFix() async {
+  Future<EditDartfixResult> performFix({List<String> includedFixes}) async {
     final id = nextRequestId;
     final params = new EditDartfixParams([projectPath]);
+    params.includedFixes = includedFixes;
     final request = new Request(id, 'edit.dartfix', params.toJson());
 
     final response = await new EditDartFix(server, request).compute();
@@ -63,12 +68,12 @@
   }
 
   test_dartfix_convertClassToMixin() async {
-    createProject();
     addTestFile('''
 class A {}
 class B extends A {}
 class C with B {}
     ''');
+    createProject();
     EditDartfixResult result = await performFix();
     expect(result.suggestions, hasLength(1));
     expectSuggestion(result.suggestions[0], 'mixin', 17, 1);
@@ -80,10 +85,10 @@
   }
 
   test_dartfix_convertToIntLiteral() async {
-    createProject();
     addTestFile('''
 const double myDouble = 42.0;
     ''');
+    createProject();
     EditDartfixResult result = await performFix();
     expect(result.suggestions, hasLength(1));
     expectSuggestion(result.suggestions[0], 'int literal', 24, 4);
@@ -93,13 +98,13 @@
   }
 
   test_dartfix_moveTypeArgumentToClass() async {
-    createProject();
     addTestFile('''
 class A<T> { A.from(Object obj) { } }
 main() {
   print(new A.from<String>([]));
 }
     ''');
+    createProject();
     EditDartfixResult result = await performFix();
     expect(result.suggestions, hasLength(1));
     expectSuggestion(result.suggestions[0], 'type arguments', 65, 8);
@@ -118,58 +123,23 @@
   enable-experiment:
     - non-nullable
 ''');
-
-    createProject();
     addTestFile('''
-main() {
-  functionWithNullableParam(new List<String>(1));
-  functionWithNullableParam(null);
-}
-
-class C1 {}
-class C2 {}
-class C extends C1 with M1 implements C2 {}
-
-mixin M1 {}
-mixin M on M1 implements C1 {}
-
-void functionWithNullableParam(String object) {
-  if (object == null) {
-    print('object is null');
-  } else {
-    print('object is not-null');
-  }
-  List<Object> list = null;
-  list = <Object>[];
-  list.add(object);
+int f(int i) => 0;
+int g(int i) => f(i);
+void test() {
+  g(null);
 }
 ''');
-    EditDartfixResult result = await performFix();
-    expect(result.suggestions, hasLength(1));
+    createProject();
+    EditDartfixResult result =
+        await performFix(includedFixes: ['non-nullable']);
+    expect(result.suggestions.length, greaterThanOrEqualTo(1));
     expect(result.hasErrors, isFalse);
-    expectSuggestion(result.suggestions[0], 'non-nullable', 46, 6);
     expectEdits(result.edits, '''
-main() {
-  functionWithNullableParam(new List<String?>(1));
-  functionWithNullableParam(null);
-}
-
-class C1 {}
-class C2 {}
-class C extends C1 with M1 implements C2 {}
-
-mixin M1 {}
-mixin M on M1 implements C1 {}
-
-void functionWithNullableParam(String? object) {
-  if (object == null) {
-    print('object is null');
-  } else {
-    print('object is not-null');
-  }
-  List<Object?>? list = null;
-  list = <Object?>[];
-  list.add(object);
+int f(int? i) => 0;
+int g(int? i) => f(i);
+void test() {
+  g(null);
 }
 ''');
   }
@@ -182,10 +152,10 @@
     - lib/**
 ''');
 
-    createProject();
     addTestFile('''
 const double myDouble = 42.0;
     ''');
+    createProject();
 
     // Assert no suggestions now that source has been excluded
     final result = await performFix();
@@ -194,7 +164,6 @@
   }
 
   test_dartfix_partFile() async {
-    createProject();
     newFile('/project/lib/lib.dart', content: '''
 library lib2;
 part 'fileToBeFixed.dart';
@@ -203,6 +172,7 @@
 part of lib2;
 const double myDouble = 42.0;
     ''');
+    createProject();
 
     // Assert dartfix suggestions
     EditDartfixResult result = await performFix();
@@ -215,11 +185,11 @@
   }
 
   test_dartfix_partFile_loose() async {
-    createProject();
     addTestFile('''
 part of lib2;
 const double myDouble = 42.0;
     ''');
+    createProject();
 
     // Assert dartfix suggestions
     EditDartfixResult result = await performFix();
diff --git a/pkg/analysis_server/test/integration/analysis/error_test.dart b/pkg/analysis_server/test/integration/analysis/error_test.dart
index 4da91dc..0a7948b 100644
--- a/pkg/analysis_server/test/integration/analysis/error_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/error_test.dart
@@ -88,7 +88,7 @@
   }
 }
 ''');
-    // ignore: deprecated_member_use
+    // ignore: deprecated_member_use_from_same_package
     await sendAnalysisUpdateOptions(
         new AnalysisOptions()..enableSuperMixins = true);
     standardAnalysisSetup();
diff --git a/pkg/analysis_server/test/integration/analysis/get_reachable_sources_test.dart b/pkg/analysis_server/test/integration/analysis/get_reachable_sources_test.dart
index 286a24e..dee7a87 100644
--- a/pkg/analysis_server/test/integration/analysis/get_reachable_sources_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/get_reachable_sources_test.dart
@@ -35,7 +35,7 @@
     await analysisFinished;
 
     AnalysisGetReachableSourcesResult result =
-        // ignore: deprecated_member_use
+        // ignore: deprecated_member_use_from_same_package
         await sendAnalysisGetReachableSources(pathname);
     Map<String, List<String>> sources = result.sources;
     List<String> keys = sources.keys.toList();
diff --git a/pkg/analysis_server/test/integration/analysis/update_options_test.dart b/pkg/analysis_server/test/integration/analysis/update_options_test.dart
index b61db22..871e4fe 100644
--- a/pkg/analysis_server/test/integration/analysis/update_options_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/update_options_test.dart
@@ -30,14 +30,14 @@
 ''');
     standardAnalysisSetup();
 
-    // ignore: deprecated_member_use
+    // ignore: deprecated_member_use_from_same_package
     await sendAnalysisUpdateOptions(
         new AnalysisOptions()..generateHints = false);
     await sendAnalysisReanalyze();
     await analysisFinished;
     expect(getErrors(pathname), isEmpty);
 
-    // ignore: deprecated_member_use
+    // ignore: deprecated_member_use_from_same_package
     await sendAnalysisUpdateOptions(
         new AnalysisOptions()..generateHints = true);
     await sendAnalysisReanalyze();
diff --git a/pkg/analysis_server/test/integration/coverage.md b/pkg/analysis_server/test/integration/coverage.md
index dcec50a..6f13a5e 100644
--- a/pkg/analysis_server/test/integration/coverage.md
+++ b/pkg/analysis_server/test/integration/coverage.md
@@ -30,18 +30,23 @@
 - [x] analysis.overrides
 
 ## completion domain
+- [ ] completion.availableSuggestions
+- [ ] completion.getSuggestionDetails
 - [x] completion.getSuggestions
+- [ ] completion.registerLibraryPaths
 - [ ] completion.results
+- [ ] completion.setSubscriptions
 
 ## diagnostic domain
 - [x] diagnostic.getDiagnostics
 - [x] diagnostic.getServerPort
 
 ## edit domain
-- [ ] edit.dartfix
+- [x] edit.dartfix
 - [x] edit.format
 - [x] edit.getAssists
 - [x] edit.getAvailableRefactorings
+- [x] edit.getDartfixInfo
 - [x] edit.getFixes
 - [x] edit.getPostfixCompletion
 - [x] edit.getRefactoring
diff --git a/pkg/analysis_server/test/integration/edit/dartfix_test.dart b/pkg/analysis_server/test/integration/edit/dartfix_test.dart
new file mode 100644
index 0000000..d389950
--- /dev/null
+++ b/pkg/analysis_server/test/integration/edit/dartfix_test.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2019, 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/protocol/protocol_generated.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../support/integration_tests.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(DartfixTest);
+  });
+}
+
+@reflectiveTest
+class DartfixTest extends AbstractAnalysisServerIntegrationTest {
+  void setupTarget() {
+    writeFile(sourcePath('test.dart'), '''
+class A {}
+class B extends A {}
+class C with B {}
+    ''');
+    standardAnalysisSetup();
+  }
+
+  test_dartfix() async {
+    setupTarget();
+    EditDartfixResult result = await sendEditDartfix([(sourceDirectory.path)]);
+    expect(result.hasErrors, isFalse);
+    expect(result.suggestions.length, greaterThanOrEqualTo(1));
+    expect(result.edits.length, greaterThanOrEqualTo(1));
+  }
+
+  test_dartfix_exclude() async {
+    setupTarget();
+    EditDartfixResult result = await sendEditDartfix([(sourceDirectory.path)],
+        excludedFixes: ['use-mixin']);
+    expect(result.hasErrors, isFalse);
+    expect(result.suggestions.length, 0);
+    expect(result.edits.length, 0);
+  }
+
+  test_dartfix_exclude_other() async {
+    setupTarget();
+    EditDartfixResult result = await sendEditDartfix([(sourceDirectory.path)],
+        excludedFixes: ['double-to-int']);
+    expect(result.hasErrors, isFalse);
+    expect(result.suggestions.length, greaterThanOrEqualTo(1));
+    expect(result.edits.length, greaterThanOrEqualTo(1));
+  }
+
+  test_dartfix_include() async {
+    setupTarget();
+    EditDartfixResult result = await sendEditDartfix([(sourceDirectory.path)],
+        includedFixes: ['use-mixin']);
+    expect(result.hasErrors, isFalse);
+    expect(result.suggestions.length, greaterThanOrEqualTo(1));
+    expect(result.edits.length, greaterThanOrEqualTo(1));
+  }
+
+  test_dartfix_include_other() async {
+    setupTarget();
+    EditDartfixResult result = await sendEditDartfix([(sourceDirectory.path)],
+        includedFixes: ['double-to-int']);
+    expect(result.hasErrors, isFalse);
+    expect(result.suggestions.length, 0);
+    expect(result.edits.length, 0);
+  }
+
+  test_dartfix_required() async {
+    setupTarget();
+    EditDartfixResult result = await sendEditDartfix([(sourceDirectory.path)],
+        includeRequiredFixes: true);
+    expect(result.hasErrors, isFalse);
+    expect(result.suggestions.length, greaterThanOrEqualTo(1));
+    expect(result.edits.length, greaterThanOrEqualTo(1));
+  }
+}
diff --git a/pkg/analysis_server/test/integration/edit/get_dartfix_info_test.dart b/pkg/analysis_server/test/integration/edit/get_dartfix_info_test.dart
new file mode 100644
index 0000000..2d92b6a
--- /dev/null
+++ b/pkg/analysis_server/test/integration/edit/get_dartfix_info_test.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2019, 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/protocol/protocol_generated.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../support/integration_tests.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(GetDartfixInfoTest);
+  });
+}
+
+@reflectiveTest
+class GetDartfixInfoTest extends AbstractAnalysisServerIntegrationTest {
+  test_getDartfixInfo() async {
+    standardAnalysisSetup();
+    EditGetDartfixInfoResult info = await sendEditGetDartfixInfo();
+    expect(info.fixes.length, greaterThanOrEqualTo(3));
+    var fix = info.fixes.firstWhere((f) => f.name == 'use-mixin');
+    expect(fix.isRequired, isTrue);
+  }
+}
diff --git a/pkg/analysis_server/test/integration/edit/test_all.dart b/pkg/analysis_server/test/integration/edit/test_all.dart
index 6706806..3a71a95 100644
--- a/pkg/analysis_server/test/integration/edit/test_all.dart
+++ b/pkg/analysis_server/test/integration/edit/test_all.dart
@@ -4,10 +4,12 @@
 
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import 'dartfix_test.dart' as dartfix_test;
 import 'format_test.dart' as format_test;
 import 'get_assists_test.dart' as get_assists_test;
 import 'get_available_refactorings_test.dart'
     as get_available_refactorings_test;
+import 'get_dartfix_info_test.dart' as get_dartfix_info_test;
 import 'get_fixes_test.dart' as get_fixes_test;
 import 'get_postfix_completion_test.dart' as get_postfix_completion_test;
 import 'get_refactoring_test.dart' as get_refactoring_test;
@@ -22,9 +24,11 @@
 
 main() {
   defineReflectiveSuite(() {
+    dartfix_test.main();
     format_test.main();
     get_assists_test.main();
     get_available_refactorings_test.main();
+    get_dartfix_info_test.main();
     get_fixes_test.main();
     get_refactoring_test.main();
     get_postfix_completion_test.main();
diff --git a/pkg/analysis_server/test/integration/execution/set_subscriptions_test.dart b/pkg/analysis_server/test/integration/execution/set_subscriptions_test.dart
index a95fc27..82c3739 100644
--- a/pkg/analysis_server/test/integration/execution/set_subscriptions_test.dart
+++ b/pkg/analysis_server/test/integration/execution/set_subscriptions_test.dart
@@ -17,7 +17,7 @@
 class SetSubscriptionsTest extends AbstractAnalysisServerIntegrationTest {
   test_subscribe() async {
     standardAnalysisSetup();
-    // ignore: deprecated_member_use
+    // ignore: deprecated_member_use_from_same_package
     await sendExecutionSetSubscriptions([ExecutionService.LAUNCH_DATA]);
   }
 }
diff --git a/pkg/analysis_server/test/integration/support/integration_test_methods.dart b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
index 65b4cec..c536416 100644
--- a/pkg/analysis_server/test/integration/support/integration_test_methods.dart
+++ b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
@@ -1056,6 +1056,105 @@
   }
 
   /**
+   * Subscribe for completion services. All previous subscriptions are replaced
+   * by the given set of services.
+   *
+   * It is an error if any of the elements in the list are not valid services.
+   * If there is an error, then the current subscriptions will remain
+   * unchanged.
+   *
+   * Parameters
+   *
+   * subscriptions: List<CompletionService>
+   *
+   *   A list of the services being subscribed to.
+   */
+  Future sendCompletionSetSubscriptions(
+      List<CompletionService> subscriptions) async {
+    var params = new CompletionSetSubscriptionsParams(subscriptions).toJson();
+    var result = await server.send("completion.setSubscriptions", params);
+    outOfTestExpect(result, isNull);
+    return null;
+  }
+
+  /**
+   * The client can make this request to express interest in certain libraries
+   * to receive completion suggestions from based on the client path. If this
+   * request is received before the client has used
+   * 'completion.setSubscriptions' to subscribe to the
+   * AVAILABLE_SUGGESTION_SETS service, then an error of type
+   * NOT_SUBSCRIBED_TO_AVAILABLE_SUGGESTION_SETS will be generated. All
+   * previous paths are replaced by the given set of paths.
+   *
+   * Parameters
+   *
+   * paths: List<LibraryPathSet>
+   *
+   *   A list of objects each containing a path and the additional libraries
+   *   from which the client is interested in receiving completion suggestions.
+   *   If one configured path is beneath another, the descendent will override
+   *   the ancestors' configured libraries of interest.
+   */
+  Future sendCompletionRegisterLibraryPaths(List<LibraryPathSet> paths) async {
+    var params = new CompletionRegisterLibraryPathsParams(paths).toJson();
+    var result = await server.send("completion.registerLibraryPaths", params);
+    outOfTestExpect(result, isNull);
+    return null;
+  }
+
+  /**
+   * Clients must make this request when the user has selected a completion
+   * suggestion from an AvailableSuggestionSet. Analysis server will respond
+   * with the text to insert as well as any SourceChange that needs to be
+   * applied in case the completion requires an additional import to be added.
+   * It is an error if the id is no longer valid, for instance if the library
+   * has been removed after the completion suggestion is accepted.
+   *
+   * Parameters
+   *
+   * file: FilePath
+   *
+   *   The path of the file into which this completion is being inserted.
+   *
+   * id: int
+   *
+   *   The identifier of the AvailableSuggestionSet containing the selected
+   *   label.
+   *
+   * label: String
+   *
+   *   The label from the AvailableSuggestionSet with the `id` for which
+   *   insertion information is requested.
+   *
+   * offset: int
+   *
+   *   The offset in the file where the completion will be inserted.
+   *
+   * Returns
+   *
+   * completion: String
+   *
+   *   The full text to insert, including any optional import prefix.
+   *
+   * change: SourceChange (optional)
+   *
+   *   A change for the client to apply in case the library containing the
+   *   accepted completion suggestion needs to be imported. The field will be
+   *   omitted if there are no additional changes that need to be made.
+   */
+  Future<CompletionGetSuggestionDetailsResult>
+      sendCompletionGetSuggestionDetails(
+          String file, int id, String label, int offset) async {
+    var params =
+        new CompletionGetSuggestionDetailsParams(file, id, label, offset)
+            .toJson();
+    var result = await server.send("completion.getSuggestionDetails", params);
+    ResponseDecoder decoder = new ResponseDecoder(null);
+    return new CompletionGetSuggestionDetailsResult.fromJson(
+        decoder, 'result', result);
+  }
+
+  /**
    * Reports the completion suggestions that should be presented to the user.
    * The set of suggestions included in the notification is always a complete
    * list that supersedes any previously reported suggestions.
@@ -1092,6 +1191,36 @@
    *
    *   True if this is that last set of results that will be returned for the
    *   indicated completion.
+   *
+   * includedSuggestionSets: List<IncludedSuggestionSet> (optional)
+   *
+   *   This field is experimental.
+   *
+   *   References to AvailableSuggestionSet objects previously sent to the
+   *   client. The client can include applicable names from the referenced
+   *   library in code completion suggestions.
+   *
+   * includedSuggestionKinds: List<ElementKind> (optional)
+   *
+   *   This field is experimental.
+   *
+   *   The client is expected to check this list against the ElementKind sent
+   *   in IncludedSuggestionSet to decide whether or not these symbols should
+   *   should be presented to the user.
+   *
+   * includedSuggestionRelevanceTags: List<IncludedSuggestionRelevanceTag>
+   * (optional)
+   *
+   *   This field is experimental.
+   *
+   *   The client is expected to check this list against the values of the
+   *   field relevanceTags of AvailableSuggestion to decide if the suggestion
+   *   should be given a different relevance than the IncludedSuggestionSet
+   *   that contains it. This might be used for example to give higher
+   *   relevance to suggestions of matching types.
+   *
+   *   If an AvailableSuggestion has relevance tags that match more than one
+   *   IncludedSuggestionRelevanceTag, the maximum relevance boost is used.
    */
   Stream<CompletionResultsParams> onCompletionResults;
 
@@ -1101,6 +1230,34 @@
   StreamController<CompletionResultsParams> _onCompletionResults;
 
   /**
+   * Reports the pre-computed, candidate completions from symbols defined in a
+   * corresponding library. This notification may be sent multiple times. When
+   * a notification is processed, clients should replace any previous
+   * information about the libraries in the list of changedLibraries, discard
+   * any information about the libraries in the list of removedLibraries, and
+   * preserve any previously received information about any libraries that are
+   * not included in either list.
+   *
+   * Parameters
+   *
+   * changedLibraries: List<AvailableSuggestionSet> (optional)
+   *
+   *   A list of pre-computed, potential completions coming from this set of
+   *   completion suggestions.
+   *
+   * removedLibraries: List<int> (optional)
+   *
+   *   A list of library ids that no longer apply.
+   */
+  Stream<CompletionAvailableSuggestionsParams> onCompletionAvailableSuggestions;
+
+  /**
+   * Stream controller for [onCompletionAvailableSuggestions].
+   */
+  StreamController<CompletionAvailableSuggestionsParams>
+      _onCompletionAvailableSuggestions;
+
+  /**
    * Perform a search for references to the element defined or referenced at
    * the given offset in the given file.
    *
@@ -1482,11 +1639,38 @@
   }
 
   /**
+   * Request information about edit.dartfix such as the list of known fixes
+   * that can be specified in an edit.dartfix request.
+   *
+   * Parameters
+   *
+   * Returns
+   *
+   * fixes: List<DartFix>
+   *
+   *   A list of fixes that can be specified in an edit.dartfix request.
+   */
+  Future<EditGetDartfixInfoResult> sendEditGetDartfixInfo() async {
+    var params = new EditGetDartfixInfoParams().toJson();
+    var result = await server.send("edit.getDartfixInfo", params);
+    ResponseDecoder decoder = new ResponseDecoder(null);
+    return new EditGetDartfixInfoResult.fromJson(decoder, 'result', result);
+  }
+
+  /**
    * Analyze the specified sources for recommended changes and return a set of
    * suggested edits for those sources. These edits may include changes to
    * sources outside the set of specified sources if a change in a specified
    * source requires it.
    *
+   * If includedFixes is specified, then those fixes will be applied. If
+   * includeRequiredFixes is specified, then "required" fixes will be applied
+   * in addition to whatever fixes are specified in includedFixes if any. If
+   * neither includedFixes nor includeRequiredFixes is specified, then all
+   * fixes will be applied. If excludedFixes is specified, then those fixes
+   * will not be applied regardless of whether they are "required" or specified
+   * in includedFixes.
+   *
    * Parameters
    *
    * included: List<FilePath>
@@ -1501,6 +1685,24 @@
    *   analysis.setAnalysisRoots), an error of type FILE_NOT_ANALYZED will be
    *   generated.
    *
+   * includedFixes: List<String> (optional)
+   *
+   *   A list of names indicating which fixes should be applied.
+   *
+   *   If a name is specified that does not match the name of a known fix, an
+   *   error of type UNKNOWN_FIX will be generated.
+   *
+   * includeRequiredFixes: bool (optional)
+   *
+   *   A flag indicating that "required" fixes should be applied.
+   *
+   * excludedFixes: List<String> (optional)
+   *
+   *   A list of names indicating which fixes should not be applied.
+   *
+   *   If a name is specified that does not match the name of a known fix, an
+   *   error of type UNKNOWN_FIX will be generated.
+   *
    * Returns
    *
    * suggestions: List<DartFixSuggestion>
@@ -1522,8 +1724,15 @@
    *
    *   A list of source edits to apply the recommended changes.
    */
-  Future<EditDartfixResult> sendEditDartfix(List<String> included) async {
-    var params = new EditDartfixParams(included).toJson();
+  Future<EditDartfixResult> sendEditDartfix(List<String> included,
+      {List<String> includedFixes,
+      bool includeRequiredFixes,
+      List<String> excludedFixes}) async {
+    var params = new EditDartfixParams(included,
+            includedFixes: includedFixes,
+            includeRequiredFixes: includeRequiredFixes,
+            excludedFixes: excludedFixes)
+        .toJson();
     var result = await server.send("edit.dartfix", params);
     ResponseDecoder decoder = new ResponseDecoder(null);
     return new EditDartfixResult.fromJson(decoder, 'result', result);
@@ -2459,6 +2668,10 @@
     _onCompletionResults =
         new StreamController<CompletionResultsParams>(sync: true);
     onCompletionResults = _onCompletionResults.stream.asBroadcastStream();
+    _onCompletionAvailableSuggestions =
+        new StreamController<CompletionAvailableSuggestionsParams>(sync: true);
+    onCompletionAvailableSuggestions =
+        _onCompletionAvailableSuggestions.stream.asBroadcastStream();
     _onSearchResults = new StreamController<SearchResultsParams>(sync: true);
     onSearchResults = _onSearchResults.stream.asBroadcastStream();
     _onExecutionLaunchData =
@@ -2555,6 +2768,12 @@
         _onCompletionResults.add(
             new CompletionResultsParams.fromJson(decoder, 'params', params));
         break;
+      case "completion.availableSuggestions":
+        outOfTestExpect(params, isCompletionAvailableSuggestionsParams);
+        _onCompletionAvailableSuggestions.add(
+            new CompletionAvailableSuggestionsParams.fromJson(
+                decoder, 'params', params));
+        break;
       case "search.results":
         outOfTestExpect(params, isSearchResultsParams);
         _onSearchResults
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index 6630dbe..ef2ded9 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -164,6 +164,56 @@
     optionalFields: {"analysisTarget": isString}));
 
 /**
+ * AvailableSuggestion
+ *
+ * {
+ *   "label": String
+ *   "element": Element
+ *   "docComplete": optional String
+ *   "docSummary": optional String
+ *   "parameterNames": optional List<String>
+ *   "parameterTypes": optional List<String>
+ *   "relevanceTags": optional List<AvailableSuggestionRelevanceTag>
+ *   "requiredParameterCount": optional int
+ * }
+ */
+final Matcher isAvailableSuggestion =
+    new LazyMatcher(() => new MatchesJsonObject("AvailableSuggestion", {
+          "label": isString,
+          "element": isElement
+        }, optionalFields: {
+          "docComplete": isString,
+          "docSummary": isString,
+          "parameterNames": isListOf(isString),
+          "parameterTypes": isListOf(isString),
+          "relevanceTags": isListOf(isAvailableSuggestionRelevanceTag),
+          "requiredParameterCount": isInt
+        }));
+
+/**
+ * AvailableSuggestionRelevanceTag
+ *
+ * String
+ */
+final Matcher isAvailableSuggestionRelevanceTag = isString;
+
+/**
+ * AvailableSuggestionSet
+ *
+ * {
+ *   "id": int
+ *   "uri": String
+ *   "items": List<AvailableSuggestion>
+ * }
+ */
+final Matcher isAvailableSuggestionSet = new LazyMatcher(() =>
+    new MatchesJsonObject("AvailableSuggestionSet", {
+      "id": isInt,
+      "uri": isString,
+      "items": isListOf(isAvailableSuggestion)
+    }));
+
+/**
  * ChangeContentOverlay
  *
  * {
@@ -195,6 +245,16 @@
 final Matcher isCompletionId = isString;
 
 /**
+ * CompletionService
+ *
+ * enum {
+ *   AVAILABLE_SUGGESTION_SETS
+ * }
+ */
+final Matcher isCompletionService =
+    new MatchesEnum("CompletionService", ["AVAILABLE_SUGGESTION_SETS"]);
+
+/**
  * CompletionSuggestion
  *
  * {
@@ -300,6 +360,19 @@
         }));
 
 /**
+ * DartFix
+ *
+ * {
+ *   "name": String
+ *   "description": optional String
+ *   "isRequired": optional bool
+ * }
+ */
+final Matcher isDartFix = new LazyMatcher(() => new MatchesJsonObject(
+    "DartFix", {"name": isString},
+    optionalFields: {"description": isString, "isRequired": isBool}));
+
+/**
  * DartFixSuggestion
  *
  * {
@@ -873,6 +946,30 @@
     {"path": isFilePath, "prefix": isString, "elements": isListOf(isString)}));
 
 /**
+ * IncludedSuggestionRelevanceTag
+ *
+ * {
+ *   "tag": AvailableSuggestionRelevanceTag
+ *   "relevanceBoost": int
+ * }
+ */
+final Matcher isIncludedSuggestionRelevanceTag = new LazyMatcher(() =>
+    new MatchesJsonObject("IncludedSuggestionRelevanceTag",
+        {"tag": isAvailableSuggestionRelevanceTag, "relevanceBoost": isInt}));
+
+/**
+ * IncludedSuggestionSet
+ *
+ * {
+ *   "id": int
+ *   "relevance": int
+ * }
+ */
+final Matcher isIncludedSuggestionSet = new LazyMatcher(() =>
+    new MatchesJsonObject(
+        "IncludedSuggestionSet", {"id": isInt, "relevance": isInt}));
+
+/**
  * KytheEntry
  *
  * {
@@ -914,6 +1011,18 @@
         }));
 
 /**
+ * LibraryPathSet
+ *
+ * {
+ *   "scope": FilePath
+ *   "libraryPaths": List<FilePath>
+ * }
+ */
+final Matcher isLibraryPathSet = new LazyMatcher(() => new MatchesJsonObject(
+    "LibraryPathSet",
+    {"scope": isFilePath, "libraryPaths": isListOf(isFilePath)}));
+
+/**
  * LinkedEditGroup
  *
  * {
@@ -1294,6 +1403,7 @@
  *   SERVER_ERROR
  *   SORT_MEMBERS_INVALID_FILE
  *   SORT_MEMBERS_PARSE_ERRORS
+ *   UNKNOWN_FIX
  *   UNKNOWN_REQUEST
  *   UNSUPPORTED_FEATURE
  * }
@@ -1325,6 +1435,7 @@
   "SERVER_ERROR",
   "SORT_MEMBERS_INVALID_FILE",
   "SORT_MEMBERS_PARSE_ERRORS",
+  "UNKNOWN_FIX",
   "UNKNOWN_REQUEST",
   "UNSUPPORTED_FEATURE"
 ]);
@@ -2032,6 +2143,48 @@
 final Matcher isAnalyticsSendTimingResult = isNull;
 
 /**
+ * completion.availableSuggestions params
+ *
+ * {
+ *   "changedLibraries": optional List<AvailableSuggestionSet>
+ *   "removedLibraries": optional List<int>
+ * }
+ */
+final Matcher isCompletionAvailableSuggestionsParams = new LazyMatcher(() =>
+    new MatchesJsonObject("completion.availableSuggestions params", null,
+        optionalFields: {
+          "changedLibraries": isListOf(isAvailableSuggestionSet),
+          "removedLibraries": isListOf(isInt)
+        }));
+
+/**
+ * completion.getSuggestionDetails params
+ *
+ * {
+ *   "file": FilePath
+ *   "id": int
+ *   "label": String
+ *   "offset": int
+ * }
+ */
+final Matcher isCompletionGetSuggestionDetailsParams = new LazyMatcher(() =>
+    new MatchesJsonObject("completion.getSuggestionDetails params",
+        {"file": isFilePath, "id": isInt, "label": isString, "offset": isInt}));
+
+/**
+ * completion.getSuggestionDetails result
+ *
+ * {
+ *   "completion": String
+ *   "change": optional SourceChange
+ * }
+ */
+final Matcher isCompletionGetSuggestionDetailsResult = new LazyMatcher(() =>
+    new MatchesJsonObject(
+        "completion.getSuggestionDetails result", {"completion": isString},
+        optionalFields: {"change": isSourceChange}));
+
+/**
  * completion.getSuggestions params
  *
  * {
@@ -2055,6 +2208,22 @@
         "completion.getSuggestions result", {"id": isCompletionId}));
 
 /**
+ * completion.registerLibraryPaths params
+ *
+ * {
+ *   "paths": List<LibraryPathSet>
+ * }
+ */
+final Matcher isCompletionRegisterLibraryPathsParams = new LazyMatcher(() =>
+    new MatchesJsonObject("completion.registerLibraryPaths params",
+        {"paths": isListOf(isLibraryPathSet)}));
+
+/**
+ * completion.registerLibraryPaths result
+ */
+final Matcher isCompletionRegisterLibraryPathsResult = isNull;
+
+/**
  * completion.results params
  *
  * {
@@ -2063,6 +2232,9 @@
  *   "replacementLength": int
  *   "results": List<CompletionSuggestion>
  *   "isLast": bool
+ *   "includedSuggestionSets": optional List<IncludedSuggestionSet>
+ *   "includedSuggestionKinds": optional List<ElementKind>
+ *   "includedSuggestionRelevanceTags": optional List<IncludedSuggestionRelevanceTag>
  * }
  */
 final Matcher isCompletionResultsParams =
@@ -2072,9 +2244,30 @@
           "replacementLength": isInt,
           "results": isListOf(isCompletionSuggestion),
           "isLast": isBool
+        }, optionalFields: {
+          "includedSuggestionSets": isListOf(isIncludedSuggestionSet),
+          "includedSuggestionKinds": isListOf(isElementKind),
+          "includedSuggestionRelevanceTags":
+              isListOf(isIncludedSuggestionRelevanceTag)
         }));
 
 /**
+ * completion.setSubscriptions params
+ *
+ * {
+ *   "subscriptions": List<CompletionService>
+ * }
+ */
+final Matcher isCompletionSetSubscriptionsParams = new LazyMatcher(() =>
+    new MatchesJsonObject("completion.setSubscriptions params",
+        {"subscriptions": isListOf(isCompletionService)}));
+
+/**
+ * completion.setSubscriptions result
+ */
+final Matcher isCompletionSetSubscriptionsResult = isNull;
+
+/**
  * convertGetterToMethod feedback
  */
 final Matcher isConvertGetterToMethodFeedback = isNull;
@@ -2130,10 +2323,19 @@
  *
  * {
  *   "included": List<FilePath>
+ *   "includedFixes": optional List<String>
+ *   "includeRequiredFixes": optional bool
+ *   "excludedFixes": optional List<String>
  * }
  */
-final Matcher isEditDartfixParams = new LazyMatcher(() => new MatchesJsonObject(
-    "edit.dartfix params", {"included": isListOf(isFilePath)}));
+final Matcher isEditDartfixParams =
+    new LazyMatcher(() => new MatchesJsonObject("edit.dartfix params", {
+          "included": isListOf(isFilePath)
+        }, optionalFields: {
+          "includedFixes": isListOf(isString),
+          "includeRequiredFixes": isBool,
+          "excludedFixes": isListOf(isString)
+        }));
 
 /**
  * edit.dartfix result
@@ -2233,6 +2435,26 @@
         {"kinds": isListOf(isRefactoringKind)}));
 
 /**
+ * edit.getDartfixInfo params
+ *
+ * {
+ * }
+ */
+final Matcher isEditGetDartfixInfoParams = new LazyMatcher(
+    () => new MatchesJsonObject("edit.getDartfixInfo params", null));
+
+/**
+ * edit.getDartfixInfo result
+ *
+ * {
+ *   "fixes": List<DartFix>
+ * }
+ */
+final Matcher isEditGetDartfixInfoResult = new LazyMatcher(() =>
+    new MatchesJsonObject(
+        "edit.getDartfixInfo result", {"fixes": isListOf(isDartFix)}));
+
+/**
  * edit.getFixes params
  *
  * {
diff --git a/pkg/analysis_server/test/lsp/completion_test.dart b/pkg/analysis_server/test/lsp/completion_test.dart
index 23f9b39..26685ec 100644
--- a/pkg/analysis_server/test/lsp/completion_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_test.dart
@@ -195,7 +195,7 @@
     expect(res.any((c) => c.label == 'abcdefghij'), isTrue);
     final item = res.singleWhere((c) => c.label == 'abcdefghij');
     expect(item.insertTextFormat, equals(InsertTextFormat.PlainText));
-    // ignore: deprecated_member_use
+    // ignore: deprecated_member_use_from_same_package
     expect(item.insertText, anyOf(equals('abcdefghij'), isNull));
     final updated = applyTextEdits(withoutMarkers(content), [item.textEdit]);
     expect(updated, contains('a.abcdefghij'));
@@ -219,7 +219,7 @@
     expect(res.any((c) => c.label == 'abcdefghij'), isTrue);
     final item = res.singleWhere((c) => c.label == 'abcdefghij');
     expect(item.insertTextFormat, equals(InsertTextFormat.PlainText));
-    // ignore: deprecated_member_use
+    // ignore: deprecated_member_use_from_same_package
     expect(item.insertText, anyOf(equals('abcdefghij'), isNull));
     final updated = applyTextEdits(withoutMarkers(content), [item.textEdit]);
     expect(updated, contains('a.abcdefghij'));
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index 94c60f2..a1735ec 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -403,6 +403,14 @@
     return expectSuccessfulResponseTo(request);
   }
 
+  Future<List<SymbolInformation>> getWorkspaceSymbols(String query) {
+    final request = makeRequest(
+      Method.workspace_symbol,
+      new WorkspaceSymbolParams(query),
+    );
+    return expectSuccessfulResponseTo(request);
+  }
+
   Future<List<FoldingRange>> getFoldingRegions(Uri uri) {
     final request = makeRequest(
       Method.textDocument_foldingRange,
diff --git a/pkg/analysis_server/test/lsp/test_all.dart b/pkg/analysis_server/test/lsp/test_all.dart
index 2745a6b..bdd25b3 100644
--- a/pkg/analysis_server/test/lsp/test_all.dart
+++ b/pkg/analysis_server/test/lsp/test_all.dart
@@ -15,6 +15,7 @@
 import 'document_highlights_test.dart' as document_highlights_test;
 import 'document_symbols_test.dart' as document_symbols_test;
 import 'file_modification_test.dart' as file_modification_test;
+import 'folding_test.dart' as folding_test;
 import 'format_test.dart' as format_test;
 import 'hover_test.dart' as hover_test;
 import 'initialization_test.dart' as initialization_test;
@@ -23,7 +24,7 @@
 import 'rename_test.dart' as rename_test;
 import 'server_test.dart' as server_test;
 import 'signature_help_test.dart' as signature_help_test;
-import 'folding_test.dart' as folding_test;
+import 'workspace_symbols_test.dart' as workspace_symbols_test;
 
 main() {
   defineReflectiveSuite(() {
@@ -46,5 +47,6 @@
     packet_transformer_tests.main();
     rename_test.main();
     folding_test.main();
+    workspace_symbols_test.main();
   }, name: 'lsp');
 }
diff --git a/pkg/analysis_server/test/lsp/workspace_symbols_test.dart b/pkg/analysis_server/test/lsp/workspace_symbols_test.dart
new file mode 100644
index 0000000..72cad45
--- /dev/null
+++ b/pkg/analysis_server/test/lsp/workspace_symbols_test.dart
@@ -0,0 +1,107 @@
+// Copyright (c) 2019, 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:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'server_abstract.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(WorkspaceSymbolsTest);
+  });
+}
+
+@reflectiveTest
+class WorkspaceSymbolsTest extends AbstractLspAnalysisServerTest {
+  test_fullMatch() async {
+    const content = '''
+    [[String topLevel = '']];
+    class MyClass {
+      int myField;
+      MyClass(this.myField);
+      myMethod() {}
+    }
+    ''';
+    newFile(mainFilePath, content: withoutMarkers(content));
+    await initialize();
+
+    final symbols = await getWorkspaceSymbols('topLevel');
+
+    final topLevel = symbols.firstWhere((s) => s.name == 'topLevel');
+    expect(topLevel.kind, equals(SymbolKind.Variable));
+    expect(topLevel.containerName, isNull);
+    expect(topLevel.location.uri, equals(mainFileUri.toString()));
+    expect(topLevel.location.range, equals(rangeFromMarkers(content)));
+
+    // Ensure we didn't get some things that definitely do not match.
+    expect(symbols.any((s) => s.name == 'MyClass'), isFalse);
+    expect(symbols.any((s) => s.name == 'myMethod'), isFalse);
+  }
+
+  test_fuzzyMatch() async {
+    const content = '''
+    String topLevel = '';
+    class MyClass {
+      [[int myField]];
+      MyClass(this.myField);
+      myMethod() {}
+    }
+    ''';
+    newFile(mainFilePath, content: withoutMarkers(content));
+    await initialize();
+
+    // meld should match myField
+    final symbols = await getWorkspaceSymbols('meld');
+
+    final field = symbols.firstWhere((s) => s.name == 'myField');
+    expect(field.kind, equals(SymbolKind.Field));
+    expect(field.containerName, equals('MyClass'));
+    expect(field.location.uri, equals(mainFileUri.toString()));
+    expect(field.location.range, equals(rangeFromMarkers(content)));
+
+    // Ensure we didn't get some things that definitely do not match.
+    expect(symbols.any((s) => s.name == 'MyClass'), isFalse);
+    expect(symbols.any((s) => s.name == 'myMethod'), isFalse);
+  }
+
+  test_partialMatch() async {
+    const content = '''
+    String topLevel = '';
+    class MyClass {
+      [[int myField]];
+      MyClass(this.myField);
+      [[myMethod() {}]]
+    }
+    ''';
+    newFile(mainFilePath, content: withoutMarkers(content));
+    await initialize();
+
+    final symbols = await getWorkspaceSymbols('my');
+    final ranges = rangesFromMarkers(content);
+    final fieldRange = ranges[0];
+    final methodRange = ranges[1];
+
+    final field = symbols.firstWhere((s) => s.name == 'myField');
+    expect(field.kind, equals(SymbolKind.Field));
+    expect(field.containerName, equals('MyClass'));
+    expect(field.location.uri, equals(mainFileUri.toString()));
+    expect(field.location.range, equals(fieldRange));
+
+    final klass = symbols.firstWhere((s) => s.name == 'MyClass');
+    expect(klass.kind, equals(SymbolKind.Class));
+    expect(klass.containerName, isNull);
+    expect(klass.location.uri, equals(mainFileUri.toString()));
+
+    final method = symbols.firstWhere((s) => s.name == 'myMethod');
+    expect(method.kind, equals(SymbolKind.Method));
+    expect(method.containerName, equals('MyClass'));
+    expect(method.location.uri, equals(mainFileUri.toString()));
+    expect(method.location.range, equals(methodRange));
+
+    // Ensure we didn't get some things that definitely do not match.
+    expect(symbols.any((s) => s.name == 'topLevel'), isFalse);
+  }
+}
diff --git a/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
index cedde60..9d9a40d 100644
--- a/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
@@ -41,23 +41,6 @@
     expect(suggestion.selectionLength, selectionLength);
   }
 
-  void assertSuggestArgumentList(
-      List<String> paramNames, List<String> paramTypes) {
-    // DEPRECATED... argument lists are no longer suggested.
-    // See https://github.com/dart-lang/sdk/issues/25197
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
-
-    // CompletionSuggestionKind csKind = CompletionSuggestionKind.ARGUMENT_LIST;
-    // CompletionSuggestion cs = getSuggest(csKind: csKind);
-    // if (cs == null) {
-    //   failedCompletion('expected completion $csKind', suggestions);
-    // }
-    // assertSuggestArgumentList_params(
-    //     paramNames, paramTypes, cs.parameterNames, cs.parameterTypes);
-    // expect(cs.relevance, DART_RELEVANCE_HIGH);
-    // assertNoOtherSuggestions([cs]);
-  }
-
   void assertSuggestArgumentList_params(
       List<String> expectedNames,
       List<String> expectedTypes,
@@ -531,54 +514,6 @@
     assertNoSuggestions();
   }
 
-  test_ArgumentList_imported_function_1() async {
-    // ArgumentList  MethodInvocation  ExpressionStatement  Block
-    addSource('/home/test/lib/a.dart', '''
-      library A;
-      bool hasLength(int expected) { }
-      expect(String arg) { }
-      void baz() { }''');
-    addTestSource('''
-      import 'a.dart'
-      class B { }
-      String bar() => true;
-      void main() {expect(^)}''');
-    await computeSuggestions();
-    assertSuggestArgumentList(['arg'], ['String']);
-  }
-
-  test_ArgumentList_imported_function_2() async {
-    // ArgumentList  MethodInvocation  ExpressionStatement  Block
-    addSource('/home/test/lib/a.dart', '''
-      library A;
-      bool hasLength(int expected) { }
-      expect(String arg1, int arg2) { }
-      void baz() { }''');
-    addTestSource('''
-      import 'a.dart'
-      class B { }
-      String bar() => true;
-      void main() {expect(^)}''');
-    await computeSuggestions();
-    assertSuggestArgumentList(['arg1', 'arg2'], ['String', 'int']);
-  }
-
-  test_ArgumentList_imported_function_3() async {
-    // ArgumentList  MethodInvocation  ExpressionStatement  Block
-    addSource('/home/test/lib/a.dart', '''
-      library A;
-      bool hasLength(int expected) { }
-      expect(String arg1, int arg2, {bool arg3}) { }
-      void baz() { }''');
-    addTestSource('''
-      import 'a.dart'
-      class B { }
-      String bar() => true;
-      void main() {expect(^)}''');
-    await computeSuggestions();
-    assertSuggestArgumentList(['arg1', 'arg2'], ['String', 'int']);
-  }
-
   test_ArgumentList_imported_function_3a() async {
     // ArgumentList  MethodInvocation  ExpressionStatement  Block
     addSource('/home/test/lib/a.dart', '''
@@ -901,39 +836,6 @@
         requiredParamIndices: [1]);
   }
 
-  test_ArgumentList_local_function_1() async {
-    // ArgumentList  MethodInvocation  ExpressionStatement  Block
-    addTestSource('''
-      expect(arg) { }
-      class B { }
-      String bar() => true;
-      void main() {expect(^)}''');
-    await computeSuggestions();
-    assertSuggestArgumentList(['arg'], ['dynamic']);
-  }
-
-  test_ArgumentList_local_function_2() async {
-    // ArgumentList  MethodInvocation  ExpressionStatement  Block
-    addTestSource('''
-      expect(arg1, int arg2) { }
-      class B { }
-      String bar() => true;
-      void main() {expect(^)}''');
-    await computeSuggestions();
-    assertSuggestArgumentList(['arg1', 'arg2'], ['dynamic', 'int']);
-  }
-
-  test_ArgumentList_local_function_3() async {
-    // ArgumentList  MethodInvocation  ExpressionStatement  Block
-    addTestSource('''
-      expect(arg1, int arg2) { }
-      class B { }
-      String bar() => true;
-      void main() {expect(^)}''');
-    await computeSuggestions();
-    assertSuggestArgumentList(['arg1', 'arg2'], ['dynamic', 'int']);
-  }
-
   test_ArgumentList_local_function_3a() async {
     // ArgumentList  MethodInvocation  ExpressionStatement  Block
     addTestSource('''
@@ -1032,20 +934,4 @@
     await computeSuggestions();
     assertNoSuggestions();
   }
-
-  test_ArgumentList_local_method_2() async {
-    // ArgumentList  MethodInvocation  ExpressionStatement  Block
-    addSource('/home/test/lib/a.dart', '''
-      library A;
-      bool hasLength(int expected) { }
-      void baz() { }''');
-    addTestSource('''
-      import 'a.dart'
-      class B {
-        expect(arg, int blat) { }
-        void foo() {expect(^)}}
-      String bar() => true;''');
-    await computeSuggestions();
-    assertSuggestArgumentList(['arg', 'blat'], ['dynamic', 'int']);
-  }
 }
diff --git a/pkg/analysis_server/test/services/completion/dart/common_usage_sorter_test.dart b/pkg/analysis_server/test/services/completion/dart/common_usage_sorter_test.dart
index a1ba82c..82d9e55 100644
--- a/pkg/analysis_server/test/services/completion/dart/common_usage_sorter_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/common_usage_sorter_test.dart
@@ -86,7 +86,7 @@
     // SimpleIdentifier  PrefixedIdentifier  ExpressionStatement
     newFile('/project/bin/myLib.dart',
         content:
-            'library L; part "${convertAbsolutePathToUri(testFile)}"; class A {static int s2;}');
+            'library L; part "${toUriStr(testFile)}"; class A {static int s2;}');
     addTestFile('part of L; foo() {A.^}');
     await getSuggestionsWith({
       'L.A': ['s2']
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
index 6e09724..6c5a310 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
@@ -10,7 +10,6 @@
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
 import 'package:analysis_server/src/services/completion/dart/imported_reference_contributor.dart';
 import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/src/task/dart.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -52,8 +51,7 @@
         new CompletionPerformance());
     Completer<DartCompletionRequest> requestCompleter =
         new Completer<DartCompletionRequest>();
-    DartCompletionRequestImpl.from(baseRequest,
-            resultDescriptor: RESOLVED_UNIT1)
+    DartCompletionRequestImpl.from(baseRequest)
         .then((DartCompletionRequest request) {
       requestCompleter.complete(request);
     });
diff --git a/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
index 1bbf623..0c2b22f 100644
--- a/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
@@ -63,7 +63,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertSuggestFunction('hasLength', 'bool');
     assertSuggestFunction('identical', 'bool');
@@ -90,7 +89,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertSuggestFunction('hasLength', 'bool');
     assertSuggestFunction('identical', 'bool');
@@ -118,7 +116,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertSuggestFunction('hasLength', 'bool',
         kind: CompletionSuggestionKind.IDENTIFIER);
@@ -150,7 +147,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertSuggestFunction('hasLength', 'bool',
         kind: CompletionSuggestionKind.IDENTIFIER);
@@ -180,7 +176,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertSuggestFunction('hasLength', 'bool');
     assertSuggestFunction('identical', 'bool');
@@ -207,7 +202,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertSuggestFunction('hasLength', 'bool');
     assertSuggestFunction('identical', 'bool');
@@ -235,7 +229,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertSuggestFunction('hasLength', 'bool',
         kind: CompletionSuggestionKind.IDENTIFIER);
@@ -265,7 +258,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertSuggestFunction('hasLength', 'bool',
         kind: CompletionSuggestionKind.IDENTIFIER);
     assertSuggestFunction('identical', 'bool',
diff --git a/pkg/analysis_server/test/services/completion/dart/local_constructor_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/local_constructor_contributor_test.dart
index 36a37e9..b0e10ba 100644
--- a/pkg/analysis_server/test/services/completion/dart/local_constructor_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/local_constructor_contributor_test.dart
@@ -87,7 +87,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -118,7 +117,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -150,7 +148,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -184,7 +181,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -216,7 +212,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -247,7 +242,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -279,7 +273,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -311,7 +304,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
     if (suggestConstructorsWithoutNew) {
diff --git a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
index d484f79..3844a66 100644
--- a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
@@ -5,7 +5,6 @@
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/local_reference_contributor.dart';
-import 'package:analyzer/file_system/memory_file_system.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -58,6 +57,20 @@
     return cs;
   }
 
+  CompletionSuggestion assertSuggestTypeParameter(String name,
+      {int relevance: DART_RELEVANCE_TYPE_PARAMETER}) {
+    CompletionSuggestion cs = assertSuggest(name,
+        csKind: CompletionSuggestionKind.IDENTIFIER, relevance: relevance);
+    expect(cs.returnType, isNull);
+    Element element = cs.element;
+    expect(element, isNotNull);
+    expect(element.kind, equals(ElementKind.TYPE_PARAMETER));
+    expect(element.name, equals(name));
+    expect(element.parameters, isNull);
+    expect(element.returnType, isNull);
+    return cs;
+  }
+
   @override
   DartCompletionContributor createContributor() {
     return new LocalReferenceContributor();
@@ -152,7 +165,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertSuggestFunction('bar', 'String',
         relevance: DART_RELEVANCE_LOCAL_FUNCTION);
     assertNotSuggested('hasLength');
@@ -180,7 +192,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertSuggestFunction('bar', 'String',
         relevance: DART_RELEVANCE_LOCAL_FUNCTION);
     assertNotSuggested('hasLength');
@@ -209,7 +220,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertSuggestFunction('bar', 'String',
         kind: CompletionSuggestionKind.IDENTIFIER,
         relevance: DART_RELEVANCE_LOCAL_FUNCTION);
@@ -241,7 +251,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertSuggestFunction('bar', 'String',
         kind: CompletionSuggestionKind.IDENTIFIER,
         relevance: DART_RELEVANCE_LOCAL_FUNCTION);
@@ -271,7 +280,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertSuggestFunction('bar', 'String',
         relevance: DART_RELEVANCE_LOCAL_FUNCTION);
     assertNotSuggested('hasLength');
@@ -299,7 +307,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertSuggestFunction('bar', 'String',
         relevance: DART_RELEVANCE_LOCAL_FUNCTION);
     assertNotSuggested('hasLength');
@@ -328,7 +335,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertSuggestFunction('bar', 'String',
         kind: CompletionSuggestionKind.IDENTIFIER,
         relevance: DART_RELEVANCE_LOCAL_FUNCTION);
@@ -362,7 +368,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertSuggestFunction('bar', 'String',
         kind: CompletionSuggestionKind.IDENTIFIER,
         relevance:
@@ -397,7 +402,6 @@
 
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
     assertSuggestClass('B', kind: CompletionSuggestionKind.IDENTIFIER);
@@ -2302,6 +2306,108 @@
     assertSuggestEnumConst('F.four');
   }
 
+  test_expression_localVariable() async {
+    addTestSource('''
+void f() {
+  var v = 0;
+  ^
+}
+''');
+    await computeSuggestions();
+    assertSuggestLocalVariable('v', 'int');
+  }
+
+  test_expression_parameter() async {
+    addTestSource('''
+void f(int a) {
+  ^
+}
+''');
+    await computeSuggestions();
+    assertSuggestParameter('a', 'int');
+  }
+
+  test_expression_typeParameter_classDeclaration() async {
+    addTestSource('''
+class A<T> {
+  void m() {
+    ^
+  }
+}
+class B<U> {}
+''');
+    await computeSuggestions();
+    assertSuggestTypeParameter('T');
+    assertNotSuggested('U');
+  }
+
+  test_expression_typeParameter_classTypeAlias() async {
+    addTestSource('''
+class A<U> {}
+class B<T> = A<^>;
+''');
+    await computeSuggestions();
+    assertSuggestTypeParameter('T');
+    assertNotSuggested('U');
+  }
+
+  test_expression_typeParameter_functionDeclaration() async {
+    addTestSource('''
+void f<T>() {
+  ^
+}
+void g<U>() {}
+''');
+    await computeSuggestions();
+    assertSuggestTypeParameter('T');
+    assertNotSuggested('U');
+  }
+
+  test_expression_typeParameter_functionDeclaration_local() async {
+    addTestSource('''
+void f() {
+  void g2<U>() {}
+  void g<T>() {
+    ^
+  }
+}
+''');
+    await computeSuggestions();
+    assertSuggestTypeParameter('T');
+    assertNotSuggested('U');
+  }
+
+  test_expression_typeParameter_functionTypeAlias() async {
+    addTestSource('''
+typedef void F<T>(^);
+''');
+    await computeSuggestions();
+    assertSuggestTypeParameter('T');
+  }
+
+  test_expression_typeParameter_genericTypeAlias() async {
+    addTestSource('''
+typedef F<T> = void Function<U>(^);
+''');
+    await computeSuggestions();
+    assertSuggestTypeParameter('T');
+    assertSuggestTypeParameter('U');
+  }
+
+  test_expression_typeParameter_methodDeclaration() async {
+    addTestSource('''
+class A {
+  void m<T>() {
+    ^
+  }
+  void m2<U>() {}
+}
+''');
+    await computeSuggestions();
+    assertSuggestTypeParameter('T');
+    assertNotSuggested('U');
+  }
+
   test_ExpressionStatement_identifier() async {
     // SimpleIdentifier  ExpressionStatement  Block
     addSource('/home/test/lib/a.dart', '''
@@ -4601,6 +4707,16 @@
     assertNoSuggestions();
   }
 
+  test_type_typeParameter_classDeclaration() async {
+    addTestSource('''
+class A<T> {
+  ^ m() {}
+}
+''');
+    await computeSuggestions();
+    assertSuggestTypeParameter('T');
+  }
+
   test_TypeArgumentList() async {
     // SimpleIdentifier  BinaryExpression  ExpressionStatement
     addSource('/home/test/lib/a.dart', '''
diff --git a/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
index 076ae44..da7ec8f 100644
--- a/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
@@ -246,6 +246,21 @@
         selectionLength: 22);
   }
 
+  test_outsideOfWorkspace() async {
+    testFile = convertPath('/home/other/lib/a.dart');
+    addTestSource('''
+class A {
+  void foo() {}
+}
+
+class B extends A {
+  f^
+}
+''');
+    await computeSuggestions();
+    _assertNoOverrideContaining('foo');
+  }
+
   test_private_otherLibrary() async {
     addSource('/home/test/lib/a.dart', '''
 class A {
diff --git a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
index 5fd129a..c3712b1 100644
--- a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
@@ -125,7 +125,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -151,7 +150,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -178,7 +176,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -207,7 +204,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -234,7 +230,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -260,7 +255,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -287,7 +281,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('bar');
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
@@ -314,7 +307,6 @@
     await computeSuggestions();
     expect(replacementOffset, completionOffset);
     expect(replacementLength, 0);
-    assertNoSuggestions(kind: CompletionSuggestionKind.ARGUMENT_LIST);
     assertNotSuggested('hasLength');
     assertNotSuggested('identical');
     assertNotSuggested('B');
@@ -2914,6 +2906,30 @@
     assertNotSuggested('==');
   }
 
+  test_mixin() async {
+    addTestSource(r'''
+class A {
+  void a() {}
+}
+
+class B {
+  void b() {}
+}
+
+mixin X on A, B {
+  void x() {}
+}
+
+void f(X x) {
+  x.^
+}
+''');
+    await computeSuggestions();
+    assertSuggestMethod('a', 'A', 'void');
+    assertSuggestMethod('b', 'B', 'void');
+    assertSuggestMethod('x', 'X', 'void');
+  }
+
   test_new_instance() async {
     addTestSource('import "dart:math"; class A {x() {new Random().^}}');
     await computeSuggestions();
diff --git a/pkg/analysis_server/test/services/refactoring/move_file_test.dart b/pkg/analysis_server/test/services/refactoring/move_file_test.dart
index fa60f61..59f07b6 100644
--- a/pkg/analysis_server/test/services/refactoring/move_file_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/move_file_test.dart
@@ -37,7 +37,7 @@
 import '22/c.dart';
 export '333/d.dart';
 part 'a.dart';
-part '${convertAbsolutePathToUri('/absolute/uri.dart')}';
+part '${toUriStr('/absolute/uri.dart')}';
 ''');
     // perform refactoring
     _createRefactoring('/home/test/000/1111/22/new_name.dart');
@@ -51,7 +51,7 @@
 import 'c.dart';
 export '../333/d.dart';
 part '../a.dart';
-part '${convertAbsolutePathToUri('/absolute/uri.dart')}';
+part '${toUriStr('/absolute/uri.dart')}';
 ''');
   }
 
diff --git a/pkg/analysis_server/test/services/refactoring/rename_local_test.dart b/pkg/analysis_server/test/services/refactoring/rename_local_test.dart
index 042fdcc..14f3924 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_local_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_local_test.dart
@@ -488,12 +488,12 @@
     await indexUnit('/home/test/lib/test2.dart', '''
 library test2;
 class A {
-  void foo({int test: 1}) {
+  void foo({int test}) {
     print(test);
   }
 }
 class B extends A {
-  void foo({int test: 2}) {
+  void foo({int test}) {
     print(test);
   }
 }
@@ -506,7 +506,7 @@
   new C().foo(test: 30);
 }
 class C extends A {
-  void foo({int test: 3}) {
+  void foo({int test}) {
     print(test);
   }
 }
@@ -524,7 +524,7 @@
   new C().foo(newName: 30);
 }
 class C extends A {
-  void foo({int newName: 3}) {
+  void foo({int newName}) {
     print(newName);
   }
 }
@@ -532,12 +532,12 @@
     assertFileChangeResult('/home/test/lib/test2.dart', '''
 library test2;
 class A {
-  void foo({int newName: 1}) {
+  void foo({int newName}) {
     print(newName);
   }
 }
 class B extends A {
-  void foo({int newName: 2}) {
+  void foo({int newName}) {
     print(newName);
   }
 }
diff --git a/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart b/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
new file mode 100644
index 0000000..ecf6d0cd
--- /dev/null
+++ b/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
@@ -0,0 +1,257 @@
+// Copyright (c) 2019, 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/protocol_server.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'available_suggestions_base.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(AvailableSuggestionSetsTest);
+  });
+}
+
+@reflectiveTest
+class AvailableSuggestionSetsTest extends AvailableSuggestionsBase {
+  test_notifications_whenFileChanges() async {
+    var path = convertPath('/home/test/lib/a.dart');
+    var uriStr = 'package:test/a.dart';
+
+    // No file initially, so no set.
+    expect(uriToSetMap.keys, isNot(contains(uriStr)));
+
+    // Create the file, should get the set.
+    {
+      newFile(path, content: r'''
+class A {}
+''');
+      var set = await waitForSetWithUri(uriStr);
+      expect(set.items.map((d) => d.label), contains('A'));
+    }
+
+    // Update the file, should get the updated set.
+    {
+      newFile(path, content: r'''
+class B {}
+''');
+      removeSet(uriStr);
+      var set = await waitForSetWithUri(uriStr);
+      expect(set.items.map((d) => d.label), contains('B'));
+    }
+
+    // Delete the file, the set should be removed.
+    deleteFile(path);
+    waitForSetWithUriRemoved(uriStr);
+  }
+
+  test_suggestion_class() async {
+    var path = convertPath('/home/test/lib/a.dart');
+    var uriStr = 'package:test/a.dart';
+
+    newFile(path, content: r'''
+class A {}
+''');
+
+    var set = await waitForSetWithUri(uriStr);
+    assertJsonText(_getSuggestion(set, 'A'), '''
+{
+  "label": "A",
+  "element": {
+    "kind": "CLASS",
+    "name": "A",
+    "location": {
+      "file": ${jsonOfPath(path)},
+      "offset": 6,
+      "length": 0,
+      "startLine": 1,
+      "startColumn": 7
+    },
+    "flags": 0
+  },
+  "relevanceTags": [
+    "package:test/a.dart::A"
+  ]
+}
+''');
+  }
+
+  test_suggestion_enum() async {
+    var path = convertPath('/home/test/lib/a.dart');
+    var uriStr = 'package:test/a.dart';
+
+    newFile(path, content: r'''
+enum MyEnum {
+  aaa,
+  bbb,
+}
+''');
+
+    var set = await waitForSetWithUri(uriStr);
+    assertJsonText(_getSuggestion(set, 'MyEnum'), '''
+{
+  "label": "MyEnum",
+  "element": {
+    "kind": "ENUM",
+    "name": "MyEnum",
+    "location": {
+      "file": ${jsonOfPath(path)},
+      "offset": 5,
+      "length": 0,
+      "startLine": 1,
+      "startColumn": 6
+    },
+    "flags": 0
+  },
+  "relevanceTags": [
+    "package:test/a.dart::MyEnum"
+  ]
+}
+''');
+    assertJsonText(_getSuggestion(set, 'MyEnum.aaa'), '''
+{
+  "label": "MyEnum.aaa",
+  "element": {
+    "kind": "ENUM_CONSTANT",
+    "name": "aaa",
+    "location": {
+      "file": ${jsonOfPath(path)},
+      "offset": 16,
+      "length": 0,
+      "startLine": 2,
+      "startColumn": 3
+    },
+    "flags": 0
+  },
+  "relevanceTags": [
+    "package:test/a.dart::MyEnum"
+  ]
+}
+''');
+    assertJsonText(_getSuggestion(set, 'MyEnum.bbb'), '''
+{
+  "label": "MyEnum.bbb",
+  "element": {
+    "kind": "ENUM_CONSTANT",
+    "name": "bbb",
+    "location": {
+      "file": ${jsonOfPath(path)},
+      "offset": 23,
+      "length": 0,
+      "startLine": 3,
+      "startColumn": 3
+    },
+    "flags": 0
+  },
+  "relevanceTags": [
+    "package:test/a.dart::MyEnum"
+  ]
+}
+''');
+  }
+
+  test_suggestion_topLevelVariable() async {
+    var path = convertPath('/home/test/lib/a.dart');
+    var uriStr = 'package:test/a.dart';
+
+    newFile(path, content: r'''
+var boolV = false;
+var intV = 0;
+var doubleV = 0.1;
+var stringV = 'hi';
+''');
+
+    var set = await waitForSetWithUri(uriStr);
+    assertJsonText(_getSuggestion(set, 'boolV'), '''
+{
+  "label": "boolV",
+  "element": {
+    "kind": "TOP_LEVEL_VARIABLE",
+    "name": "boolV",
+    "location": {
+      "file": ${jsonOfPath(path)},
+      "offset": 4,
+      "length": 0,
+      "startLine": 1,
+      "startColumn": 5
+    },
+    "flags": 0,
+    "returnType": ""
+  },
+  "relevanceTags": [
+    "dart:core::bool"
+  ]
+}
+''');
+    assertJsonText(_getSuggestion(set, 'intV'), '''
+{
+  "label": "intV",
+  "element": {
+    "kind": "TOP_LEVEL_VARIABLE",
+    "name": "intV",
+    "location": {
+      "file": ${jsonOfPath(path)},
+      "offset": 23,
+      "length": 0,
+      "startLine": 2,
+      "startColumn": 5
+    },
+    "flags": 0,
+    "returnType": ""
+  },
+  "relevanceTags": [
+    "dart:core::int"
+  ]
+}
+''');
+    assertJsonText(_getSuggestion(set, 'doubleV'), '''
+{
+  "label": "doubleV",
+  "element": {
+    "kind": "TOP_LEVEL_VARIABLE",
+    "name": "doubleV",
+    "location": {
+      "file": ${jsonOfPath(path)},
+      "offset": 37,
+      "length": 0,
+      "startLine": 3,
+      "startColumn": 5
+    },
+    "flags": 0,
+    "returnType": ""
+  },
+  "relevanceTags": [
+    "dart:core::double"
+  ]
+}
+''');
+    assertJsonText(_getSuggestion(set, 'stringV'), '''
+{
+  "label": "stringV",
+  "element": {
+    "kind": "TOP_LEVEL_VARIABLE",
+    "name": "stringV",
+    "location": {
+      "file": ${jsonOfPath(path)},
+      "offset": 56,
+      "length": 0,
+      "startLine": 4,
+      "startColumn": 5
+    },
+    "flags": 0,
+    "returnType": ""
+  },
+  "relevanceTags": [
+    "dart:core::String"
+  ]
+}
+''');
+  }
+
+  static AvailableSuggestion _getSuggestion(
+      AvailableSuggestionSet set, String label) {
+    return set.items.singleWhere((s) => s.label == label);
+  }
+}
diff --git a/pkg/analysis_server/test/src/domains/completion/available_suggestions_base.dart b/pkg/analysis_server/test/src/domains/completion/available_suggestions_base.dart
new file mode 100644
index 0000000..97064f5
--- /dev/null
+++ b/pkg/analysis_server/test/src/domains/completion/available_suggestions_base.dart
@@ -0,0 +1,122 @@
+// Copyright (c) 2019, 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 'dart:convert';
+
+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/domain_completion.dart';
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../analysis_abstract.dart';
+import '../../../constants.dart';
+
+@reflectiveTest
+class AvailableSuggestionsBase extends AbstractAnalysisTest {
+  final Map<int, AvailableSuggestionSet> idToSetMap = {};
+  final Map<String, AvailableSuggestionSet> uriToSetMap = {};
+  final Map<String, CompletionResultsParams> idToSuggestions = {};
+
+  void assertJsonText(Object object, String expected) {
+    expected = expected.trimRight();
+    var actual = JsonEncoder.withIndent('  ').convert(object);
+    if (actual != expected) {
+      print('-----');
+      print(actual);
+      print('-----');
+    }
+    expect(actual, expected);
+  }
+
+  String jsonOfPath(String path) {
+    path = convertPath(path);
+    return json.encode(path);
+  }
+
+  @override
+  void processNotification(Notification notification) {
+    super.processNotification(notification);
+    if (notification.event == COMPLETION_NOTIFICATION_AVAILABLE_SUGGESTIONS) {
+      var params = CompletionAvailableSuggestionsParams.fromNotification(
+        notification,
+      );
+      for (var set in params.changedLibraries) {
+        idToSetMap[set.id] = set;
+        uriToSetMap[set.uri] = set;
+      }
+      for (var id in params.removedLibraries) {
+        var set = idToSetMap.remove(id);
+        uriToSetMap.remove(set?.uri);
+      }
+    } else if (notification.event == COMPLETION_RESULTS) {
+      var params = CompletionResultsParams.fromNotification(notification);
+      idToSuggestions[params.id] = params;
+    }
+  }
+
+  /// Remove the set with the given [uri].
+  /// The set must be already received.
+  void removeSet(String uri) {
+    var set = uriToSetMap.remove(uri);
+    expect(set, isNotNull);
+
+    idToSetMap.remove(set.id);
+  }
+
+  @override
+  void setUp() {
+    super.setUp();
+    projectPath = convertPath('/home');
+    testFile = convertPath('/home/test/lib/test.dart');
+
+    newFile('/home/test/pubspec.yaml', content: '');
+    newFile('/home/test/.packages', content: '''
+test:${toUri('/home/test/lib')}
+''');
+
+    createProject();
+    handler = server.handlers.whereType<CompletionDomainHandler>().single;
+    _setCompletionSubscriptions([CompletionService.AVAILABLE_SUGGESTION_SETS]);
+  }
+
+  Future<CompletionResultsParams> waitForGetSuggestions(String id) async {
+    while (true) {
+      var result = idToSuggestions[id];
+      if (result != null) {
+        return result;
+      }
+      await Future.delayed(const Duration(milliseconds: 1));
+    }
+  }
+
+  Future<AvailableSuggestionSet> waitForSetWithUri(String uri) async {
+    while (true) {
+      var result = uriToSetMap[uri];
+      if (result != null) {
+        return result;
+      }
+      await Future.delayed(const Duration(milliseconds: 1));
+    }
+  }
+
+  Future<void> waitForSetWithUriRemoved(String uri) async {
+    while (true) {
+      var result = uriToSetMap[uri];
+      if (result == null) {
+        return;
+      }
+      await Future.delayed(const Duration(milliseconds: 1));
+    }
+  }
+
+  void _setCompletionSubscriptions(List<CompletionService> subscriptions) {
+    handleSuccessfulRequest(
+      CompletionSetSubscriptionsParams(subscriptions).toRequest('0'),
+    );
+  }
+}
diff --git a/pkg/analysis_server/test/src/domains/completion/get_suggestion_details_test.dart b/pkg/analysis_server/test/src/domains/completion/get_suggestion_details_test.dart
new file mode 100644
index 0000000..84fdccb
--- /dev/null
+++ b/pkg/analysis_server/test/src/domains/completion/get_suggestion_details_test.dart
@@ -0,0 +1,136 @@
+// Copyright (c) 2019, 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_generated.dart';
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:meta/meta.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'available_suggestions_base.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(GetSuggestionDetailsTest);
+  });
+}
+
+@reflectiveTest
+class GetSuggestionDetailsTest extends AvailableSuggestionsBase {
+  test_enum() async {
+    newFile('/home/test/lib/a.dart', content: r'''
+enum MyEnum {
+  aaa, bbb
+}
+''');
+    addTestFile(r'''
+main() {} // ref
+''');
+
+    var set = await waitForSetWithUri('package:test/a.dart');
+    var result = await _getSuggestionDetails(
+      id: set.id,
+      label: 'MyEnum.aaa',
+      offset: testCode.indexOf('} // ref'),
+    );
+
+    expect(result.completion, 'MyEnum.aaa');
+    _assertTestFileChange(result.change, r'''
+import 'package:test/a.dart';
+
+main() {} // ref
+''');
+  }
+
+  test_existingImport() async {
+    addTestFile(r'''
+import 'dart:math';
+
+main() {} // ref
+''');
+
+    var mathSet = await waitForSetWithUri('dart:math');
+    var result = await _getSuggestionDetails(
+      id: mathSet.id,
+      label: 'sin',
+      offset: testCode.indexOf('} // ref'),
+    );
+
+    expect(result.completion, 'sin');
+    _assertEmptyChange(result.change);
+  }
+
+  test_existingImport_prefixed() async {
+    addTestFile(r'''
+import 'dart:math' as math;
+
+main() {} // ref
+''');
+
+    var mathSet = await waitForSetWithUri('dart:math');
+    var result = await _getSuggestionDetails(
+      id: mathSet.id,
+      label: 'sin',
+      offset: testCode.indexOf('} // ref'),
+    );
+
+    expect(result.completion, 'math.sin');
+    _assertEmptyChange(result.change);
+  }
+
+  test_newImport() async {
+    addTestFile(r'''
+main() {} // ref
+''');
+
+    var mathSet = await waitForSetWithUri('dart:math');
+    var result = await _getSuggestionDetails(
+      id: mathSet.id,
+      label: 'sin',
+      offset: testCode.indexOf('} // ref'),
+    );
+
+    expect(result.completion, 'sin');
+    _assertTestFileChange(result.change, r'''
+import 'dart:math';
+
+main() {} // ref
+''');
+  }
+
+  void _assertEmptyChange(SourceChange change) {
+    expect(change.edits, isEmpty);
+  }
+
+  void _assertTestFileChange(SourceChange change, String expected) {
+    var fileEdits = change.edits;
+    expect(fileEdits, hasLength(1));
+
+    var fileEdit = fileEdits[0];
+    expect(fileEdit.file, testFile);
+
+    var edits = fileEdit.edits;
+    expect(SourceEdit.applySequence(testCode, edits), expected);
+  }
+
+  Future<CompletionGetSuggestionDetailsResult> _getSuggestionDetails({
+    String file,
+    @required int id,
+    @required String label,
+    @required int offset,
+  }) async {
+    file ??= testFile;
+    var response = await waitResponse(
+      CompletionGetSuggestionDetailsParams(
+        file,
+        id,
+        label,
+        offset,
+      ).toRequest('0'),
+    );
+    return CompletionGetSuggestionDetailsResult.fromResponse(response);
+  }
+}
diff --git a/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart b/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
new file mode 100644
index 0000000..0de95fdf
--- /dev/null
+++ b/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
@@ -0,0 +1,226 @@
+// Copyright (c) 2019, 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/protocol_server.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'available_suggestions_base.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(GetSuggestionAvailableTest);
+  });
+}
+
+@reflectiveTest
+class GetSuggestionAvailableTest extends AvailableSuggestionsBase {
+  test_dart() async {
+    addTestFile('');
+    var mathSet = await waitForSetWithUri('dart:math');
+    var asyncSet = await waitForSetWithUri('dart:async');
+
+    var results = await _getSuggestions(testFile, 0);
+    expect(results.includedSuggestionKinds, isNotEmpty);
+
+    var includedIdSet = results.includedSuggestionSets.map((set) => set.id);
+    expect(includedIdSet, contains(mathSet.id));
+    expect(includedIdSet, contains(asyncSet.id));
+  }
+
+  test_includedSuggestionKinds_type() async {
+    addTestFile(r'''
+class X extends {} // ref
+''');
+
+    var results = await _getSuggestions(
+      testFile,
+      testCode.indexOf('{} // ref'),
+    );
+
+    expect(
+      results.includedSuggestionKinds,
+      unorderedEquals([
+        ElementKind.CLASS,
+        ElementKind.CLASS_TYPE_ALIAS,
+        ElementKind.ENUM,
+        ElementKind.FUNCTION_TYPE_ALIAS,
+        ElementKind.MIXIN,
+      ]),
+    );
+  }
+
+  test_includedSuggestionKinds_value() async {
+    addTestFile(r'''
+main() {
+  print(); // ref
+}
+''');
+
+    var results = await _getSuggestions(
+      testFile,
+      testCode.indexOf('); // ref'),
+    );
+
+    expect(
+      results.includedSuggestionKinds,
+      unorderedEquals([
+        ElementKind.CLASS,
+        ElementKind.CLASS_TYPE_ALIAS,
+        ElementKind.ENUM,
+        ElementKind.ENUM_CONSTANT,
+        ElementKind.FUNCTION,
+        ElementKind.FUNCTION_TYPE_ALIAS,
+        ElementKind.MIXIN,
+        ElementKind.TOP_LEVEL_VARIABLE,
+      ]),
+    );
+  }
+
+  test_inHtml() async {
+    newFile('/home/test/lib/a.dart', content: 'class A {}');
+
+    var path = convertPath('/home/test/doc/a.html');
+    newFile(path, content: '<html></html>');
+
+    await waitResponse(
+      CompletionGetSuggestionsParams(path, 0).toRequest('0'),
+    );
+    expect(serverErrors, isEmpty);
+  }
+
+  test_relevanceTags_enum() async {
+    newFile('/home/test/lib/a.dart', content: r'''
+enum MyEnum {
+  aaa, bbb
+}
+''');
+    addTestFile(r'''
+import 'a.dart';
+
+void f(MyEnum e) {
+  e = // ref;
+}
+''');
+
+    var results = await _getSuggestions(
+      testFile,
+      testCode.indexOf(' // ref'),
+    );
+
+    assertJsonText(results.includedSuggestionRelevanceTags, r'''
+[
+  {
+    "tag": "package:test/a.dart::MyEnum",
+    "relevanceBoost": 1100
+  }
+]
+''');
+  }
+
+  test_relevanceTags_location_argumentList_named() async {
+    addTestFile(r'''
+void foo({int a, String b}) {}
+
+main() {
+  foo(b: ); // ref
+}
+''');
+
+    var results = await _getSuggestions(
+      testFile,
+      testCode.indexOf('); // ref'),
+    );
+
+    assertJsonText(results.includedSuggestionRelevanceTags, r'''
+[
+  {
+    "tag": "dart:core::String",
+    "relevanceBoost": 10
+  }
+]
+''');
+  }
+
+  test_relevanceTags_location_argumentList_positional() async {
+    addTestFile(r'''
+void foo(double a) {}
+
+main() {
+  foo(); // ref
+}
+''');
+
+    var results = await _getSuggestions(
+      testFile,
+      testCode.indexOf('); // ref'),
+    );
+
+    assertJsonText(results.includedSuggestionRelevanceTags, r'''
+[
+  {
+    "tag": "dart:core::double",
+    "relevanceBoost": 10
+  }
+]
+''');
+  }
+
+  test_relevanceTags_location_assignment() async {
+    addTestFile(r'''
+main() {
+  int v;
+  v = // ref;
+}
+''');
+
+    var results = await _getSuggestions(
+      testFile,
+      testCode.indexOf(' // ref'),
+    );
+
+    assertJsonText(results.includedSuggestionRelevanceTags, r'''
+[
+  {
+    "tag": "dart:core::int",
+    "relevanceBoost": 10
+  }
+]
+''');
+  }
+
+  test_relevanceTags_location_listLiteral() async {
+    addTestFile(r'''
+main() {
+  var v = [0, ]; // ref
+}
+''');
+
+    var results = await _getSuggestions(
+      testFile,
+      testCode.indexOf(']; // ref'),
+    );
+
+    assertJsonText(results.includedSuggestionRelevanceTags, r'''
+[
+  {
+    "tag": "dart:core::int",
+    "relevanceBoost": 10
+  }
+]
+''');
+  }
+
+  Future<CompletionResultsParams> _getSuggestions(
+    String path,
+    int offset,
+  ) async {
+    var response = CompletionGetSuggestionsResult.fromResponse(
+      await waitResponse(
+        CompletionGetSuggestionsParams(path, offset).toRequest('0'),
+      ),
+    );
+    return await waitForGetSuggestions(response.id);
+  }
+}
diff --git a/pkg/analysis_server/test/src/domains/completion/test_all.dart b/pkg/analysis_server/test/src/domains/completion/test_all.dart
new file mode 100644
index 0000000..066f43c
--- /dev/null
+++ b/pkg/analysis_server/test/src/domains/completion/test_all.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2019, 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:test_reflective_loader/test_reflective_loader.dart';
+
+import 'available_suggestion_sets_test.dart' as available_suggestion_sets;
+import 'get_suggestion_details_test.dart' as get_suggestion_details;
+import 'get_suggestions_available_test.dart' as get_suggestions_available;
+
+main() {
+  defineReflectiveSuite(() {
+    available_suggestion_sets.main();
+    get_suggestion_details.main();
+    get_suggestions_available.main();
+  });
+}
diff --git a/pkg/analysis_server/test/src/domains/execution/completion_test.dart b/pkg/analysis_server/test/src/domains/execution/completion_test.dart
index 37ff7fa..c3d9f15 100644
--- a/pkg/analysis_server/test/src/domains/execution/completion_test.dart
+++ b/pkg/analysis_server/test/src/domains/execution/completion_test.dart
@@ -60,8 +60,7 @@
     code = code.replaceAll('^', '');
 
     var computer = new RuntimeCompletionComputer(
-        resourceProvider,
-        fileContentOverlay,
+        overlayResourceProvider,
         driver,
         code,
         codeOffset,
diff --git a/pkg/analysis_server/test/src/domains/test_all.dart b/pkg/analysis_server/test/src/domains/test_all.dart
index ea67387..b936c18 100644
--- a/pkg/analysis_server/test/src/domains/test_all.dart
+++ b/pkg/analysis_server/test/src/domains/test_all.dart
@@ -4,10 +4,12 @@
 
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
-import 'execution/test_all.dart' as execution_test;
+import 'completion/test_all.dart' as completion;
+import 'execution/test_all.dart' as execution;
 
 main() {
   defineReflectiveSuite(() {
-    execution_test.main();
+    completion.main();
+    execution.main();
   });
 }
diff --git a/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
new file mode 100644
index 0000000..88c6a34
--- /dev/null
+++ b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
@@ -0,0 +1,965 @@
+// Copyright (c) 2019, 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/nullability/conditional_discard.dart';
+import 'package:analysis_server/src/nullability/constraint_gatherer.dart';
+import 'package:analysis_server/src/nullability/constraint_variable_gatherer.dart';
+import 'package:analysis_server/src/nullability/decorated_type.dart';
+import 'package:analysis_server/src/nullability/expression_checks.dart';
+import 'package:analysis_server/src/nullability/transitional_api.dart';
+import 'package:analysis_server/src/nullability/unit_propagation.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/test_utilities/find_node.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../abstract_single_unit.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ConstraintGathererTest);
+    defineReflectiveTests(ConstraintVariableGathererTest);
+  });
+}
+
+@reflectiveTest
+class ConstraintGathererTest extends ConstraintsTestBase {
+  @override
+  final _Constraints constraints = _Constraints();
+
+  /// Checks that a constraint was recorded with a left hand side of
+  /// [conditions] and a right hand side of [consequence].
+  void assertConstraint(
+      Iterable<ConstraintVariable> conditions, ConstraintVariable consequence) {
+    expect(constraints._clauses,
+        contains(_Clause(conditions.toSet(), consequence)));
+  }
+
+  /// Checks that no constraint was recorded with a right hand side of
+  /// [consequence].
+  void assertNoConstraints(ConstraintVariable consequence) {
+    expect(
+        constraints._clauses,
+        isNot(contains(
+            predicate((_Clause clause) => clause.consequence == consequence))));
+  }
+
+  /// Gets the [ExpressionChecks] associated with the expression whose text
+  /// representation is [text], or `null` if the expression has no
+  /// [ExpressionChecks] associated with it.
+  ExpressionChecks checkExpression(String text) {
+    return _variables.checkExpression(findNode.expression(text));
+  }
+
+  /// Gets the [DecoratedType] associated with the expression whose text
+  /// representation is [text], or `null` if the expression has no
+  /// [DecoratedType] associated with it.
+  DecoratedType decoratedExpressionType(String text) {
+    return _variables.decoratedExpressionType(findNode.expression(text));
+  }
+
+  test_always() async {
+    await analyze('');
+
+    // No clause is needed for `always`; it is assigned the value `true` before
+    // solving begins.
+    assertNoConstraints(ConstraintVariable.always);
+    assert(ConstraintVariable.always.value, isTrue);
+  }
+
+  test_assert_demonstrates_non_null_intent() async {
+    await analyze('''
+void f(int i) {
+  assert(i != null);
+}
+''');
+
+    assertConstraint([], decoratedTypeAnnotation('int i').nonNullIntent);
+  }
+
+  test_binaryExpression_add_left_check() async {
+    await analyze('''
+int f(int i, int j) => i + j;
+''');
+
+    assertConstraint([decoratedTypeAnnotation('int i').nullable],
+        checkExpression('i +').nullCheck);
+  }
+
+  test_binaryExpression_add_left_check_custom() async {
+    await analyze('''
+class Int {
+  Int operator+(Int other) => this;
+}
+Int f(Int i, Int j) => i + j;
+''');
+
+    assertConstraint([decoratedTypeAnnotation('Int i').nullable],
+        checkExpression('i +').nullCheck);
+  }
+
+  test_binaryExpression_add_result_custom() async {
+    await analyze('''
+class Int {
+  Int operator+(Int other) => this;
+}
+Int f(Int i, Int j) => i + j;
+''');
+
+    assertConstraint([decoratedTypeAnnotation('Int operator+').nullable],
+        decoratedTypeAnnotation('Int f').nullable);
+  }
+
+  test_binaryExpression_add_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i + j;
+''');
+
+    assertNoConstraints(decoratedTypeAnnotation('int f').nullable);
+  }
+
+  test_binaryExpression_add_right_check() async {
+    await analyze('''
+int f(int i, int j) => i + j;
+''');
+
+    assertConstraint([decoratedTypeAnnotation('int j').nullable],
+        checkExpression('j;').nullCheck);
+  }
+
+  test_binaryExpression_add_right_check_custom() async {
+    await analyze('''
+class Int {
+  Int operator+(Int other) => this;
+}
+Int f(Int i, Int j) => i + j;
+''');
+
+    assertConstraint([decoratedTypeAnnotation('Int j').nullable],
+        decoratedTypeAnnotation('Int other').nullable);
+  }
+
+  test_binaryExpression_equal() async {
+    await analyze('''
+bool f(int i, int j) => i == j;
+''');
+
+    assertNoConstraints(decoratedTypeAnnotation('bool f').nullable);
+  }
+
+  test_conditionalExpression_condition_check() async {
+    await analyze('''
+int f(bool b, int i, int j) {
+  return (b ? i : j);
+}
+''');
+
+    var nullable_b = decoratedTypeAnnotation('bool b').nullable;
+    var check_b = checkExpression('b ?').nullCheck;
+    assertConstraint([nullable_b], check_b);
+  }
+
+  test_conditionalExpression_general() async {
+    await analyze('''
+int f(bool b, int i, int j) {
+  return (b ? i : j);
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    var nullable_j = decoratedTypeAnnotation('int j').nullable;
+    var nullable_i_or_nullable_j = _mockOr(nullable_i, nullable_j);
+    var nullable_conditional = decoratedExpressionType('(b ?').nullable;
+    var nullable_return = decoratedTypeAnnotation('int f').nullable;
+    assertConstraint([nullable_i], nullable_conditional);
+    assertConstraint([nullable_j], nullable_conditional);
+    assertConstraint([nullable_conditional], nullable_i_or_nullable_j);
+    assertConstraint([nullable_conditional], nullable_return);
+  }
+
+  test_conditionalExpression_left_non_null() async {
+    await analyze('''
+int f(bool b, int i) {
+  return (b ? (throw i) : i);
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    var nullable_conditional = decoratedExpressionType('(b ?').nullable;
+    expect(nullable_conditional, same(nullable_i));
+  }
+
+  test_conditionalExpression_left_null() async {
+    await analyze('''
+int f(bool b, int i) {
+  return (b ? null : i);
+}
+''');
+
+    var nullable_conditional = decoratedExpressionType('(b ?').nullable;
+    expect(nullable_conditional, same(ConstraintVariable.always));
+  }
+
+  test_conditionalExpression_right_non_null() async {
+    await analyze('''
+int f(bool b, int i) {
+  return (b ? i : (throw i));
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    var nullable_conditional = decoratedExpressionType('(b ?').nullable;
+    expect(nullable_conditional, same(nullable_i));
+  }
+
+  test_conditionalExpression_right_null() async {
+    await analyze('''
+int f(bool b, int i) {
+  return (b ? i : null);
+}
+''');
+
+    var nullable_conditional = decoratedExpressionType('(b ?').nullable;
+    expect(nullable_conditional, same(ConstraintVariable.always));
+  }
+
+  test_functionDeclaration_expression_body() async {
+    await analyze('''
+int/*1*/ f(int/*2*/ i) => i;
+''');
+
+    assertConstraint([decoratedTypeAnnotation('int/*2*/').nullable],
+        decoratedTypeAnnotation('int/*1*/').nullable);
+  }
+
+  test_functionDeclaration_parameter_named_default_notNull() async {
+    await analyze('''
+void f({int i = 1}) {}
+''');
+
+    assertNoConstraints(decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionDeclaration_parameter_named_default_null() async {
+    await analyze('''
+void f({int i = null}) {}
+''');
+
+    assertConstraint(
+        [ConstraintVariable.always], decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionDeclaration_parameter_named_no_default_assume_nullable() async {
+    await analyze('''
+void f({int i}) {}
+''',
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeNullable));
+
+    assertConstraint([], decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionDeclaration_parameter_named_no_default_assume_required() async {
+    await analyze('''
+void f({int i}) {}
+''',
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+
+    assertNoConstraints(decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionDeclaration_parameter_named_no_default_required_assume_nullable() async {
+    addMetaPackage();
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required int i}) {}
+''',
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeNullable));
+
+    assertNoConstraints(decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionDeclaration_parameter_named_no_default_required_assume_required() async {
+    addMetaPackage();
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required int i}) {}
+''',
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+
+    assertNoConstraints(decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionDeclaration_parameter_positionalOptional_default_notNull() async {
+    await analyze('''
+void f([int i = 1]) {}
+''');
+
+    assertNoConstraints(decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionDeclaration_parameter_positionalOptional_default_null() async {
+    await analyze('''
+void f([int i = null]) {}
+''');
+
+    assertConstraint(
+        [ConstraintVariable.always], decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionDeclaration_parameter_positionalOptional_no_default() async {
+    await analyze('''
+void f([int i]) {}
+''');
+
+    assertConstraint([], decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionDeclaration_parameter_positionalOptional_no_default_assume_required() async {
+    // Note: the `assumeRequired` behavior shouldn't affect the behavior here
+    // because it only affects named parameters.
+    await analyze('''
+void f([int i]) {}
+''',
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+
+    assertConstraint([], decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionInvocation_parameter_fromLocalParameter() async {
+    await analyze('''
+void f(int/*1*/ i) {}
+void test(int/*2*/ i) {
+  f(i);
+}
+''');
+
+    assertConstraint([decoratedTypeAnnotation('int/*2*/').nullable],
+        decoratedTypeAnnotation('int/*1*/').nullable);
+  }
+
+  test_functionInvocation_parameter_named() async {
+    await analyze('''
+void f({int i: 0}) {}
+void g(int j) {
+  f(i: j);
+}
+''');
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    var nullable_j = decoratedTypeAnnotation('int j').nullable;
+    assertConstraint([nullable_j], nullable_i);
+  }
+
+  test_functionInvocation_parameter_named_missing() async {
+    await analyze('''
+void f({int i}) {}
+void g() {
+  f();
+}
+''');
+    var optional_i = possiblyOptionalParameter('int i');
+    assertConstraint([], optional_i);
+  }
+
+  test_functionInvocation_parameter_named_missing_required() async {
+    addMetaPackage();
+    verifyNoTestUnitErrors = false;
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required int i}) {}
+void g() {
+  f();
+}
+''');
+    // The call at `f()` is presumed to be in error; no constraint is recorded.
+    var optional_i = possiblyOptionalParameter('int i');
+    expect(optional_i, isNull);
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    assertNoConstraints(nullable_i);
+  }
+
+  test_functionInvocation_parameter_null() async {
+    await analyze('''
+void f(int i) {}
+void test() {
+  f(null);
+}
+''');
+
+    assertConstraint(
+        [ConstraintVariable.always], decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionInvocation_return() async {
+    await analyze('''
+int/*1*/ f() => 0;
+int/*2*/ g() {
+  return f();
+}
+''');
+
+    assertConstraint([decoratedTypeAnnotation('int/*1*/').nullable],
+        decoratedTypeAnnotation('int/*2*/').nullable);
+  }
+
+  test_if_condition() async {
+    await analyze('''
+void f(bool b) {
+  if (b) {}
+}
+''');
+
+    assertConstraint([(decoratedTypeAnnotation('bool b').nullable)],
+        checkExpression('b) {}').nullCheck);
+  }
+
+  test_if_conditional_control_flow_after() async {
+    // Asserts after ifs don't demonstrate non-null intent.
+    // TODO(paulberry): if both branches complete normally, they should.
+    await analyze('''
+void f(bool b, int i) {
+  if (b) return;
+  assert(i != null);
+}
+''');
+
+    assertNoConstraints(decoratedTypeAnnotation('int i').nonNullIntent);
+  }
+
+  test_if_conditional_control_flow_within() async {
+    // Asserts inside ifs don't demonstrate non-null intent.
+    await analyze('''
+void f(bool b, int i) {
+  if (b) {
+    assert(i != null);
+  } else {
+    assert(i != null);
+  }
+}
+''');
+
+    assertNoConstraints(decoratedTypeAnnotation('int i').nonNullIntent);
+  }
+
+  test_if_guard_equals_null() async {
+    await analyze('''
+int f(int i, int j, int k) {
+  if (i == null) {
+    return j;
+  } else {
+    return k;
+  }
+}
+''');
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    var nullable_j = decoratedTypeAnnotation('int j').nullable;
+    var nullable_k = decoratedTypeAnnotation('int k').nullable;
+    var nullable_return = decoratedTypeAnnotation('int f').nullable;
+    assertConstraint([nullable_i, nullable_j], nullable_return);
+    assertConstraint([nullable_k], nullable_return);
+    var discard = statementDiscard('if (i == null)');
+    expect(discard.keepTrue, same(nullable_i));
+    expect(discard.keepFalse, same(ConstraintVariable.always));
+    expect(discard.pureCondition, true);
+  }
+
+  test_if_simple() async {
+    await analyze('''
+int f(bool b, int i, int j) {
+  if (b) {
+    return i;
+  } else {
+    return j;
+  }
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    var nullable_j = decoratedTypeAnnotation('int j').nullable;
+    var nullable_return = decoratedTypeAnnotation('int f').nullable;
+    assertConstraint([nullable_i], nullable_return);
+    assertConstraint([nullable_j], nullable_return);
+  }
+
+  test_if_without_else() async {
+    await analyze('''
+int f(bool b, int i) {
+  if (b) {
+    return i;
+  }
+  return 0;
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    var nullable_return = decoratedTypeAnnotation('int f').nullable;
+    assertConstraint([nullable_i], nullable_return);
+  }
+
+  test_intLiteral() async {
+    await analyze('''
+int f() {
+  return 0;
+}
+''');
+    assertNoConstraints(decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_methodInvocation_parameter_contravariant() async {
+    await analyze('''
+class C<T> {
+  void f(T t) {}
+}
+void g(C<int> c, int i) {
+  c.f(i);
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    var nullable_c_t =
+        decoratedTypeAnnotation('C<int>').typeArguments[0].nullable;
+    var nullable_t = decoratedTypeAnnotation('T t').nullable;
+    var nullable_c_t_or_nullable_t = _mockOr(nullable_c_t, nullable_t);
+    assertConstraint([nullable_i], nullable_c_t_or_nullable_t);
+  }
+
+  test_methodInvocation_parameter_generic() async {
+    await analyze('''
+class C<T> {}
+void f(C<int/*1*/>/*2*/ c) {}
+void g(C<int/*3*/>/*4*/ c) {
+  f(c);
+}
+''');
+
+    assertConstraint([decoratedTypeAnnotation('int/*3*/').nullable],
+        decoratedTypeAnnotation('int/*1*/').nullable);
+    assertConstraint([decoratedTypeAnnotation('C<int/*3*/>/*4*/').nullable],
+        decoratedTypeAnnotation('C<int/*1*/>/*2*/').nullable);
+  }
+
+  test_methodInvocation_parameter_named() async {
+    await analyze('''
+class C {
+  void f({int i: 0}) {}
+}
+void g(C c, int j) {
+  c.f(i: j);
+}
+''');
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    var nullable_j = decoratedTypeAnnotation('int j').nullable;
+    assertConstraint([nullable_j], nullable_i);
+  }
+
+  test_methodInvocation_target_check() async {
+    await analyze('''
+class C {
+  void m() {}
+}
+void test(C c) {
+  c.m();
+}
+''');
+
+    assertConstraint([decoratedTypeAnnotation('C c').nullable],
+        checkExpression('c.m').nullCheck);
+  }
+
+  test_parenthesizedExpression() async {
+    await analyze('''
+int f() {
+  return (null);
+}
+''');
+
+    assertConstraint(
+        [ConstraintVariable.always], decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_return_implicit_null() async {
+    verifyNoTestUnitErrors = false;
+    await analyze('''
+int f() {
+  return;
+}
+''');
+
+    assertConstraint(
+        [ConstraintVariable.always], decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_return_null() async {
+    await analyze('''
+int f() {
+  return null;
+}
+''');
+
+    assertConstraint(
+        [ConstraintVariable.always], decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_stringLiteral() async {
+    // TODO(paulberry): also test string interpolations
+    await analyze('''
+String f() {
+  return 'x';
+}
+''');
+    assertNoConstraints(decoratedTypeAnnotation('String').nullable);
+  }
+
+  test_thisExpression() async {
+    await analyze('''
+class C {
+  C f() => this;
+}
+''');
+
+    assertNoConstraints(decoratedTypeAnnotation('C f').nullable);
+  }
+
+  test_throwExpression() async {
+    await analyze('''
+int f() {
+  return throw null;
+}
+''');
+    assertNoConstraints(decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_typeName() async {
+    await analyze('''
+Type f() {
+  return int;
+}
+''');
+    assertNoConstraints(decoratedTypeAnnotation('Type').nullable);
+  }
+
+  /// Creates a variable representing the disjunction of [a] and [b] solely for
+  /// the purpose of inspecting constraint equations in unit tests.  No
+  /// additional constraints will be recorded in [_constraints] as a consequence
+  /// of creating this variable.
+  ConstraintVariable _mockOr(ConstraintVariable a, ConstraintVariable b) =>
+      ConstraintVariable.or(_MockConstraints(), a, b);
+}
+
+abstract class ConstraintsTestBase extends MigrationVisitorTestBase {
+  Constraints get constraints;
+
+  /// Analyzes the given source code, producing constraint variables and
+  /// constraints for it.
+  @override
+  Future<CompilationUnit> analyze(String code,
+      {NullabilityMigrationAssumptions assumptions:
+          const NullabilityMigrationAssumptions()}) async {
+    var unit = await super.analyze(code);
+    unit.accept(ConstraintGatherer(
+        typeProvider, _variables, constraints, testSource, false, assumptions));
+    return unit;
+  }
+}
+
+@reflectiveTest
+class ConstraintVariableGathererTest extends MigrationVisitorTestBase {
+  /// Gets the [DecoratedType] associated with the function declaration whose
+  /// name matches [search].
+  DecoratedType decoratedFunctionType(String search) =>
+      _variables.decoratedElementType(
+          findNode.functionDeclaration(search).declaredElement);
+
+  test_interfaceType_nullable() async {
+    await analyze('''
+void f(int? x) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('int?');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedType));
+    expect(decoratedType.nullable, same(ConstraintVariable.always));
+  }
+
+  test_interfaceType_typeParameter() async {
+    await analyze('''
+void f(List<int> x) {}
+''');
+    var decoratedListType = decoratedTypeAnnotation('List<int>');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedListType));
+    expect(decoratedListType.nullable, isNotNull);
+    var decoratedIntType = decoratedTypeAnnotation('int');
+    expect(decoratedListType.typeArguments[0], same(decoratedIntType));
+    expect(decoratedIntType.nullable, isNotNull);
+  }
+
+  test_topLevelFunction_parameterType_implicit_dynamic() async {
+    await analyze('''
+void f(x) {}
+''');
+    var decoratedType =
+        _variables.decoratedElementType(findNode.simple('x').staticElement);
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedType));
+    expect(decoratedType.type.isDynamic, isTrue);
+    expect(decoratedType.nullable, same(ConstraintVariable.always));
+  }
+
+  test_topLevelFunction_parameterType_named_no_default() async {
+    await analyze('''
+void f({String s}) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('String');
+    var functionType = decoratedFunctionType('f');
+    expect(functionType.namedParameters['s'], same(decoratedType));
+    expect(decoratedType.nullable, isNotNull);
+    expect(decoratedType.nullable, isNot(same(ConstraintVariable.always)));
+    expect(functionType.namedParameterOptionalVariables['s'],
+        same(decoratedType.nullable));
+  }
+
+  test_topLevelFunction_parameterType_named_no_default_required() async {
+    addMetaPackage();
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('String');
+    var functionType = decoratedFunctionType('f');
+    expect(functionType.namedParameters['s'], same(decoratedType));
+    expect(decoratedType.nullable, isNotNull);
+    expect(decoratedType.nullable, isNot(same(ConstraintVariable.always)));
+    expect(functionType.namedParameterOptionalVariables['s'], isNull);
+  }
+
+  test_topLevelFunction_parameterType_named_with_default() async {
+    await analyze('''
+void f({String s: 'x'}) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('String');
+    var functionType = decoratedFunctionType('f');
+    expect(functionType.namedParameters['s'], same(decoratedType));
+    expect(decoratedType.nullable, isNotNull);
+    expect(functionType.namedParameterOptionalVariables['s'],
+        same(ConstraintVariable.always));
+  }
+
+  test_topLevelFunction_parameterType_positionalOptional() async {
+    await analyze('''
+void f([int i]) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('int');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedType));
+    expect(decoratedType.nullable, isNotNull);
+  }
+
+  test_topLevelFunction_parameterType_simple() async {
+    await analyze('''
+void f(int i) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('int');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedType));
+    expect(decoratedType.nullable, isNotNull);
+    expect(decoratedType.nonNullIntent, isNotNull);
+  }
+
+  test_topLevelFunction_returnType_implicit_dynamic() async {
+    await analyze('''
+f() {}
+''');
+    var decoratedType = decoratedFunctionType('f').returnType;
+    expect(decoratedType.type.isDynamic, isTrue);
+    expect(decoratedType.nullable, same(ConstraintVariable.always));
+  }
+
+  test_topLevelFunction_returnType_simple() async {
+    await analyze('''
+int f() => 0;
+''');
+    var decoratedType = decoratedTypeAnnotation('int');
+    expect(decoratedFunctionType('f').returnType, same(decoratedType));
+    expect(decoratedType.nullable, isNotNull);
+  }
+}
+
+class MigrationVisitorTestBase extends AbstractSingleUnitTest {
+  final _variables = _Variables();
+
+  FindNode findNode;
+
+  TypeProvider get typeProvider => testAnalysisResult.typeProvider;
+
+  Future<CompilationUnit> analyze(String code,
+      {NullabilityMigrationAssumptions assumptions:
+          const NullabilityMigrationAssumptions()}) async {
+    await resolveTestUnit(code);
+    testUnit.accept(
+        ConstraintVariableGatherer(_variables, testSource, false, assumptions));
+    findNode = FindNode(code, testUnit);
+    return testUnit;
+  }
+
+  /// Gets the [DecoratedType] associated with the type annotation whose text
+  /// is [text].
+  DecoratedType decoratedTypeAnnotation(String text) {
+    return _variables.decoratedTypeAnnotation(findNode.typeAnnotation(text));
+  }
+
+  ConstraintVariable possiblyOptionalParameter(String text) {
+    return _variables
+        .possiblyOptionalParameter(findNode.defaultParameter(text));
+  }
+
+  @override
+  void setUp() {
+    createAnalysisOptionsFile(experiments: [EnableString.non_nullable]);
+    super.setUp();
+  }
+
+  /// Gets the [ConditionalDiscard] information associated with the statement
+  /// whose text is [text].
+  ConditionalDiscard statementDiscard(String text) {
+    return _variables.conditionalDiscard(findNode.statement(text));
+  }
+}
+
+/// Mock representation of a constraint equation that is not connected to a
+/// constraint solver.  We use this to confirm that analysis produces the
+/// correct constraint equations.
+///
+/// [hashCode] and equality are implemented using [toString] for simplicity.
+class _Clause {
+  final Set<ConstraintVariable> conditions;
+  final ConstraintVariable consequence;
+
+  _Clause(this.conditions, this.consequence);
+
+  @override
+  int get hashCode => toString().hashCode;
+
+  @override
+  bool operator ==(Object other) =>
+      other is _Clause && toString() == other.toString();
+
+  @override
+  String toString() {
+    String lhs;
+    if (conditions.isNotEmpty) {
+      var sortedConditionStrings = conditions.map((v) => v.toString()).toList()
+        ..sort();
+      lhs = sortedConditionStrings.join(' & ') + ' => ';
+    } else {
+      lhs = '';
+    }
+    String rhs = consequence.toString();
+    return lhs + rhs;
+  }
+}
+
+/// Mock representation of a constraint solver that does not actually do any
+/// solving.  We use this to confirm that analysis produced the correct
+/// constraint equations.
+class _Constraints extends Constraints {
+  final _clauses = <_Clause>[];
+
+  @override
+  void record(
+      Iterable<ConstraintVariable> conditions, ConstraintVariable consequence) {
+    _clauses.add(_Clause(conditions.toSet(), consequence));
+  }
+}
+
+/// Mock implementation of [Constraints] that doesn't record any constraints.
+class _MockConstraints implements Constraints {
+  @override
+  void record(Iterable<ConstraintVariable> conditions,
+      ConstraintVariable consequence) {}
+}
+
+/// Mock representation of constraint variables.
+class _Variables extends Variables {
+  final _conditionalDiscard = <AstNode, ConditionalDiscard>{};
+
+  final _decoratedExpressionTypes = <Expression, DecoratedType>{};
+
+  final _decoratedTypeAnnotations = <TypeAnnotation, DecoratedType>{};
+
+  final _expressionChecks = <Expression, ExpressionChecks>{};
+
+  final _possiblyOptional = <DefaultFormalParameter, ConstraintVariable>{};
+
+  /// Gets the [ExpressionChecks] associated with the given [expression].
+  ExpressionChecks checkExpression(Expression expression) =>
+      _expressionChecks[_normalizeExpression(expression)];
+
+  /// Gets the [conditionalDiscard] associated with the given [expression].
+  ConditionalDiscard conditionalDiscard(AstNode node) =>
+      _conditionalDiscard[node];
+
+  /// Gets the [DecoratedType] associated with the given [expression].
+  DecoratedType decoratedExpressionType(Expression expression) =>
+      _decoratedExpressionTypes[_normalizeExpression(expression)];
+
+  /// Gets the [DecoratedType] associated with the given [typeAnnotation].
+  DecoratedType decoratedTypeAnnotation(TypeAnnotation typeAnnotation) =>
+      _decoratedTypeAnnotations[typeAnnotation];
+
+  /// Gets the [ConstraintVariable] associated with the possibility that
+  /// [parameter] may be optional.
+  ConstraintVariable possiblyOptionalParameter(
+          DefaultFormalParameter parameter) =>
+      _possiblyOptional[parameter];
+
+  @override
+  void recordConditionalDiscard(
+      Source source, AstNode node, ConditionalDiscard conditionalDiscard) {
+    _conditionalDiscard[node] = conditionalDiscard;
+    super.recordConditionalDiscard(source, node, conditionalDiscard);
+  }
+
+  void recordDecoratedExpressionType(Expression node, DecoratedType type) {
+    super.recordDecoratedExpressionType(node, type);
+    _decoratedExpressionTypes[_normalizeExpression(node)] = type;
+  }
+
+  void recordDecoratedTypeAnnotation(TypeAnnotation node, DecoratedType type) {
+    super.recordDecoratedTypeAnnotation(node, type);
+    _decoratedTypeAnnotations[node] = type;
+  }
+
+  @override
+  void recordExpressionChecks(Expression expression, ExpressionChecks checks) {
+    super.recordExpressionChecks(expression, checks);
+    _expressionChecks[_normalizeExpression(expression)] = checks;
+  }
+
+  @override
+  void recordPossiblyOptional(Source source, DefaultFormalParameter parameter,
+      ConstraintVariable variable) {
+    _possiblyOptional[parameter] = variable;
+    super.recordPossiblyOptional(source, parameter, variable);
+  }
+
+  /// Unwraps any parentheses surrounding [expression].
+  Expression _normalizeExpression(Expression expression) {
+    while (expression is ParenthesizedExpression) {
+      expression = (expression as ParenthesizedExpression).expression;
+    }
+    return expression;
+  }
+}
diff --git a/pkg/analysis_server/test/src/nullability/provisional_api_test.dart b/pkg/analysis_server/test/src/nullability/provisional_api_test.dart
new file mode 100644
index 0000000..8f9f86e
--- /dev/null
+++ b/pkg/analysis_server/test/src/nullability/provisional_api_test.dart
@@ -0,0 +1,611 @@
+// Copyright (c) 2019, 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/nullability/provisional_api.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../abstract_context.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ProvisionalApiTest);
+    defineReflectiveTests(ProvisionalApiTestPermissive);
+    defineReflectiveTests(ProvisionalApiTestWithReset);
+  });
+}
+
+/// Tests of the provisional API.
+@reflectiveTest
+class ProvisionalApiTest extends ProvisionalApiTestBase
+    with ProvisionalApiTestCases {
+  @override
+  bool get usePermissiveMode => false;
+}
+
+/// Base class for provisional API tests.
+abstract class ProvisionalApiTestBase extends AbstractContextTest {
+  bool get usePermissiveMode;
+
+  /// Hook invoked after calling `prepareInput` on each input.
+  void _afterPrepare() {}
+
+  /// Verifies that migration of the files in [input] produces the output in
+  /// [expectedOutput].
+  Future<void> _checkMultipleFileChanges(
+      Map<String, String> input, Map<String, String> expectedOutput,
+      {NullabilityMigrationAssumptions assumptions:
+          const NullabilityMigrationAssumptions()}) async {
+    for (var path in input.keys) {
+      newFile(path, content: input[path]);
+    }
+    var listener = new TestMigrationListener();
+    var migration = NullabilityMigration(listener,
+        permissive: usePermissiveMode, assumptions: assumptions);
+    for (var path in input.keys) {
+      migration.prepareInput(await session.getResolvedUnit(path));
+    }
+    _afterPrepare();
+    for (var path in input.keys) {
+      migration.processInput(await session.getResolvedUnit(path));
+    }
+    migration.finish();
+    var sourceEdits = <String, List<SourceEdit>>{};
+    for (var fix in listener.fixes) {
+      var path = fix.source.fullName;
+      expect(expectedOutput.keys, contains(path));
+      (sourceEdits[path] ??= []).addAll(fix.sourceEdits);
+    }
+    for (var path in expectedOutput.keys) {
+      var sourceEditsForPath = sourceEdits[path] ?? [];
+      sourceEditsForPath.sort((a, b) => b.offset.compareTo(a.offset));
+      expect(SourceEdit.applySequence(input[path], sourceEditsForPath),
+          expectedOutput[path]);
+    }
+  }
+
+  /// Verifies that migraiton of the single file with the given [content]
+  /// produces the [expected] output.
+  Future<void> _checkSingleFileChanges(String content, String expected,
+      {NullabilityMigrationAssumptions assumptions:
+          const NullabilityMigrationAssumptions()}) async {
+    var sourcePath = convertPath('/home/test/lib/test.dart');
+    await _checkMultipleFileChanges(
+        {sourcePath: content}, {sourcePath: expected},
+        assumptions: assumptions);
+  }
+}
+
+/// Mixin containing test cases for the provisional API.
+mixin ProvisionalApiTestCases on ProvisionalApiTestBase {
+  test_data_flow_generic_inward() async {
+    var content = '''
+class C<T> {
+  void f(T t) {}
+}
+void g(C<int> c, int i) {
+  c.f(i);
+}
+void test(C<int> c) {
+  g(c, null);
+}
+''';
+
+    // Default behavior is to add nullability at the call site.  Rationale: this
+    // is correct in the common case where the generic parameter represents the
+    // type of an item in a container.  Also, if there are many callers that are
+    // largely independent, adding nullability to the callee would likely
+    // propagate to a field in the class, and thence (via return values of other
+    // methods) to most users of the class.  Whereas if we add nullability at
+    // the call site it's possible that other call sites won't need it.
+    //
+    // TODO(paulberry): possible improvement: detect that since C uses T in a
+    // contravariant way, and deduce that test should change to
+    // `void test(C<int?> c)`
+    var expected = '''
+class C<T> {
+  void f(T t) {}
+}
+void g(C<int?> c, int? i) {
+  c.f(i);
+}
+void test(C<int> c) {
+  g(c, null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_data_flow_generic_inward_hint() async {
+    var content = '''
+class C<T> {
+  void f(T? t) {}
+}
+void g(C<int> c, int i) {
+  c.f(i);
+}
+void test(C<int> c) {
+  g(c, null);
+}
+''';
+
+    // The user may override the behavior shown in test_data_flow_generic_inward
+    // by explicitly marking f's use of T as nullable.  Since this makes g's
+    // call to f valid regardless of the type of c, c's type will remain
+    // C<int>.
+    var expected = '''
+class C<T> {
+  void f(T? t) {}
+}
+void g(C<int> c, int? i) {
+  c.f(i);
+}
+void test(C<int> c) {
+  g(c, null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_data_flow_inward() async {
+    var content = '''
+int f(int i) => 0;
+int g(int i) => f(i);
+void test() {
+  g(null);
+}
+''';
+
+    var expected = '''
+int f(int? i) => 0;
+int g(int? i) => f(i);
+void test() {
+  g(null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_data_flow_inward_missing_type() async {
+    var content = '''
+int f(int i) => 0;
+int g(i) => f(i); // TODO(danrubel): suggest type
+void test() {
+  g(null);
+}
+''';
+
+    var expected = '''
+int f(int? i) => 0;
+int g(i) => f(i); // TODO(danrubel): suggest type
+void test() {
+  g(null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_data_flow_outward() async {
+    var content = '''
+int f(int i) => null;
+int g(int i) => f(i);
+''';
+
+    var expected = '''
+int? f(int i) => null;
+int? g(int i) => f(i);
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_data_flow_outward_missing_type() async {
+    var content = '''
+f(int i) => null; // TODO(danrubel): suggest type
+int g(int i) => f(i);
+''';
+
+    var expected = '''
+f(int i) => null; // TODO(danrubel): suggest type
+int? g(int i) => f(i);
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_discard_simple_condition() async {
+    var content = '''
+int f(int i) {
+  if (i == null) {
+    return null;
+  } else {
+    return i + 1;
+  }
+}
+''';
+
+    var expected = '''
+int f(int i) {
+  /* if (i == null) {
+    return null;
+  } else {
+    */ return i + 1; /*
+  } */
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_named_parameter_no_default_unused_option2_assume_nullable() async {
+    var content = '''
+void f({String s}) {}
+main() {
+  f();
+}
+''';
+    var expected = '''
+void f({String? s}) {}
+main() {
+  f();
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeNullable));
+  }
+
+  test_named_parameter_no_default_unused_option2_assume_required() async {
+    var content = '''
+void f({String s}) {}
+main() {
+  f();
+}
+''';
+    var expected = '''
+void f({String? s}) {}
+main() {
+  f();
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+  }
+
+  test_named_parameter_no_default_unused_required_option2_assume_nullable() async {
+    // The `@required` annotation overrides the assumption of nullability.
+    // The call at `f()` is presumed to be in error.
+    addMetaPackage();
+    var content = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f();
+}
+''';
+    var expected = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f();
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeNullable));
+  }
+
+  test_named_parameter_no_default_unused_required_option2_assume_required() async {
+    // Since the `@required` annotation is already present, it is not added
+    // again.
+    // The call at `f()` is presumed to be in error.
+    addMetaPackage();
+    var content = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f();
+}
+''';
+    var expected = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f();
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+  }
+
+  test_named_parameter_no_default_used_non_null_option2_assume_nullable() async {
+    var content = '''
+void f({String s}) {}
+main() {
+  f(s: 'x');
+}
+''';
+    var expected = '''
+void f({String? s}) {}
+main() {
+  f(s: 'x');
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeNullable));
+  }
+
+  test_named_parameter_no_default_used_non_null_option2_assume_required() async {
+    var content = '''
+void f({String s}) {}
+main() {
+  f(s: 'x');
+}
+''';
+    var expected = '''
+void f({@required String s}) {}
+main() {
+  f(s: 'x');
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+  }
+
+  test_named_parameter_no_default_used_non_null_required_option2_assume_required() async {
+    // Even if we are using the "assumeRequired" heuristic, we should not add a
+    // duplicate `@required` annotation.
+    addMetaPackage();
+    var content = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f(s: 'x');
+}
+''';
+    var expected = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f(s: 'x');
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+  }
+
+  test_named_parameter_no_default_used_null_option2() async {
+    var content = '''
+void f({String s}) {}
+main() {
+  f(s: null);
+}
+''';
+    var expected = '''
+void f({String? s}) {}
+main() {
+  f(s: null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_named_parameter_no_default_used_null_required_option2_assume_nullable() async {
+    // Explicitly passing `null` forces the parameter to be nullable even though
+    // it is required.
+    addMetaPackage();
+    var content = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f(s: null);
+}
+''';
+    var expected = '''
+import 'package:meta/meta.dart';
+void f({@required String? s}) {}
+main() {
+  f(s: null);
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeNullable));
+  }
+
+  test_named_parameter_no_default_used_null_required_option2_assume_required() async {
+    // Explicitly passing `null` forces the parameter to be nullable even though
+    // it is required.
+    addMetaPackage();
+    var content = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f(s: null);
+}
+''';
+    var expected = '''
+import 'package:meta/meta.dart';
+void f({@required String? s}) {}
+main() {
+  f(s: null);
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+  }
+
+  test_named_parameter_with_non_null_default_unused_option2() async {
+    var content = '''
+void f({String s: 'foo'}) {}
+main() {
+  f();
+}
+''';
+    var expected = '''
+void f({String s: 'foo'}) {}
+main() {
+  f();
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_named_parameter_with_non_null_default_used_non_null_option2() async {
+    var content = '''
+void f({String s: 'foo'}) {}
+main() {
+  f(s: 'bar');
+}
+''';
+    var expected = '''
+void f({String s: 'foo'}) {}
+main() {
+  f(s: 'bar');
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_named_parameter_with_non_null_default_used_null_option2() async {
+    var content = '''
+void f({String s: 'foo'}) {}
+main() {
+  f(s: null);
+}
+''';
+    var expected = '''
+void f({String? s: 'foo'}) {}
+main() {
+  f(s: null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_named_parameter_with_null_default_unused_option2() async {
+    var content = '''
+void f({String s: null}) {}
+main() {
+  f();
+}
+''';
+    var expected = '''
+void f({String? s: null}) {}
+main() {
+  f();
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_non_null_assertion() async {
+    var content = '''
+int f(int i, [int j]) {
+  if (i == 0) return i;
+  return i + j;
+}
+''';
+
+    var expected = '''
+int f(int i, [int? j]) {
+  if (i == 0) return i;
+  return i + j!;
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_single_file_multiple_changes() async {
+    var content = '''
+int f() => null;
+int g() => null;
+''';
+    var expected = '''
+int? f() => null;
+int? g() => null;
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_single_file_single_change() async {
+    var content = '''
+int f() => null;
+''';
+    var expected = '''
+int? f() => null;
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_two_files() async {
+    var root = '/home/test/lib';
+    var path1 = convertPath('$root/file1.dart');
+    var file1 = '''
+import 'file2.dart';
+int f() => null;
+int h() => g();
+''';
+    var expected1 = '''
+import 'file2.dart';
+int? f() => null;
+int? h() => g();
+''';
+    var path2 = convertPath('$root/file2.dart');
+    var file2 = '''
+import 'file1.dart';
+int g() => f();
+''';
+    var expected2 = '''
+import 'file1.dart';
+int? g() => f();
+''';
+    await _checkMultipleFileChanges(
+        {path1: file1, path2: file2}, {path1: expected1, path2: expected2});
+  }
+}
+
+@reflectiveTest
+class ProvisionalApiTestPermissive extends ProvisionalApiTestBase
+    with ProvisionalApiTestCases {
+  @override
+  bool get usePermissiveMode => true;
+}
+
+/// Tests of the provisional API, where the driver is reset between calls to
+/// `prepareInput` and `processInput`, ensuring that the migration algorithm
+/// sees different AST and element objects during different phases.
+@reflectiveTest
+class ProvisionalApiTestWithReset extends ProvisionalApiTestBase
+    with ProvisionalApiTestCases {
+  @override
+  bool get usePermissiveMode => false;
+
+  @override
+  void _afterPrepare() {
+    driver.resetUriResolution();
+  }
+}
+
+class TestMigrationListener implements NullabilityMigrationListener {
+  final fixes = <SingleNullabilityFix>[];
+
+  @override
+  void addFix(SingleNullabilityFix fix) {
+    fixes.add(fix);
+  }
+}
diff --git a/pkg/analysis_server/test/src/nullability/test_all.dart b/pkg/analysis_server/test/src/nullability/test_all.dart
new file mode 100644
index 0000000..77b5187
--- /dev/null
+++ b/pkg/analysis_server/test/src/nullability/test_all.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2019, 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:test_reflective_loader/test_reflective_loader.dart';
+
+import 'migration_visitor_test.dart' as migration_visitor_test;
+import 'provisional_api_test.dart' as provisional_api_test;
+import 'unit_propagation_test.dart' as unit_propagation_test;
+
+main() {
+  defineReflectiveSuite(() {
+    migration_visitor_test.main();
+    provisional_api_test.main();
+    unit_propagation_test.main();
+  });
+}
diff --git a/pkg/analysis_server/test/src/nullability/unit_propagation_test.dart b/pkg/analysis_server/test/src/nullability/unit_propagation_test.dart
new file mode 100644
index 0000000..d2fcf89
--- /dev/null
+++ b/pkg/analysis_server/test/src/nullability/unit_propagation_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2019, 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/nullability/unit_propagation.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(UnitPropagationTest);
+  });
+}
+
+/// TODO(paulberry): write more tests
+@reflectiveTest
+class UnitPropagationTest {
+  var solver = Solver();
+
+  ConstraintVariable newVar(String name) => _NamedConstraintVariable(name);
+
+  test_record_copies_conditions() {
+    var a = newVar('a');
+    var b = newVar('b');
+    var conditions = [a];
+    solver.record(conditions, b);
+    conditions.removeLast();
+    expect(a.value, false);
+    expect(b.value, false);
+    solver.record([], a);
+    expect(a.value, true);
+    expect(b.value, true);
+  }
+
+  test_record_propagates_true_variables_immediately() {
+    var a = newVar('a');
+    expect(a.value, false);
+    solver.record([], a);
+    expect(a.value, true);
+    var b = newVar('b');
+    expect(b.value, false);
+    solver.record([a], b);
+    expect(b.value, true);
+  }
+}
+
+/// Representation of a constraint variable with a specified name.
+///
+/// This makes test failures easier to comprehend.
+class _NamedConstraintVariable extends ConstraintVariable {
+  final String _name;
+
+  _NamedConstraintVariable(this._name);
+
+  @override
+  String toString() => _name;
+}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_documentation_into_line_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_documentation_into_line_test.dart
index 535bf55..b13e269 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_documentation_into_line_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_documentation_into_line_test.dart
@@ -109,4 +109,25 @@
 }
 ''');
   }
+
+  test_preserveIndentation() async {
+    await resolveTestUnit('''
+class A {
+  /**
+   * First line.
+   *     Indented line.
+   * Last line.
+   */
+  m() {}
+}
+''');
+    await assertHasAssistAt('Indented', '''
+class A {
+  /// First line.
+  ///     Indented line.
+  /// Last line.
+  m() {}
+}
+''');
+  }
 }
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_into_async_body_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_into_async_body_test.dart
index 170a3c4..5e430f9 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_into_async_body_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_into_async_body_test.dart
@@ -120,11 +120,7 @@
   int m();
 }
 ''');
-    await assertHasAssistAt('m()', '''
-abstract class C {
-  Future<int> m();
-}
-''');
+    await assertNoAssist();
   }
 
   test_method_noReturnType() async {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_if_element_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_if_element_test.dart
new file mode 100644
index 0000000..fda239f
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_if_element_test.dart
@@ -0,0 +1,271 @@
+// Copyright (c) 2019, 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/assist.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'assist_processor.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ConvertToIfElementTest);
+  });
+}
+
+@reflectiveTest
+class ConvertToIfElementTest extends AssistProcessorTest {
+  @override
+  AssistKind get kind => DartAssistKind.CONVERT_TO_IF_ELEMENT;
+
+  void setUp() {
+    createAnalysisOptionsFile(experiments: [
+      EnableString.control_flow_collections,
+      EnableString.set_literals
+    ]);
+    super.setUp();
+  }
+
+  test_conditional_list() async {
+    await resolveTestUnit('''
+f(bool b) {
+  return ['a', b /*caret*/? 'c' : 'd', 'e'];
+}
+''');
+    await assertHasAssist('''
+f(bool b) {
+  return ['a', if (b) 'c' else 'd', 'e'];
+}
+''');
+  }
+
+  test_conditional_list_withParentheses() async {
+    await resolveTestUnit('''
+f(bool b) {
+  return ['a', (b /*caret*/? 'c' : 'd'), 'e'];
+}
+''');
+    await assertHasAssist('''
+f(bool b) {
+  return ['a', if (b) 'c' else 'd', 'e'];
+}
+''');
+  }
+
+  test_conditional_map() async {
+    await resolveTestUnit('''
+f(bool b) {
+  return {'a' : 1, b /*caret*/? 'c' : 'd' : 2, 'e' : 3};
+}
+''');
+    await assertNoAssist();
+  }
+
+  test_conditional_notConditional() async {
+    await resolveTestUnit('''
+f(bool b) {
+  return {'/*caret*/a', b ? 'c' : 'd', 'e'};
+}
+''');
+    await assertNoAssist();
+  }
+
+  test_conditional_notInLiteral() async {
+    await resolveTestUnit('''
+f(bool b) {
+  return b /*caret*/? 'c' : 'd';
+}
+''');
+    await assertNoAssist();
+  }
+
+  test_conditional_set() async {
+    await resolveTestUnit('''
+f(bool b) {
+  return {'a', b /*caret*/? 'c' : 'd', 'e'};
+}
+''');
+    await assertHasAssist('''
+f(bool b) {
+  return {'a', if (b) 'c' else 'd', 'e'};
+}
+''');
+  }
+
+  test_conditional_set_withParentheses() async {
+    await resolveTestUnit('''
+f(bool b) {
+  return {'a', ((b /*caret*/? 'c' : 'd')), 'e'};
+}
+''');
+    await assertHasAssist('''
+f(bool b) {
+  return {'a', if (b) 'c' else 'd', 'e'};
+}
+''');
+  }
+
+  test_mapFromIterable_complexKey() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  return Map.fromIt/*caret*/erable(i, key: (e) {
+    var result = e * 2;
+    return result;
+  }, value: (e) => e + 3);
+}
+''');
+    await assertNoAssist();
+  }
+
+  test_mapFromIterable_complexValue() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  return Map.fromIt/*caret*/erable(i, key: (e) => e * 2, value: (e) {
+    var result = e  + 3;
+    return result;
+  });
+}
+''');
+    await assertNoAssist();
+  }
+
+  test_mapFromIterable_differentParameterNames_usedInKey_conflictInValue() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  var k = 3;
+  return Map.fromIt/*caret*/erable(i, key: (k) => k * 2, value: (v) => k);
+}
+''');
+    await assertHasAssist('''
+f(Iterable<int> i) {
+  var k = 3;
+  return { for (var e in i) e * 2 : k };
+}
+''');
+  }
+
+  test_mapFromIterable_differentParameterNames_usedInKey_noConflictInValue() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  return Map.fromIt/*caret*/erable(i, key: (k) => k * 2, value: (v) => 0);
+}
+''');
+    await assertHasAssist('''
+f(Iterable<int> i) {
+  return { for (var k in i) k * 2 : 0 };
+}
+''');
+  }
+
+  test_mapFromIterable_differentParameterNames_usedInKeyAndValue_conflictWithDefault() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  var e = 2;
+  return Map.fromIt/*caret*/erable(i, key: (k) => k * e, value: (v) => v + e);
+}
+''');
+    await assertHasAssist('''
+f(Iterable<int> i) {
+  var e = 2;
+  return { for (var e1 in i) e1 * e : e1 + e };
+}
+''');
+  }
+
+  test_mapFromIterable_differentParameterNames_usedInKeyAndValue_noConflictWithDefault() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  return Map.fromIt/*caret*/erable(i, key: (k) => k * 2, value: (v) => v + 3);
+}
+''');
+    await assertHasAssist('''
+f(Iterable<int> i) {
+  return { for (var e in i) e * 2 : e + 3 };
+}
+''');
+  }
+
+  test_mapFromIterable_differentParameterNames_usedInValue_conflictInKey() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  int v = 0;
+  return Map.fromIt/*caret*/erable(i, key: (k) => v++, value: (v) => v * 10);
+}
+''');
+    await assertHasAssist('''
+f(Iterable<int> i) {
+  int v = 0;
+  return { for (var e in i) v++ : e * 10 };
+}
+''');
+  }
+
+  test_mapFromIterable_differentParameterNames_usedInValue_noConflictInKey() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  int index = 0;
+  return Map.fromIt/*caret*/erable(i, key: (k) => index++, value: (v) => v * 10);
+}
+''');
+    await assertHasAssist('''
+f(Iterable<int> i) {
+  int index = 0;
+  return { for (var v in i) index++ : v * 10 };
+}
+''');
+  }
+
+  test_mapFromIterable_missingKey() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  return Map.fromIt/*caret*/erable(i, value: (e) => e + 3);
+}
+''');
+    await assertNoAssist();
+  }
+
+  test_mapFromIterable_missingKeyAndValue() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  return Map.fromIt/*caret*/erable(i);
+}
+''');
+    await assertNoAssist();
+  }
+
+  test_mapFromIterable_missingValue() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  return Map.fromIt/*caret*/erable(i, key: (e) => e * 2);
+}
+''');
+    await assertNoAssist();
+  }
+
+  test_mapFromIterable_notMapFromIterable() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  return A.fromIt/*caret*/erable(i, key: (e) => e * 2, value: (e) => e + 3);
+}
+class A {
+  A.fromIterable(i, {key, value});
+}
+''');
+    await assertNoAssist();
+  }
+
+  test_mapFromIterable_sameParameterNames() async {
+    await resolveTestUnit('''
+f(Iterable<int> i) {
+  return Map.fromIt/*caret*/erable(i, key: (e) => e * 2, value: (e) => e + 3);
+}
+''');
+    await assertHasAssist('''
+f(Iterable<int> i) {
+  return { for (var e in i) e * 2 : e + 3 };
+}
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_list_literal_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_list_literal_test.dart
new file mode 100644
index 0000000..a9b106e
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_list_literal_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2019, 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/assist.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'assist_processor.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ConvertToListLiteralTest);
+  });
+}
+
+@reflectiveTest
+class ConvertToListLiteralTest extends AssistProcessorTest {
+  @override
+  AssistKind get kind => DartAssistKind.CONVERT_TO_LIST_LITERAL;
+
+  test_default_declaredType() async {
+    await resolveTestUnit('''
+List l = Li/*caret*/st();
+''');
+    await assertHasAssist('''
+List l = [];
+''');
+  }
+
+  test_default_minimal() async {
+    await resolveTestUnit('''
+var l = Li/*caret*/st();
+''');
+    await assertHasAssist('''
+var l = [];
+''');
+  }
+
+  test_default_newKeyword() async {
+    await resolveTestUnit('''
+var l = new Li/*caret*/st();
+''');
+    await assertHasAssist('''
+var l = [];
+''');
+  }
+
+  test_default_tooManyArguments() async {
+    await resolveTestUnit('''
+var l = Li/*caret*/st(5);
+''');
+    await assertNoAssist();
+  }
+
+  test_default_typeArg() async {
+    await resolveTestUnit('''
+var l = Li/*caret*/st<int>();
+''');
+    await assertHasAssist('''
+var l = <int>[];
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_map_literal_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_map_literal_test.dart
new file mode 100644
index 0000000..150b545
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_map_literal_test.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2019, 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/assist.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'assist_processor.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ConvertToMapLiteralTest);
+  });
+}
+
+@reflectiveTest
+class ConvertToMapLiteralTest extends AssistProcessorTest {
+  @override
+  AssistKind get kind => DartAssistKind.CONVERT_TO_MAP_LITERAL;
+
+  test_default_declaredType() async {
+    await resolveTestUnit('''
+Map m = Ma/*caret*/p();
+''');
+    await assertHasAssist('''
+Map m = {};
+''');
+  }
+
+  test_default_linkedHashMap() async {
+    await resolveTestUnit('''
+import 'dart:collection';
+var m = LinkedHashMa/*caret*/p();
+''');
+    await assertHasAssist('''
+import 'dart:collection';
+var m = {};
+''');
+  }
+
+  test_default_minimal() async {
+    await resolveTestUnit('''
+var m = Ma/*caret*/p();
+''');
+    await assertHasAssist('''
+var m = {};
+''');
+  }
+
+  test_default_newKeyword() async {
+    await resolveTestUnit('''
+var m = new Ma/*caret*/p();
+''');
+    await assertHasAssist('''
+var m = {};
+''');
+  }
+
+  test_default_typeArg() async {
+    await resolveTestUnit('''
+var m = Ma/*caret*/p<String, int>();
+''');
+    await assertHasAssist('''
+var m = <String, int>{};
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_set_literal_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_set_literal_test.dart
new file mode 100644
index 0000000..053bba7
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_set_literal_test.dart
@@ -0,0 +1,181 @@
+// Copyright (c) 2019, 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/assist.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'assist_processor.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ConvertToSetLiteralTest);
+  });
+}
+
+@reflectiveTest
+class ConvertToSetLiteralTest extends AssistProcessorTest {
+  @override
+  AssistKind get kind => DartAssistKind.CONVERT_TO_SET_LITERAL;
+
+  test_default_declaredType() async {
+    await resolveTestUnit('''
+Set s = S/*caret*/et();
+''');
+    await assertHasAssist('''
+Set s = {};
+''');
+  }
+
+  test_default_minimal() async {
+    await resolveTestUnit('''
+var s = S/*caret*/et();
+''');
+    await assertHasAssist('''
+var s = <dynamic>{};
+''');
+  }
+
+  test_default_newKeyword() async {
+    await resolveTestUnit('''
+var s = new S/*caret*/et();
+''');
+    await assertHasAssist('''
+var s = <dynamic>{};
+''');
+  }
+
+  test_default_typeArg() async {
+    await resolveTestUnit('''
+var s = S/*caret*/et<int>();
+''');
+    await assertHasAssist('''
+var s = <int>{};
+''');
+  }
+
+  test_from_empty() async {
+    await resolveTestUnit('''
+var s = S/*caret*/et.from([]);
+''');
+    await assertHasAssist('''
+var s = <dynamic>{};
+''');
+  }
+
+  test_from_newKeyword() async {
+    await resolveTestUnit('''
+var s = new S/*caret*/et.from([2, 3]);
+''');
+    await assertHasAssist('''
+var s = {2, 3};
+''');
+  }
+
+  test_from_noKeyword_declaredType() async {
+    await resolveTestUnit('''
+Set s = S/*caret*/et.from([2, 3]);
+''');
+    await assertHasAssist('''
+Set s = {2, 3};
+''');
+  }
+
+  test_from_noKeyword_typeArg_onConstructor() async {
+    await resolveTestUnit('''
+var s = S/*caret*/et<int>.from([2, 3]);
+''');
+    await assertHasAssist('''
+var s = <int>{2, 3};
+''');
+  }
+
+  test_from_noKeyword_typeArg_onConstructorAndLiteral() async {
+    await resolveTestUnit('''
+var s = S/*caret*/et<int>.from(<num>[2, 3]);
+''');
+    await assertHasAssist('''
+var s = <int>{2, 3};
+''');
+  }
+
+  test_from_noKeyword_typeArg_onLiteral() async {
+    await resolveTestUnit('''
+var s = S/*caret*/et.from(<int>[2, 3]);
+''');
+    await assertHasAssist('''
+var s = <int>{2, 3};
+''');
+  }
+
+  test_from_nonEmpty() async {
+    await resolveTestUnit('''
+var s = S/*caret*/et.from([2, 3]);
+''');
+    await assertHasAssist('''
+var s = {2, 3};
+''');
+  }
+
+  test_from_notALiteral() async {
+    await resolveTestUnit('''
+var l = [1];
+Set s = new S/*caret*/et.from(l);
+''');
+    await assertNoAssist();
+  }
+
+  test_from_trailingComma() async {
+    await resolveTestUnit('''
+var s = S/*caret*/et.from([2, 3,]);
+''');
+    await assertHasAssist('''
+var s = {2, 3,};
+''');
+  }
+
+  test_toSet_empty() async {
+    await resolveTestUnit('''
+var s = [].to/*caret*/Set();
+''');
+    await assertHasAssist('''
+var s = <dynamic>{};
+''');
+  }
+
+  test_toSet_empty_typeArg() async {
+    await resolveTestUnit('''
+var s = <int>[].to/*caret*/Set();
+''');
+    await assertHasAssist('''
+var s = <int>{};
+''');
+  }
+
+  test_toSet_nonEmpty() async {
+    await resolveTestUnit('''
+var s = [2, 3].to/*caret*/Set();
+''');
+    await assertHasAssist('''
+var s = {2, 3};
+''');
+  }
+
+  test_toSet_nonEmpty_typeArg() async {
+    await resolveTestUnit('''
+var s = <int>[2, 3].to/*caret*/Set();
+''');
+    await assertHasAssist('''
+var s = <int>{2, 3};
+''');
+  }
+
+  test_toSet_notALiteral() async {
+    await resolveTestUnit('''
+var l = [];
+var s = l.to/*caret*/Set();
+''');
+    await assertNoAssist();
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_spread_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_spread_test.dart
new file mode 100644
index 0000000..648502f
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_spread_test.dart
@@ -0,0 +1,129 @@
+// Copyright (c) 2019, 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/assist.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'assist_processor.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ConvertToSpreadTest);
+    defineReflectiveTests(ConvertToSpreadWithControlFlowTest);
+  });
+}
+
+@reflectiveTest
+class ConvertToSpreadTest extends AssistProcessorTest {
+  @override
+  AssistKind get kind => DartAssistKind.CONVERT_TO_SPREAD;
+
+  void setUp() {
+    createAnalysisOptionsFile(experiments: [EnableString.spread_collections]);
+    super.setUp();
+  }
+
+  test_addAll_expression() async {
+    await resolveTestUnit('''
+f() {
+  var ints = [1, 2, 3];
+  print(['a']..addAl/*caret*/l(ints.map((i) => i.toString()))..addAll(['c']));
+}
+''');
+    await assertHasAssist('''
+f() {
+  var ints = [1, 2, 3];
+  print(['a', ...ints.map((i) => i.toString())]..addAll(['c']));
+}
+''');
+  }
+
+  test_addAll_literal() async {
+    await resolveTestUnit('''
+var l = ['a']..add/*caret*/All(['b'])..addAll(['c']);
+''');
+    await assertHasAssist('''
+var l = ['a', ...['b']]..addAll(['c']);
+''');
+  }
+
+  test_addAll_nonLiteralTarget() async {
+    await resolveTestUnit('''
+var l1 = [];
+var l2 = l1..addAl/*caret*/l(['b'])..addAll(['c']);
+''');
+    await assertNoAssist();
+  }
+
+  test_addAll_notFirst() async {
+    await resolveTestUnit('''
+var l = ['a']..addAll(['b'])../*caret*/addAll(['c']);
+''');
+    await assertNoAssist();
+  }
+
+  test_addAll_nullAware_const() async {
+    await resolveTestUnit('''
+var things;
+var l = ['a']..add/*caret*/All(things ?? const []);
+''');
+    await assertHasAssist('''
+var things;
+var l = ['a', ...?things];
+''');
+  }
+
+  test_addAll_nullAware_nonConst() async {
+    await resolveTestUnit('''
+var things;
+var l = ['a']..add/*caret*/All(things ?? []);
+''');
+    await assertHasAssist('''
+var things;
+var l = ['a', ...?things];
+''');
+  }
+}
+
+@reflectiveTest
+class ConvertToSpreadWithControlFlowTest extends AssistProcessorTest {
+  @override
+  AssistKind get kind => DartAssistKind.CONVERT_TO_SPREAD;
+
+  void setUp() {
+    createAnalysisOptionsFile(experiments: [
+      EnableString.control_flow_collections,
+      EnableString.spread_collections
+    ]);
+    super.setUp();
+  }
+
+  test_addAll_condition_const() async {
+    await resolveTestUnit('''
+bool condition;
+var things;
+var l = ['a']..add/*caret*/All(condition ? things : const []);
+''');
+    await assertHasAssist('''
+bool condition;
+var things;
+var l = ['a', if (condition) ...things];
+''');
+  }
+
+  test_addAll_condition_nonConst() async {
+    await resolveTestUnit('''
+bool condition;
+var things;
+var l = ['a']..add/*caret*/All(condition ? things : []);
+''');
+    await assertHasAssist('''
+bool condition;
+var things;
+var l = ['a', if (condition) ...things];
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
index f4d13a4..d3aaded 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
@@ -36,12 +36,10 @@
 
 class MyWidget extends StatefulWidget {
   @override
-  MyWidgetState createState() {
-    return MyWidgetState();
-  }
+  _MyWidgetState createState() => _MyWidgetState();
 }
 
-class MyWidgetState extends State<MyWidget> {
+class _MyWidgetState extends State<MyWidget> {
   @override
   Widget build(BuildContext context) {
     return Container();
@@ -103,12 +101,10 @@
   }
 
   @override
-  MyWidgetState createState() {
-    return MyWidgetState();
-  }
+  _MyWidgetState createState() => _MyWidgetState();
 }
 
-class MyWidgetState extends State<MyWidget> {
+class _MyWidgetState extends State<MyWidget> {
   String instanceField4;
 
   String instanceField5;
@@ -165,16 +161,14 @@
 
 class MyWidget extends StatefulWidget {
   @override
-  MyWidgetState createState() {
-    return MyWidgetState();
-  }
+  _MyWidgetState createState() => _MyWidgetState();
 
   static String get staticGetter1 => '';
 
   static String get staticGetter2 => '';
 }
 
-class MyWidgetState extends State<MyWidget> {
+class _MyWidgetState extends State<MyWidget> {
   @override
   Widget build(BuildContext context) {
     return Row(
@@ -246,9 +240,7 @@
   MyWidget(this.instanceField1);
 
   @override
-  MyWidgetState createState() {
-    return MyWidgetState();
-  }
+  _MyWidgetState createState() => _MyWidgetState();
 
   static void staticMethod1() {
     print('static 1');
@@ -259,7 +251,7 @@
   }
 }
 
-class MyWidgetState extends State<MyWidget> {
+class _MyWidgetState extends State<MyWidget> {
   String instanceField2;
 
   @override
@@ -349,12 +341,10 @@
   const MyWidget(this.aaa, this.bbb);
 
   @override
-  MyWidgetState createState() {
-    return MyWidgetState();
-  }
+  _MyWidgetState createState() => _MyWidgetState();
 }
 
-class MyWidgetState extends State<MyWidget> {
+class _MyWidgetState extends State<MyWidget> {
   @override
   Widget build(BuildContext context) {
     return Row(
@@ -387,12 +377,10 @@
 
 class MyWidget extends StatefulWidget {
   @override
-  MyWidgetState createState() {
-    return MyWidgetState();
-  }
+  _MyWidgetState createState() => _MyWidgetState();
 }
 
-class MyWidgetState extends State<MyWidget> {
+class _MyWidgetState extends State<MyWidget> {
   @override
   Widget build(BuildContext context) {
     return Container();
diff --git a/pkg/analysis_server/test/src/services/correction/assist/test_all.dart b/pkg/analysis_server/test/src/services/correction/assist/test_all.dart
index a090848..86c5872 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/test_all.dart
@@ -25,11 +25,16 @@
 import 'convert_to_double_quoted_string_test.dart'
     as convert_to_double_quoted_string;
 import 'convert_to_field_parameter_test.dart' as convert_to_field_parameter;
+import 'convert_to_if_element_test.dart' as convert_to_if_element;
 import 'convert_to_int_literal_test.dart' as convert_to_int_literal;
+import 'convert_to_list_literal_test.dart' as convert_to_list_literal;
+import 'convert_to_map_literal_test.dart' as convert_to_map_literal;
 import 'convert_to_multiline_string_test.dart' as convert_to_multiline_string;
 import 'convert_to_normal_parameter_test.dart' as convert_to_normal_parameter;
+import 'convert_to_set_literal_test.dart' as convert_to_set_literal;
 import 'convert_to_single_quoted_string_test.dart'
     as convert_to_single_quoted_string;
+import 'convert_to_spread_test.dart' as convert_to_spread;
 import 'encapsulate_field_test.dart' as encapsulate_field;
 import 'exchange_operands_test.dart' as exchange_operands;
 import 'flutter_convert_to_children_test.dart' as flutter_convert_to_children;
@@ -88,10 +93,15 @@
     convert_part_of_to_uri.main();
     convert_to_double_quoted_string.main();
     convert_to_field_parameter.main();
+    convert_to_if_element.main();
     convert_to_int_literal.main();
+    convert_to_list_literal.main();
+    convert_to_map_literal.main();
     convert_to_multiline_string.main();
     convert_to_normal_parameter.main();
+    convert_to_set_literal.main();
     convert_to_single_quoted_string.main();
+    convert_to_spread.main();
     encapsulate_field.main();
     exchange_operands.main();
     flutter_convert_to_children.main();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
index 20df52c..57ff490 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
@@ -325,6 +325,7 @@
   test(abc: null);
 }
 ''');
+    assertLinkedGroup(change.linkedEditGroups[0], ['null);']);
   }
 
   test_single_normal() async {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_with_literal_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_with_literal_test.dart
deleted file mode 100644
index 7ec1088..0000000
--- a/pkg/analysis_server/test/src/services/correction/fix/replace_with_literal_test.dart
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2018, 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/fix.dart';
-import 'package:analysis_server/src/services/correction/fix_internal.dart';
-import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
-import 'package:test_reflective_loader/test_reflective_loader.dart';
-
-import 'fix_processor.dart';
-
-main() {
-  defineReflectiveSuite(() {
-    defineReflectiveTests(ReplaceWithLiteralTest);
-  });
-}
-
-@reflectiveTest
-class ReplaceWithLiteralTest extends FixProcessorLintTest {
-  @override
-  FixKind get kind => DartFixKind.REPLACE_WITH_LITERAL;
-
-  @override
-  String get lintCode => LintNames.prefer_collection_literals;
-
-  test_linkedHashMap_withCommentsInGeneric() async {
-    await resolveTestUnit('''
-import 'dart:collection';
-
-final a = /*LINT*/new LinkedHashMap<int,/*comment*/int>();
-''');
-    await assertHasFix('''
-import 'dart:collection';
-
-final a = /*LINT*/<int,/*comment*/int>{};
-''');
-  }
-
-  test_linkedHashMap_withDynamicGenerics() async {
-    await resolveTestUnit('''
-import 'dart:collection';
-
-final a = /*LINT*/new LinkedHashMap<dynamic,dynamic>();
-''');
-    await assertHasFix('''
-import 'dart:collection';
-
-final a = /*LINT*/<dynamic,dynamic>{};
-''');
-  }
-
-  test_linkedHashMap_withGeneric() async {
-    await resolveTestUnit('''
-import 'dart:collection';
-
-final a = /*LINT*/new LinkedHashMap<int,int>();
-''');
-    await assertHasFix('''
-import 'dart:collection';
-
-final a = /*LINT*/<int,int>{};
-''');
-  }
-
-  test_linkedHashMap_withoutGeneric() async {
-    await resolveTestUnit('''
-import 'dart:collection';
-
-final a = /*LINT*/new LinkedHashMap();
-''');
-    await assertHasFix('''
-import 'dart:collection';
-
-final a = /*LINT*/{};
-''');
-  }
-
-  test_list_withGeneric() async {
-    await resolveTestUnit('''
-final a = /*LINT*/new List<int>();
-''');
-    await assertHasFix('''
-final a = /*LINT*/<int>[];
-''');
-  }
-
-  test_list_withoutGeneric() async {
-    await resolveTestUnit('''
-final a = /*LINT*/new List();
-''');
-    await assertHasFix('''
-final a = /*LINT*/[];
-''');
-  }
-
-  test_map_withGeneric() async {
-    await resolveTestUnit('''
-final a = /*LINT*/new Map<int,int>();
-''');
-    await assertHasFix('''
-final a = /*LINT*/<int,int>{};
-''');
-  }
-
-  test_map_withoutGeneric() async {
-    await resolveTestUnit('''
-final a = /*LINT*/new Map();
-''');
-    await assertHasFix('''
-final a = /*LINT*/{};
-''');
-  }
-}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index 30cad40..6a259e9 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -86,7 +86,6 @@
 import 'replace_with_conditional_assignment_test.dart'
     as replace_with_conditional_assignment;
 import 'replace_with_identifier_test.dart' as replace_with_identifier;
-import 'replace_with_literal_test.dart' as replace_with_literal;
 import 'replace_with_null_aware_test.dart' as replace_with_null_aware;
 import 'replace_with_tear_off_test.dart' as replace_with_tear_off;
 import 'update_sdk_constraints_test.dart' as update_sdk_constraints;
@@ -170,7 +169,6 @@
     replace_var_with_dynamic.main();
     replace_with_brackets.main();
     replace_with_conditional_assignment.main();
-    replace_with_literal.main();
     replace_with_identifier.main();
     replace_with_null