Version 2.14.0-323.0.dev

Merge commit 'c3faa95568a2419e9c22510890923c78cec6b995' into 'dev'
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index f63580d..2e13ba2 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -4770,13 +4770,13 @@
           The one-based index of the column containing the first character of
           the range.
         </p>
-      </dd><dt class="field"><b>endLine: int</b></dt><dd>
+      </dd><dt class="field"><b>endLine: int<span style="color:#999999"> (optional)</span></b></dt><dd>
         
         <p>
           The one-based index of the line containing the character immediately
           following the range.
         </p>
-      </dd><dt class="field"><b>endColumn: int</b></dt><dd>
+      </dd><dt class="field"><b>endColumn: int<span style="color:#999999"> (optional)</span></b></dt><dd>
         
         <p>
           The one-based index of the column containing the character immediately
diff --git a/pkg/analysis_server/lib/src/computer/computer_outline.dart b/pkg/analysis_server/lib/src/computer/computer_outline.dart
index 46408c8..c4247be 100644
--- a/pkg/analysis_server/lib/src/computer/computer_outline.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_outline.dart
@@ -85,8 +85,8 @@
     var endLocation = resolvedUnit.lineInfo.getLocation(offset + length);
     var endLine = endLocation.lineNumber;
     var endColumn = endLocation.columnNumber;
-    return Location(
-        path, offset, length, startLine, startColumn, endLine, endColumn);
+    return Location(path, offset, length, startLine, startColumn,
+        endLine: endLine, endColumn: endColumn);
   }
 
   Outline _newClassOutline(ClassDeclaration node, List<Outline> classContents) {
diff --git a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
index d618f66..54ffabf 100644
--- a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
+++ b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
@@ -261,8 +261,8 @@
       0, // length
       declaration.locationStartLine,
       declaration.locationStartColumn,
-      declaration.locationStartLine, // endLine
-      declaration.locationStartColumn, // endColumn
+      endLine: declaration.locationStartLine,
+      endColumn: declaration.locationStartColumn,
     ),
     parameters: declaration.parameters,
     returnType: declaration.returnType,
diff --git a/pkg/analysis_server/lib/src/protocol_server.dart b/pkg/analysis_server/lib/src/protocol_server.dart
index 5283798..7ad36bd 100644
--- a/pkg/analysis_server/lib/src/protocol_server.dart
+++ b/pkg/analysis_server/lib/src/protocol_server.dart
@@ -126,8 +126,8 @@
     var endLine = endLocation.lineNumber;
     var endColumn = endLocation.columnNumber;
 
-    location = Location(
-        file, offset, length, startLine, startColumn, endLine, endColumn);
+    location = Location(file, offset, length, startLine, startColumn,
+        endLine: endLine, endColumn: endColumn);
   }
 
   // Default to the error's severity if none is specified.
@@ -171,8 +171,8 @@
 
   return DiagnosticMessage(
       message.messageText(includeUrl: true),
-      Location(
-          file, offset, length, startLine, startColumn, endLine, endColumn));
+      Location(file, offset, length, startLine, startColumn,
+          endLine: endLine, endColumn: endColumn));
 }
 
 /// Create a Location based on an [engine.Element].
@@ -317,5 +317,6 @@
     //  should be able to throw an exception. Try removing the try statement.
   }
   return Location(unitElement.source.fullName, range.offset, range.length,
-      startLine, startColumn, endLine, endColumn);
+      startLine, startColumn,
+      endLine: endLine, endColumn: endColumn);
 }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart b/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
index 8812c24..26a1a94 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
@@ -158,7 +158,8 @@
     bool isDeprecated = false}) {
   var name = id.name;
   // TODO(danrubel) use lineInfo to determine startLine and startColumn
-  var location = Location(source.fullName, id.offset, id.length, 0, 0, 0, 0);
+  var location = Location(source.fullName, id.offset, id.length, 0, 0,
+      endLine: 0, endColumn: 0);
   var flags = protocol.Element.makeFlags(
       isAbstract: isAbstract,
       isDeprecated: isDeprecated,
diff --git a/pkg/analysis_server/test/edit/fixes_test.dart b/pkg/analysis_server/test/edit/fixes_test.dart
index 53ba68b..ff64169 100644
--- a/pkg/analysis_server/test/edit/fixes_test.dart
+++ b/pkg/analysis_server/test/edit/fixes_test.dart
@@ -73,7 +73,7 @@
     var fixes = plugin.AnalysisErrorFixes(AnalysisError(
         AnalysisErrorSeverity.ERROR,
         AnalysisErrorType.HINT,
-        Location('', 0, 0, 0, 0, 0, 0),
+        Location('', 0, 0, 0, 0, endLine: 0, endColumn: 0),
         'message',
         'code'));
     var result = plugin.EditGetFixesResult(<plugin.AnalysisErrorFixes>[fixes]);
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index 3c0e282..3c3eecb 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -1104,15 +1104,16 @@
 ///   "length": int
 ///   "startLine": int
 ///   "startColumn": int
-///   "endLine": int
-///   "endColumn": int
+///   "endLine": optional int
+///   "endColumn": optional int
 /// }
 final Matcher isLocation = LazyMatcher(() => MatchesJsonObject('Location', {
       'file': isFilePath,
       'offset': isInt,
       'length': isInt,
       'startLine': isInt,
-      'startColumn': isInt,
+      'startColumn': isInt
+    }, optionalFields: {
       'endLine': isInt,
       'endColumn': isInt
     }));
diff --git a/pkg/analysis_server/test/lsp/diagnostic_test.dart b/pkg/analysis_server/test/lsp/diagnostic_test.dart
index 0f7ecf1..96c7f5a 100644
--- a/pkg/analysis_server/test/lsp/diagnostic_test.dart
+++ b/pkg/analysis_server/test/lsp/diagnostic_test.dart
@@ -30,12 +30,15 @@
     final pluginError = plugin.AnalysisError(
       plugin.AnalysisErrorSeverity.ERROR,
       plugin.AnalysisErrorType.STATIC_TYPE_WARNING,
-      plugin.Location(pluginAnalyzedFilePath, 0, 6, 0, 0, 0, 6),
+      plugin.Location(pluginAnalyzedFilePath, 0, 6, 0, 0,
+          endLine: 0, endColumn: 6),
       'Test error from plugin',
       'ERR1',
       contextMessages: [
-        plugin.DiagnosticMessage('Related error',
-            plugin.Location(pluginAnalyzedFilePath, 31, 4, 1, 12, 1, 16))
+        plugin.DiagnosticMessage(
+            'Related error',
+            plugin.Location(pluginAnalyzedFilePath, 31, 4, 1, 12,
+                endLine: 1, endColumn: 16))
       ],
     );
     final pluginResult =
@@ -317,7 +320,7 @@
     final pluginError = plugin.AnalysisError(
       plugin.AnalysisErrorSeverity.ERROR,
       plugin.AnalysisErrorType.STATIC_TYPE_WARNING,
-      plugin.Location(mainFilePath, 0, 1, 0, 0, 0, 1),
+      plugin.Location(mainFilePath, 0, 1, 0, 0, endLine: 0, endColumn: 1),
       pluginErrorMessage,
       'ERR1',
     );
diff --git a/pkg/analysis_server/test/services/correction/status_test.dart b/pkg/analysis_server/test/services/correction/status_test.dart
index 41c0eec..055c0e4 100644
--- a/pkg/analysis_server/test/services/correction/status_test.dart
+++ b/pkg/analysis_server/test/services/correction/status_test.dart
@@ -98,7 +98,7 @@
   }
 
   void test_addFatalError_withLocation() {
-    var location = Location('/test.dart', 1, 2, 3, 4, 5, 6);
+    var location = Location('/test.dart', 1, 2, 3, 4, endLine: 5, endColumn: 6);
     var refactoringStatus = RefactoringStatus();
     // initial state
     expect(refactoringStatus.severity, null);
@@ -210,7 +210,7 @@
   }
 
   void test_newError() {
-    var location = Location('/test.dart', 1, 2, 3, 4, 5, 6);
+    var location = Location('/test.dart', 1, 2, 3, 4, endLine: 5, endColumn: 6);
     var refactoringStatus = RefactoringStatus.error('msg', location);
     var problem = refactoringStatus.problem!;
     var problemLocation = problem.location!;
diff --git a/pkg/analysis_server/test/src/plugin/protocol_test_utilities.dart b/pkg/analysis_server/test/src/plugin/protocol_test_utilities.dart
index 631dcac..4ad6049 100644
--- a/pkg/analysis_server/test/src/plugin/protocol_test_utilities.dart
+++ b/pkg/analysis_server/test/src/plugin/protocol_test_utilities.dart
@@ -79,7 +79,8 @@
   Element element(int stringIndex, int intIndex, {ElementKind? kind}) =>
       Element(kind ?? ElementKind.CLASS, strings[stringIndex++], intIndex++,
           location: Location(fileName(stringIndex++), intIndex++, intIndex++,
-              intIndex++, intIndex++, intIndex++, intIndex++),
+              intIndex++, intIndex++,
+              endLine: intIndex++, endColumn: intIndex++),
           parameters: strings[stringIndex++],
           returnType: strings[stringIndex++],
           typeParameters: strings[stringIndex++]);
@@ -99,8 +100,8 @@
       intIndex++,
       intIndex++,
       intIndex++,
-      intIndex++,
-      intIndex++);
+      endLine: intIndex++,
+      endColumn: intIndex++);
 
   /// On return, increment [stringIndex] by 5 and [intIndex] by 7.
   Occurrences occurrences(int stringIndex, int intIndex) {
diff --git a/pkg/analysis_server/test/src/plugin/result_merger_test.dart b/pkg/analysis_server/test/src/plugin/result_merger_test.dart
index 934e335..9affdc3 100644
--- a/pkg/analysis_server/test/src/plugin/result_merger_test.dart
+++ b/pkg/analysis_server/test/src/plugin/result_merger_test.dart
@@ -29,7 +29,8 @@
     AnalysisError createError(int offset) {
       var severity = AnalysisErrorSeverity.ERROR;
       var type = AnalysisErrorType.HINT;
-      var location = Location('test.dart', offset, 2, 3, 4, 5, 6);
+      var location =
+          Location('test.dart', offset, 2, 3, 4, endLine: 5, endColumn: 6);
       return AnalysisError(severity, type, location, '', '');
     }
 
@@ -68,7 +69,8 @@
     AnalysisError createError(int offset) {
       var severity = AnalysisErrorSeverity.ERROR;
       var type = AnalysisErrorType.HINT;
-      var location = Location('test.dart', offset, 2, 3, 4, 5, 6);
+      var location =
+          Location('test.dart', offset, 2, 3, 4, endLine: 5, endColumn: 6);
       return AnalysisError(severity, type, location, '', '');
     }
 
@@ -265,7 +267,7 @@
 
   void test_mergeOutline() {
     Element element(ElementKind kind, int offset) {
-      var location = Location('', offset, 0, 0, 0, 0, 0);
+      var location = Location('', offset, 0, 0, 0, endLine: 0, endColumn: 0);
       return Element(kind, '', 0, location: location);
     }
 
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test.dart
index e2c4988..e983738 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test.dart
@@ -6,7 +6,6 @@
 import 'package:analysis_server/src/services/correction/dart/data_driven.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_manager.dart';
 import 'package:analysis_server/src/services/correction/fix_internal.dart';
-import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/Location.java b/pkg/analysis_server/tool/spec/generated/java/types/Location.java
index 1df7d78..13e14bd 100644
--- a/pkg/analysis_server/tool/spec/generated/java/types/Location.java
+++ b/pkg/analysis_server/tool/spec/generated/java/types/Location.java
@@ -63,17 +63,17 @@
   /**
    * The one-based index of the line containing the character immediately following the range.
    */
-  private final int endLine;
+  private final Integer endLine;
 
   /**
    * The one-based index of the column containing the character immediately following the range.
    */
-  private final int endColumn;
+  private final Integer endColumn;
 
   /**
    * Constructor for {@link Location}.
    */
-  public Location(String file, int offset, int length, int startLine, int startColumn, int endLine, int endColumn) {
+  public Location(String file, int offset, int length, int startLine, int startColumn, Integer endLine, Integer endColumn) {
     this.file = file;
     this.offset = offset;
     this.length = length;
@@ -93,8 +93,8 @@
         other.length == length &&
         other.startLine == startLine &&
         other.startColumn == startColumn &&
-        other.endLine == endLine &&
-        other.endColumn == endColumn;
+        ObjectUtilities.equals(other.endLine, endLine) &&
+        ObjectUtilities.equals(other.endColumn, endColumn);
     }
     return false;
   }
@@ -105,8 +105,8 @@
     int length = jsonObject.get("length").getAsInt();
     int startLine = jsonObject.get("startLine").getAsInt();
     int startColumn = jsonObject.get("startColumn").getAsInt();
-    int endLine = jsonObject.get("endLine").getAsInt();
-    int endColumn = jsonObject.get("endColumn").getAsInt();
+    Integer endLine = jsonObject.get("endLine") == null ? null : jsonObject.get("endLine").getAsInt();
+    Integer endColumn = jsonObject.get("endColumn") == null ? null : jsonObject.get("endColumn").getAsInt();
     return new Location(file, offset, length, startLine, startColumn, endLine, endColumn);
   }
 
@@ -125,14 +125,14 @@
   /**
    * The one-based index of the column containing the character immediately following the range.
    */
-  public int getEndColumn() {
+  public Integer getEndColumn() {
     return endColumn;
   }
 
   /**
    * The one-based index of the line containing the character immediately following the range.
    */
-  public int getEndLine() {
+  public Integer getEndLine() {
     return endLine;
   }
 
@@ -191,8 +191,12 @@
     jsonObject.addProperty("length", length);
     jsonObject.addProperty("startLine", startLine);
     jsonObject.addProperty("startColumn", startColumn);
-    jsonObject.addProperty("endLine", endLine);
-    jsonObject.addProperty("endColumn", endColumn);
+    if (endLine != null) {
+      jsonObject.addProperty("endLine", endLine);
+    }
+    if (endColumn != null) {
+      jsonObject.addProperty("endColumn", endColumn);
+    }
     return jsonObject;
   }
 
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
index dfbd44c..76b7a2c 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
@@ -2884,8 +2884,8 @@
 ///   "length": int
 ///   "startLine": int
 ///   "startColumn": int
-///   "endLine": int
-///   "endColumn": int
+///   "endLine": optional int
+///   "endColumn": optional int
 /// }
 ///
 /// Clients may not extend, implement or mix-in this class.
@@ -2909,14 +2909,15 @@
 
   /// The one-based index of the line containing the character immediately
   /// following the range.
-  int endLine;
+  int? endLine;
 
   /// The one-based index of the column containing the character immediately
   /// following the range.
-  int endColumn;
+  int? endColumn;
 
-  Location(this.file, this.offset, this.length, this.startLine,
-      this.startColumn, this.endLine, this.endColumn);
+  Location(
+      this.file, this.offset, this.length, this.startLine, this.startColumn,
+      {this.endLine, this.endColumn});
 
   factory Location.fromJson(
       JsonDecoder jsonDecoder, String jsonPath, Object? json) {
@@ -2954,21 +2955,17 @@
       } else {
         throw jsonDecoder.mismatch(jsonPath, 'startColumn');
       }
-      int endLine;
+      int? endLine;
       if (json.containsKey('endLine')) {
         endLine = jsonDecoder.decodeInt(jsonPath + '.endLine', json['endLine']);
-      } else {
-        throw jsonDecoder.mismatch(jsonPath, 'endLine');
       }
-      int endColumn;
+      int? endColumn;
       if (json.containsKey('endColumn')) {
         endColumn =
             jsonDecoder.decodeInt(jsonPath + '.endColumn', json['endColumn']);
-      } else {
-        throw jsonDecoder.mismatch(jsonPath, 'endColumn');
       }
-      return Location(
-          file, offset, length, startLine, startColumn, endLine, endColumn);
+      return Location(file, offset, length, startLine, startColumn,
+          endLine: endLine, endColumn: endColumn);
     } else {
       throw jsonDecoder.mismatch(jsonPath, 'Location', json);
     }
@@ -2982,8 +2979,14 @@
     result['length'] = length;
     result['startLine'] = startLine;
     result['startColumn'] = startColumn;
-    result['endLine'] = endLine;
-    result['endColumn'] = endColumn;
+    var endLine = this.endLine;
+    if (endLine != null) {
+      result['endLine'] = endLine;
+    }
+    var endColumn = this.endColumn;
+    if (endColumn != null) {
+      result['endColumn'] = endColumn;
+    }
     return result;
   }
 
diff --git a/pkg/analyzer_plugin/doc/api.html b/pkg/analyzer_plugin/doc/api.html
index dbe9f26..96c8505 100644
--- a/pkg/analyzer_plugin/doc/api.html
+++ b/pkg/analyzer_plugin/doc/api.html
@@ -1579,13 +1579,13 @@
           The one-based index of the column containing the first character of
           the range.
         </p>
-      </dd><dt class="field"><b>endLine: int</b></dt><dd>
+      </dd><dt class="field"><b>endLine: int<span style="color:#999999"> (optional)</span></b></dt><dd>
         
         <p>
           The one-based index of the line containing the character immediately
           following the range.
         </p>
-      </dd><dt class="field"><b>endColumn: int</b></dt><dd>
+      </dd><dt class="field"><b>endColumn: int<span style="color:#999999"> (optional)</span></b></dt><dd>
         
         <p>
           The one-based index of the column containing the character immediately
diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
index 691c48d..e437a89 100644
--- a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
+++ b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
@@ -2884,8 +2884,8 @@
 ///   "length": int
 ///   "startLine": int
 ///   "startColumn": int
-///   "endLine": int
-///   "endColumn": int
+///   "endLine": optional int
+///   "endColumn": optional int
 /// }
 ///
 /// Clients may not extend, implement or mix-in this class.
@@ -2909,14 +2909,15 @@
 
   /// The one-based index of the line containing the character immediately
   /// following the range.
-  int endLine;
+  int? endLine;
 
   /// The one-based index of the column containing the character immediately
   /// following the range.
-  int endColumn;
+  int? endColumn;
 
-  Location(this.file, this.offset, this.length, this.startLine,
-      this.startColumn, this.endLine, this.endColumn);
+  Location(
+      this.file, this.offset, this.length, this.startLine, this.startColumn,
+      {this.endLine, this.endColumn});
 
   factory Location.fromJson(
       JsonDecoder jsonDecoder, String jsonPath, Object? json) {
@@ -2954,21 +2955,17 @@
       } else {
         throw jsonDecoder.mismatch(jsonPath, 'startColumn');
       }
-      int endLine;
+      int? endLine;
       if (json.containsKey('endLine')) {
         endLine = jsonDecoder.decodeInt(jsonPath + '.endLine', json['endLine']);
-      } else {
-        throw jsonDecoder.mismatch(jsonPath, 'endLine');
       }
-      int endColumn;
+      int? endColumn;
       if (json.containsKey('endColumn')) {
         endColumn =
             jsonDecoder.decodeInt(jsonPath + '.endColumn', json['endColumn']);
-      } else {
-        throw jsonDecoder.mismatch(jsonPath, 'endColumn');
       }
-      return Location(
-          file, offset, length, startLine, startColumn, endLine, endColumn);
+      return Location(file, offset, length, startLine, startColumn,
+          endLine: endLine, endColumn: endColumn);
     } else {
       throw jsonDecoder.mismatch(jsonPath, 'Location', json);
     }
@@ -2982,8 +2979,14 @@
     result['length'] = length;
     result['startLine'] = startLine;
     result['startColumn'] = startColumn;
-    result['endLine'] = endLine;
-    result['endColumn'] = endColumn;
+    var endLine = this.endLine;
+    if (endLine != null) {
+      result['endLine'] = endLine;
+    }
+    var endColumn = this.endColumn;
+    if (endColumn != null) {
+      result['endColumn'] = endColumn;
+    }
     return result;
   }
 
diff --git a/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart b/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart
index 87af1a3..575cd5b 100644
--- a/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart
+++ b/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart
@@ -48,8 +48,9 @@
     return plugin.AnalysisError(
         convertErrorSeverity(severity),
         convertErrorType(errorCode.type),
-        plugin.Location(error.source.fullName, offset, error.length, startLine,
-            startColumn, endLine, endColumn),
+        plugin.Location(
+            error.source.fullName, offset, error.length, startLine, startColumn,
+            endLine: endLine, endColumn: endColumn),
         error.message,
         errorCode.name.toLowerCase(),
         contextMessages: contextMessages,
@@ -107,8 +108,8 @@
     }
     return plugin.DiagnosticMessage(
         message.messageText(includeUrl: true),
-        plugin.Location(
-            file, offset, length, startLine, startColumn, endLine, endColumn));
+        plugin.Location(file, offset, length, startLine, startColumn,
+            endLine: endLine, endColumn: endColumn));
   }
 
   /// Convert the given [element] from the 'analyzer' package to an element
@@ -407,6 +408,7 @@
       // Ignore exceptions
     }
     return plugin.Location(unitElement.source.fullName, range.offset,
-        range.length, startLine, startColumn, endLine, endColumn);
+        range.length, startLine, startColumn,
+        endLine: endLine, endColumn: endColumn);
   }
 }
diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
index 19d5b1e..c161993 100644
--- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
@@ -261,7 +261,8 @@
               uriNode.offset,
               uriNode.length,
               protocol.ElementKind.LIBRARY,
-              protocol.Location(source.fullName, 0, 0, 0, 0, 0, 0));
+              protocol.Location(source.fullName, 0, 0, 0, 0,
+                  endLine: 0, endColumn: 0));
         }
       }
     }
diff --git a/pkg/analyzer_plugin/pubspec.yaml b/pkg/analyzer_plugin/pubspec.yaml
index 1ee70fe..2b5441c 100644
--- a/pkg/analyzer_plugin/pubspec.yaml
+++ b/pkg/analyzer_plugin/pubspec.yaml
@@ -1,6 +1,6 @@
 name: analyzer_plugin
 description: A framework and support code for building plugins for the analysis server.
-version: 0.6.0
+version: 0.7.0-dev
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_plugin
 
 environment:
diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
index 5c52f19..4ed7683 100644
--- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
+++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
@@ -591,15 +591,16 @@
 ///   "length": int
 ///   "startLine": int
 ///   "startColumn": int
-///   "endLine": int
-///   "endColumn": int
+///   "endLine": optional int
+///   "endColumn": optional int
 /// }
 final Matcher isLocation = LazyMatcher(() => MatchesJsonObject('Location', {
       'file': isFilePath,
       'offset': isInt,
       'length': isInt,
       'startLine': isInt,
-      'startColumn': isInt,
+      'startColumn': isInt
+    }, optionalFields: {
       'endLine': isInt,
       'endColumn': isInt
     }));
diff --git a/pkg/analyzer_plugin/test/plugin/navigation_mixin_test.dart b/pkg/analyzer_plugin/test/plugin/navigation_mixin_test.dart
index 2c3dcfd..3c29dfb 100644
--- a/pkg/analyzer_plugin/test/plugin/navigation_mixin_test.dart
+++ b/pkg/analyzer_plugin/test/plugin/navigation_mixin_test.dart
@@ -80,8 +80,8 @@
   void computeNavigation(
       NavigationRequest request, NavigationCollector collector) {
     for (var i = 0; i < regionCount; i++) {
-      collector.addRegion(
-          i, 5, ElementKind.METHOD, Location('a', 5, 5, 1, 5, 1, 10));
+      collector.addRegion(i, 5, ElementKind.METHOD,
+          Location('a', 5, 5, 1, 5, endLine: 1, endColumn: 10));
     }
   }
 }
diff --git a/pkg/analyzer_plugin/test/src/utilities/navigation/navigation_test.dart b/pkg/analyzer_plugin/test/src/utilities/navigation/navigation_test.dart
index 45179b0..5a76e0f 100644
--- a/pkg/analyzer_plugin/test/src/utilities/navigation/navigation_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/navigation/navigation_test.dart
@@ -24,14 +24,16 @@
     var targetStartColumnA1 = 4;
     var targetKindA1 = ElementKind.CLASS;
     var targetLocationA1 = Location(fileA, targetOffsetA1, targetLengthA1,
-        targetStartLineA1, targetStartColumnA1, 0, 0);
+        targetStartLineA1, targetStartColumnA1,
+        endLine: 0, endColumn: 0);
     var targetOffsetA2 = 5;
     var targetLengthA2 = 6;
     var targetStartLineA2 = 7;
     var targetStartColumnA2 = 8;
     var targetKindA2 = ElementKind.FUNCTION;
     var targetLocationA2 = Location(fileA, targetOffsetA2, targetLengthA2,
-        targetStartLineA2, targetStartColumnA2, 0, 0);
+        targetStartLineA2, targetStartColumnA2,
+        endLine: 0, endColumn: 0);
 
     var fileB = 'b.dart';
     var targetOffsetB1 = 9;
@@ -40,14 +42,16 @@
     var targetStartColumnB1 = 12;
     var targetKindB1 = ElementKind.ENUM;
     var targetLocationB1 = Location(fileB, targetOffsetB1, targetLengthB1,
-        targetStartLineB1, targetStartColumnB1, 0, 0);
+        targetStartLineB1, targetStartColumnB1,
+        endLine: 0, endColumn: 0);
     var targetOffsetB2 = 13;
     var targetLengthB2 = 14;
     var targetStartLineB2 = 15;
     var targetStartColumnB2 = 16;
     var targetKindB2 = ElementKind.METHOD;
     var targetLocationB2 = Location(fileB, targetOffsetB2, targetLengthB2,
-        targetStartLineB2, targetStartColumnB2, 0, 0);
+        targetStartLineB2, targetStartColumnB2,
+        endLine: 0, endColumn: 0);
 
     // Six regions targeting a1, b1, a2, b1, a1, b2
     var regionOffsets = <int>[17, 18, 19, 20, 21, 22];
@@ -112,7 +116,8 @@
     var targetStartLine = 5;
     var targetStartColumn = 1;
     var targetLocation = Location(targetFile, targetOffset, targetLength,
-        targetStartLine, targetStartColumn, 0, 0);
+        targetStartLine, targetStartColumn,
+        endLine: 0, endColumn: 0);
     collector.addRegion(regionOffset, regionLength, targetKind, targetLocation);
     collector.createRegions();
     expect(collector.files, [targetFile]);
diff --git a/pkg/analyzer_plugin/tool/spec/common_types_spec.html b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
index 1ec7a90..412fcb7 100644
--- a/pkg/analyzer_plugin/tool/spec/common_types_spec.html
+++ b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
@@ -1057,14 +1057,14 @@
           the range.
         </p>
       </field>
-      <field name="endLine">
+      <field name="endLine" optional="true">
         <ref>int</ref>
         <p>
           The one-based index of the line containing the character immediately
           following the range.
         </p>
       </field>
-      <field name="endColumn">
+      <field name="endColumn" optional="true">
         <ref>int</ref>
         <p>
           The one-based index of the column containing the character immediately
diff --git a/pkg/compiler/lib/src/elements/types.dart b/pkg/compiler/lib/src/elements/types.dart
index 41e58eb..7b34a8a 100644
--- a/pkg/compiler/lib/src/elements/types.dart
+++ b/pkg/compiler/lib/src/elements/types.dart
@@ -911,86 +911,29 @@
 
   R visit(covariant DartType type, A argument) => type.accept(this, argument);
 
-  R visitLegacyType(covariant LegacyType type, A argument) => null;
+  R visitLegacyType(covariant LegacyType type, A argument);
 
-  R visitNullableType(covariant NullableType type, A argument) => null;
+  R visitNullableType(covariant NullableType type, A argument);
 
-  R visitNeverType(covariant NeverType type, A argument) => null;
+  R visitNeverType(covariant NeverType type, A argument);
 
-  R visitVoidType(covariant VoidType type, A argument) => null;
+  R visitVoidType(covariant VoidType type, A argument);
 
-  R visitTypeVariableType(covariant TypeVariableType type, A argument) => null;
+  R visitTypeVariableType(covariant TypeVariableType type, A argument);
 
-  R visitFunctionTypeVariable(
-          covariant FunctionTypeVariable type, A argument) =>
-      null;
+  R visitFunctionTypeVariable(covariant FunctionTypeVariable type, A argument);
 
-  R visitFunctionType(covariant FunctionType type, A argument) => null;
+  R visitFunctionType(covariant FunctionType type, A argument);
 
-  R visitInterfaceType(covariant InterfaceType type, A argument) => null;
+  R visitInterfaceType(covariant InterfaceType type, A argument);
 
-  R visitDynamicType(covariant DynamicType type, A argument) => null;
+  R visitDynamicType(covariant DynamicType type, A argument);
 
-  R visitErasedType(covariant ErasedType type, A argument) => null;
+  R visitErasedType(covariant ErasedType type, A argument);
 
-  R visitAnyType(covariant AnyType type, A argument) => null;
+  R visitAnyType(covariant AnyType type, A argument);
 
-  R visitFutureOrType(covariant FutureOrType type, A argument) => null;
-}
-
-abstract class BaseDartTypeVisitor<R, A> extends DartTypeVisitor<R, A> {
-  const BaseDartTypeVisitor();
-
-  R visitType(covariant DartType type, A argument);
-
-  @override
-  R visitLegacyType(covariant LegacyType type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitNullableType(covariant NullableType type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitNeverType(covariant NeverType type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitVoidType(covariant VoidType type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitTypeVariableType(covariant TypeVariableType type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitFunctionTypeVariable(
-          covariant FunctionTypeVariable type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitFunctionType(covariant FunctionType type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitInterfaceType(covariant InterfaceType type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitDynamicType(covariant DynamicType type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitErasedType(covariant ErasedType type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitAnyType(covariant AnyType type, A argument) =>
-      visitType(type, argument);
-
-  @override
-  R visitFutureOrType(covariant FutureOrType type, A argument) =>
-      visitType(type, argument);
+  R visitFutureOrType(covariant FutureOrType type, A argument);
 }
 
 class _LegacyErasureVisitor extends DartTypeVisitor<DartType, Null> {
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index e8b00b7..3544fa3 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -557,9 +557,12 @@
   /// key into maps.
   final Map<LibraryEntity, String> _libraryKeys = {};
 
+  _TypeConstantRepresentationVisitor _typeConstantRepresenter;
+
   Namer(this._closedWorld, this.fixedNames) {
     _literalGetterPrefix = new StringBackedName(fixedNames.getterPrefix);
     _literalSetterPrefix = new StringBackedName(fixedNames.setterPrefix);
+    _typeConstantRepresenter = _TypeConstantRepresentationVisitor(this);
   }
 
   JElementEnvironment get _elementEnvironment =>
@@ -1460,32 +1463,79 @@
     }
   }
 
-  String getTypeRepresentationForTypeConstant(DartType type) {
-    type = type.withoutNullability;
-    if (type is DynamicType) return "dynamic";
-    if (type is NeverType) return "Never";
-    if (type is FutureOrType) {
-      return "FutureOr<dynamic>";
-    }
-    if (type is FunctionType) {
-      // TODO(johnniwinther): Add naming scheme for function type literals.
-      // These currently only occur from kernel.
-      return '()->';
-    }
-    InterfaceType interface = type;
-    String name = uniqueNameForTypeConstantElement(
-        interface.element.library, interface.element);
+  String getTypeRepresentationForTypeConstant(DartType type) =>
+      _typeConstantRepresenter.visit(type, null);
+}
+
+class _TypeConstantRepresentationVisitor extends DartTypeVisitor<String, Null> {
+  final Namer _namer;
+
+  _TypeConstantRepresentationVisitor(this._namer);
+
+  String _represent(DartType type) => visit(type, null);
+
+  @override
+  String visitLegacyType(LegacyType type, _) => '${_represent(type.baseType)}*';
+
+  @override
+  String visitNullableType(NullableType type, _) =>
+      '${_represent(type.baseType)}?';
+
+  @override
+  String visitNeverType(NeverType type, _) => 'Never';
+
+  @override
+  String visitVoidType(VoidType type, _) => 'void';
+
+  @override
+  String visitTypeVariableType(TypeVariableType type, _) {
+    throw StateError('Unexpected TypeVariableType $type');
+  }
+
+  @override
+  String visitFunctionTypeVariable(FunctionTypeVariable type, _) {
+    throw StateError('Unexpected FunctionTypeVariable $type');
+  }
+
+  @override
+  String visitFunctionType(FunctionType type, _) {
+    // TODO(johnniwinther): Add naming scheme for function type literals.
+    // These currently only occur from kernel.
+    return '()->';
+  }
+
+  @override
+  String visitInterfaceType(InterfaceType type, _) {
+    String name = _namer.uniqueNameForTypeConstantElement(
+        type.element.library, type.element);
 
     // Type constants can currently only be raw types, so there is no point
     // adding ground-term type parameters, as they would just be 'dynamic'.
     // TODO(sra): Since the result string is used only in constructing constant
     // names, it would result in more readable names if the final string was a
     // legal JavaScript identifier.
-    if (interface.typeArguments.isEmpty) return name;
+    if (type.typeArguments.isEmpty) return name;
     String arguments =
-        new List.filled(interface.typeArguments.length, 'dynamic').join(', ');
+        new List.filled(type.typeArguments.length, 'dynamic').join(', ');
     return '$name<$arguments>';
   }
+
+  @override
+  String visitDynamicType(DynamicType type, _) => 'dynamic';
+
+  @override
+  String visitErasedType(ErasedType type, _) {
+    throw StateError('Unexpected ErasedType $type');
+  }
+
+  @override
+  String visitAnyType(AnyType type, _) {
+    throw StateError('Unexpected AnyType $type');
+  }
+
+  @override
+  String visitFutureOrType(FutureOrType type, _) =>
+      'FutureOr<${_represent(type.typeArgument)}>';
 }
 
 /// Returns a unique suffix for an intercepted accesses to [classes]. This is
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart
index e7113df..90b7b7c 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart
@@ -829,8 +829,23 @@
   }
 
   @override
-  void visitFutureOrType(FutureOrType type, _) {
-    collect(type.typeArgument);
+  void visitNeverType(NeverType type, _) {}
+
+  @override
+  void visitVoidType(VoidType type, _) {}
+
+  @override
+  void visitTypeVariableType(TypeVariableType type, _) {}
+
+  @override
+  void visitFunctionTypeVariable(FunctionTypeVariable type, _) {}
+
+  @override
+  void visitFunctionType(FunctionType type, _) {
+    collect(type.returnType);
+    collectAll(type.parameterTypes);
+    collectAll(type.optionalParameterTypes);
+    collectAll(type.namedParameterTypes);
   }
 
   @override
@@ -840,11 +855,17 @@
   }
 
   @override
-  void visitFunctionType(FunctionType type, _) {
-    collect(type.returnType);
-    collectAll(type.parameterTypes);
-    collectAll(type.optionalParameterTypes);
-    collectAll(type.namedParameterTypes);
+  void visitDynamicType(DynamicType type, _) {}
+
+  @override
+  void visitErasedType(ErasedType type, _) {}
+
+  @override
+  void visitAnyType(AnyType type, _) {}
+
+  @override
+  void visitFutureOrType(FutureOrType type, _) {
+    collect(type.typeArgument);
   }
 }
 
@@ -913,8 +934,10 @@
       visitType(type.baseType, state);
 
   @override
-  void visitFutureOrType(FutureOrType type, TypeVisitorState state) =>
-      visitType(type.typeArgument, state);
+  void visitNeverType(NeverType type, TypeVisitorState state) {}
+
+  @override
+  void visitVoidType(VoidType type, TypeVisitorState state) {}
 
   @override
   void visitTypeVariableType(TypeVariableType type, TypeVisitorState state) {
@@ -924,15 +947,14 @@
   }
 
   @override
-  visitInterfaceType(InterfaceType type, TypeVisitorState state) {
-    if (onClass != null) {
-      onClass(type.element, state: state);
+  visitFunctionTypeVariable(FunctionTypeVariable type, TypeVisitorState state) {
+    if (_visitedFunctionTypeVariables.add(type)) {
+      visitType(type.bound, state);
     }
-    visitTypes(type.typeArguments, covariantArgument(state));
   }
 
   @override
-  visitFunctionType(FunctionType type, TypeVisitorState state) {
+  void visitFunctionType(FunctionType type, TypeVisitorState state) {
     if (onFunctionType != null) {
       onFunctionType(type, state: state);
     }
@@ -946,11 +968,25 @@
   }
 
   @override
-  visitFunctionTypeVariable(FunctionTypeVariable type, TypeVisitorState state) {
-    if (_visitedFunctionTypeVariables.add(type)) {
-      visitType(type.bound, state);
+  void visitInterfaceType(InterfaceType type, TypeVisitorState state) {
+    if (onClass != null) {
+      onClass(type.element, state: state);
     }
+    visitTypes(type.typeArguments, covariantArgument(state));
   }
+
+  @override
+  void visitDynamicType(DynamicType type, TypeVisitorState state) {}
+
+  @override
+  void visitErasedType(ErasedType type, TypeVisitorState state) {}
+
+  @override
+  void visitAnyType(AnyType type, TypeVisitorState state) {}
+
+  @override
+  void visitFutureOrType(FutureOrType type, TypeVisitorState state) =>
+      visitType(type.typeArgument, state);
 }
 
 /// Runtime type usage for a class.
diff --git a/pkg/compiler/test/rti/rti_need_test_helper.dart b/pkg/compiler/test/rti/rti_need_test_helper.dart
index 4206006..5b15bb6 100644
--- a/pkg/compiler/test/rti/rti_need_test_helper.dart
+++ b/pkg/compiler/test/rti/rti_need_test_helper.dart
@@ -187,14 +187,16 @@
 }
 
 /// Visitor that determines whether a type refers to [entity].
-class FindTypeVisitor extends BaseDartTypeVisitor<bool, Null> {
+class FindTypeVisitor extends DartTypeVisitor<bool, Null> {
   final Entity entity;
 
   FindTypeVisitor(this.entity);
 
-  bool visitTypes(List<DartType> types) {
+  bool check(DartType type) => visit(type, null);
+
+  bool checkList(List<DartType> types) {
     for (DartType type in types) {
-      if (type.accept(this, null)) {
+      if (check(type)) {
         return true;
       }
     }
@@ -202,39 +204,47 @@
   }
 
   @override
-  bool visitType(DartType type, _) => false;
-
-  @override
   bool visitLegacyType(LegacyType type, _) => visit(type.baseType, _);
 
   @override
   bool visitNullableType(NullableType type, _) => visit(type.baseType, _);
 
   @override
-  bool visitInterfaceType(InterfaceType type, _) {
-    if (type.element == entity) return true;
-    return visitTypes(type.typeArguments);
-  }
+  bool visitNeverType(NeverType type, _) => false;
 
   @override
-  bool visitFunctionType(FunctionType type, _) {
-    if (type.returnType.accept(this, null)) return true;
-    if (visitTypes(type.typeVariables)) return true;
-    if (visitTypes(type.parameterTypes)) return true;
-    if (visitTypes(type.optionalParameterTypes)) return true;
-    if (visitTypes(type.namedParameterTypes)) return true;
-    return false;
-  }
+  bool visitVoidType(VoidType type, _) => false;
 
   @override
-  bool visitTypeVariableType(TypeVariableType type, _) {
-    return type.element.typeDeclaration == entity;
-  }
+  bool visitTypeVariableType(TypeVariableType type, _) =>
+      type.element.typeDeclaration == entity;
 
   @override
-  bool visitFutureOrType(FutureOrType type, _) {
-    return type.typeArgument.accept(this, null);
-  }
+  bool visitFunctionTypeVariable(FunctionTypeVariable type, _) => false;
+
+  @override
+  bool visitFunctionType(FunctionType type, _) =>
+      type.returnType.accept(this, null) ||
+      checkList(type.typeVariables) ||
+      checkList(type.parameterTypes) ||
+      checkList(type.optionalParameterTypes) ||
+      checkList(type.namedParameterTypes);
+
+  @override
+  bool visitInterfaceType(InterfaceType type, _) =>
+      type.element == entity || checkList(type.typeArguments);
+
+  @override
+  bool visitDynamicType(DynamicType type, _) => false;
+
+  @override
+  bool visitErasedType(ErasedType type, _) => false;
+
+  @override
+  bool visitAnyType(AnyType type, _) => false;
+
+  @override
+  bool visitFutureOrType(FutureOrType type, _) => check(type.typeArgument);
 }
 
 class RtiNeedDataComputer extends DataComputer<String> {
diff --git a/pkg/dev_compiler/test/shared_test_options.dart b/pkg/dev_compiler/test/shared_test_options.dart
index 53d8879..88013a3 100644
--- a/pkg/dev_compiler/test/shared_test_options.dart
+++ b/pkg/dev_compiler/test/shared_test_options.dart
@@ -6,7 +6,6 @@
 
 import 'package:cli_util/cli_util.dart';
 import 'package:dev_compiler/dev_compiler.dart';
-import 'package:dev_compiler/src/compiler/module_builder.dart';
 import 'package:front_end/src/api_unstable/ddc.dart';
 import 'package:front_end/src/compute_platform_binaries_location.dart';
 import 'package:front_end/src/fasta/incremental_serializer.dart';
diff --git a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.strong.transformed.expect b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.strong.transformed.expect
index f92bdce..a821c4b 100644
--- a/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/aot/enum_from_lib_used_as_type/main.dart.strong.transformed.expect
@@ -9,7 +9,10 @@
 static method main() → dynamic {
   core::List<dynamic> list = core::_GrowableList::•<dynamic>(0);
   if(list.{core::Iterable::isNotEmpty}{core::bool}) {
-    let dynamic #t1 = new mai::Class::•() in let dynamic #t2 = _in::unsafeCast<dynamic>(null) as{TypeError,ForDynamic,ForNonNullableByDefault} mai::Enum in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
+    block {
+      new mai::Class::•();
+      _in::unsafeCast<dynamic>(null) as{TypeError,ForDynamic,ForNonNullableByDefault} mai::Enum;
+    } =>throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
   }
 }
 
diff --git a/pkg/front_end/testcases/aot/tree_shake/main.dart.strong.transformed.expect b/pkg/front_end/testcases/aot/tree_shake/main.dart.strong.transformed.expect
index 077bd72..0aaa3a0 100644
--- a/pkg/front_end/testcases/aot/tree_shake/main.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/aot/tree_shake/main.dart.strong.transformed.expect
@@ -35,7 +35,10 @@
   #C3;
   core::List<self::UnusedEnum> list = core::_GrowableList::•<self::UnusedEnum>(0);
   if(list.{core::Iterable::isNotEmpty}{core::bool}) {
-    let dynamic #t2 = new mai::ConstClass::•() in let dynamic #t3 = _in::unsafeCast<dynamic>(null) as{TypeError,ForDynamic,ForNonNullableByDefault} mai::ConstEnum in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
+    block {
+      new mai::ConstClass::•();
+      _in::unsafeCast<dynamic>(null) as{TypeError,ForDynamic,ForNonNullableByDefault} mai::ConstEnum;
+    } =>throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
   }
 }
 
diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart
index e79df1f..aaa17c7 100644
--- a/pkg/nnbd_migration/lib/nnbd_migration.dart
+++ b/pkg/nnbd_migration/lib/nnbd_migration.dart
@@ -175,6 +175,12 @@
           'Removed a null-aware assignment, because the target cannot be null',
       kind: NullabilityFixKind.removeDeadCode);
 
+  /// A built_value `@nullable` annotation has been discarded.
+  static const removeNullableAnnotation = NullabilityFixDescription._(
+    appliedMessage: 'Discarded a use of the built_value annotation @nullable',
+    kind: NullabilityFixKind.removeNullableAnnotation,
+  );
+
   /// A message used to indicate a fix has been applied.
   final String appliedMessage;
 
@@ -293,6 +299,7 @@
   removeAs,
   removeDeadCode,
   removeLanguageVersionComment,
+  removeNullableAnnotation,
   replaceVar,
   typeNotMadeNullable,
   typeNotMadeNullableDueToHint,
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 381a783..d155843 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -895,18 +895,20 @@
     _dispatch(node.parameters);
     var parameterElement = node.declaredElement as FieldFormalParameterElement;
     var parameterType = _variables!.decoratedElementType(parameterElement);
-    var field = parameterElement.field!;
-    _fieldsNotInitializedByConstructor!.remove(field);
-    var fieldType = _variables!.decoratedElementType(field);
-    var origin = FieldFormalParameterOrigin(source, node);
-    if (node.type == null) {
-      _linkDecoratedTypes(parameterType, fieldType, origin, isUnion: false);
-      _checkAssignment(origin, FixReasonTarget.root,
-          source: fieldType, destination: parameterType, hard: false);
-    } else {
-      _dispatch(node.type);
-      _checkAssignment(origin, FixReasonTarget.root,
-          source: parameterType, destination: fieldType, hard: true);
+    var field = parameterElement.field;
+    if (field != null) {
+      _fieldsNotInitializedByConstructor!.remove(field);
+      var fieldType = _variables!.decoratedElementType(field);
+      var origin = FieldFormalParameterOrigin(source, node);
+      if (node.type == null) {
+        _linkDecoratedTypes(parameterType, fieldType, origin, isUnion: false);
+        _checkAssignment(origin, FixReasonTarget.root,
+            source: fieldType, destination: parameterType, hard: false);
+      } else {
+        _dispatch(node.type);
+        _checkAssignment(origin, FixReasonTarget.root,
+            source: parameterType, destination: fieldType, hard: true);
+      }
     }
 
     return null;
@@ -2083,7 +2085,10 @@
     var result = <PropertyAccessorElement, FieldFormalParameterElement>{};
     for (var parameter in constructorElement.parameters) {
       if (parameter is FieldFormalParameterElement) {
-        result[parameter.field!.getter!] = parameter;
+        var getter = parameter.field?.getter;
+        if (getter != null) {
+          result[getter] = parameter;
+        }
       }
     }
     return result;
diff --git a/pkg/nnbd_migration/lib/src/edit_plan.dart b/pkg/nnbd_migration/lib/src/edit_plan.dart
index 1487963..b0e55fc 100644
--- a/pkg/nnbd_migration/lib/src/edit_plan.dart
+++ b/pkg/nnbd_migration/lib/src/edit_plan.dart
@@ -838,6 +838,8 @@
       return node.members;
     } else if (node is CompilationUnit) {
       return [...node.directives, ...node.declarations];
+    } else if (node is MethodDeclaration) {
+      return node.metadata;
     } else {
       return null;
     }
@@ -1542,7 +1544,8 @@
     if (parent is Block ||
         parent is ClassDeclaration ||
         parent is CompilationUnit ||
-        parent is FormalParameter) {
+        parent is FormalParameter ||
+        parent is MethodDeclaration) {
       // These parent types don't use separators.
       return null;
     } else {
diff --git a/pkg/nnbd_migration/lib/src/fix_aggregator.dart b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
index 7e74f7e..ddf1bb4 100644
--- a/pkg/nnbd_migration/lib/src/fix_aggregator.dart
+++ b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
@@ -911,6 +911,34 @@
 }
 
 /// Implementation of [NodeChange] specialized for operating on
+/// [MethodDeclaration] nodes.
+class NodeChangeForMethodDeclaration extends NodeChange<MethodDeclaration> {
+  /// If non-null, indicates a `@nullable` annotation which should be removed
+  /// from this node.
+  Annotation? annotationToRemove;
+
+  /// If [annotationToRemove] is non-null, the information that should be
+  /// contained in the edit.
+  AtomicEditInfo? removeAnnotationInfo;
+
+  NodeChangeForMethodDeclaration() : super._();
+
+  @override
+  Iterable<String> get _toStringParts =>
+      [if (annotationToRemove != null) 'annotationToRemove'];
+
+  @override
+  EditPlan _apply(MethodDeclaration node, FixAggregator aggregator) {
+    return aggregator.planner.passThrough(node, innerPlans: [
+      if (annotationToRemove != null)
+        aggregator.planner
+            .removeNode(annotationToRemove!, info: removeAnnotationInfo),
+      ...aggregator.innerPlansForNode(node),
+    ]);
+  }
+}
+
+/// Implementation of [NodeChange] specialized for operating on
 /// [MethodInvocation] nodes.
 class NodeChangeForMethodInvocation
     extends NodeChangeForExpression<MethodInvocation>
@@ -1423,6 +1451,10 @@
   NodeChange visitIfStatement(IfStatement node) => NodeChangeForIfStatement();
 
   @override
+  NodeChange visitMethodDeclaration(MethodDeclaration node) =>
+      NodeChangeForMethodDeclaration();
+
+  @override
   NodeChange visitMethodInvocation(MethodInvocation node) =>
       NodeChangeForMethodInvocation();
 
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index 77fc0d3..3e08d2f 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -1147,15 +1147,18 @@
       // Potentially add an explicit type to a field formal parameter.
       var decl = node.declaredElement as FieldFormalParameterElement;
       var decoratedType = _fixBuilder._variables!.decoratedElementType(decl);
-      var decoratedFieldType =
-          _fixBuilder._variables!.decoratedElementType(decl.field!);
-      var typeToAdd = _fixBuilder._variables!.toFinalType(decoratedType);
-      var fieldFinalType =
-          _fixBuilder._variables!.toFinalType(decoratedFieldType);
-      if (typeToAdd is InterfaceType &&
-          !_fixBuilder._typeSystem.isSubtypeOf(fieldFinalType, typeToAdd)) {
-        (_fixBuilder._getChange(node) as NodeChangeForFieldFormalParameter)
-            .addExplicitType = typeToAdd;
+      var field = decl.field;
+      if (field != null) {
+        var decoratedFieldType =
+            _fixBuilder._variables!.decoratedElementType(field);
+        var typeToAdd = _fixBuilder._variables!.toFinalType(decoratedType);
+        var fieldFinalType =
+            _fixBuilder._variables!.toFinalType(decoratedFieldType);
+        if (typeToAdd is InterfaceType &&
+            !_fixBuilder._typeSystem.isSubtypeOf(fieldFinalType, typeToAdd)) {
+          (_fixBuilder._getChange(node) as NodeChangeForFieldFormalParameter)
+              .addExplicitType = typeToAdd;
+        }
       }
     } else if (node.parameters != null) {
       // Handle function-typed field formal parameters.
@@ -1198,6 +1201,31 @@
   }
 
   @override
+  void visitMethodDeclaration(MethodDeclaration node) {
+    if (node.isGetter && node.isAbstract) {
+      for (var annotation in node.metadata) {
+        if (annotation.arguments == null) {
+          var element = annotation.element;
+          if (element is PropertyAccessorElement &&
+              element.name == 'nullable') {
+            if (element.enclosingElement is CompilationUnitElement) {
+              if (element.library.source.uri.toString() ==
+                  'package:built_value/built_value.dart') {
+                var info = AtomicEditInfo(
+                    NullabilityFixDescription.removeNullableAnnotation, {});
+                (_fixBuilder._getChange(node) as NodeChangeForMethodDeclaration)
+                  ..annotationToRemove = annotation
+                  ..removeAnnotationInfo = info;
+              }
+            }
+          }
+        }
+      }
+    }
+    super.visitMethodDeclaration(node);
+  }
+
+  @override
   void visitTypeName(TypeName node) {
     var decoratedType = _fixBuilder._variables!
         .decoratedTypeAnnotation(_fixBuilder.source, node);
diff --git a/pkg/nnbd_migration/lib/src/front_end/info_builder.dart b/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
index b471651..c6f4bb7 100644
--- a/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
@@ -265,6 +265,10 @@
       case NullabilityFixKind.addThen:
         // We don't offer any edits around addition of `.then` to a future.
         break;
+      case NullabilityFixKind.removeNullableAnnotation:
+        // We don't offer any edits around removal of built_value `@nullable`
+        // annotations.
+        break;
     }
     return edits;
   }
diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart b/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart
index 7a72ee6..90adae8 100644
--- a/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart
@@ -113,6 +113,8 @@
         return 'typeNotMadeNullable';
       case NullabilityFixKind.typeNotMadeNullableDueToHint:
         return 'typeNotMadeNullableDueToHint';
+      case NullabilityFixKind.removeNullableAnnotation:
+        return 'removeNullableAnnotation';
     }
   }
 }
diff --git a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
index bed1a35..fea31af 100644
--- a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
@@ -277,8 +277,8 @@
         var edit = SourceEdit(offset, configText.length, newText);
         listener.addSourceFileEdit(
             'enable Null Safety language feature',
-            Location(
-                packageConfigFile.path, offset, newText.length, line, 0, 0, 0),
+            Location(packageConfigFile.path, offset, newText.length, line, 0,
+                endLine: 0, endColumn: 0),
             SourceFileEdit(packageConfigFile.path, 0, edits: [edit]));
       }
     } on FormatException catch (e) {
@@ -599,7 +599,8 @@
     var edit = SourceEdit(offset, 0, content);
     listener.addSourceFileEdit(
         'enable Null Safety language feature',
-        Location(path, offset, content.length, line, 0, 0, 0),
+        Location(path, offset, content.length, line, 0,
+            endLine: 0, endColumn: 0),
         SourceFileEdit(path, 0, edits: [edit]));
   }
 
@@ -609,7 +610,8 @@
     var edit = SourceEdit(offset, span.length, content);
     listener.addSourceFileEdit(
         'enable Null Safety language feature',
-        Location(path, offset, content.length, line, 0, 0, 0),
+        Location(path, offset, content.length, line, 0,
+            endLine: 0, endColumn: 0),
         SourceFileEdit(path, 0, edits: [edit]));
   }
 
diff --git a/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart
index c5f8ef3..6b6257cc 100644
--- a/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart
@@ -43,6 +43,7 @@
     NullabilityFixKind.removeAs,
     NullabilityFixKind.addLate,
     NullabilityFixKind.addLateDueToTestSetup,
+    NullabilityFixKind.removeNullableAnnotation,
     NullabilityFixKind.addLateDueToHint,
     NullabilityFixKind.addLateFinalDueToHint,
     NullabilityFixKind.checkExpressionDueToHint,
@@ -337,6 +338,8 @@
         return '$count type$s not made nullable';
       case NullabilityFixKind.typeNotMadeNullableDueToHint:
         return '$count type$s not made nullable due to hint$s';
+      case NullabilityFixKind.removeNullableAnnotation:
+        return '$count @nullable annotation$s removed';
     }
   }
 
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart
index 14fd871..b50bed1 100644
--- a/pkg/nnbd_migration/lib/src/node_builder.dart
+++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -446,9 +446,10 @@
 
   @override
   DecoratedType? visitMethodDeclaration(MethodDeclaration node) {
-    _handleExecutableDeclaration(
+    var declaredElement = node.declaredElement;
+    var decoratedType = _handleExecutableDeclaration(
         node,
-        node.declaredElement!,
+        declaredElement!,
         node.metadata,
         node.returnType,
         node.typeParameters,
@@ -456,6 +457,19 @@
         null,
         node.body,
         null);
+    if (declaredElement is PropertyAccessorElement) {
+      // Store a decorated type for the synthetic field so that in case we try
+      // to access it later we won't crash (this could happen due to errors in
+      // the source code).
+      if (declaredElement.isGetter) {
+        _variables!.recordDecoratedElementType(
+            declaredElement.variable, decoratedType.returnType);
+      } else {
+        _variables!.recordDecoratedElementType(
+            declaredElement.variable, decoratedType.positionalParameters![0],
+            soft: true);
+      }
+    }
     return null;
   }
 
@@ -675,7 +689,7 @@
   }
 
   /// Common handling of function and method declarations.
-  void _handleExecutableDeclaration(
+  DecoratedType _handleExecutableDeclaration(
       AstNode node,
       ExecutableElement declaredElement,
       NodeList<Annotation>? metadata,
@@ -726,6 +740,7 @@
     }
     _variables!
         .recordDecoratedElementType(declaredElement, decoratedFunctionType);
+    return decoratedFunctionType;
   }
 
   DecoratedType? _handleFormalParameter(
diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
index be547dc..f22569b 100644
--- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
@@ -270,8 +270,8 @@
       edit.length,
       startLocation.lineNumber,
       startLocation.columnNumber,
-      endLocation.lineNumber,
-      endLocation.columnNumber,
+      endLine: endLocation.lineNumber,
+      endColumn: endLocation.columnNumber,
     );
     return location;
   }
diff --git a/pkg/nnbd_migration/lib/src/variables.dart b/pkg/nnbd_migration/lib/src/variables.dart
index 44d0475..7222753 100644
--- a/pkg/nnbd_migration/lib/src/variables.dart
+++ b/pkg/nnbd_migration/lib/src/variables.dart
@@ -214,7 +214,8 @@
   }
 
   /// Associates decorated type information with the given [element].
-  void recordDecoratedElementType(Element? element, DecoratedType? type) {
+  void recordDecoratedElementType(Element? element, DecoratedType? type,
+      {bool soft = false}) {
     assert(() {
       assert(element is! TypeParameterElement,
           'Use recordDecoratedTypeParameterBound instead');
@@ -227,6 +228,9 @@
       }
       return true;
     }());
+    if (soft && _decoratedElementTypes.containsKey(element)) {
+      return;
+    }
     _decoratedElementTypes[element] = type;
   }
 
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index df8afb3..cc1bc01 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -116,7 +116,7 @@
   ///
   /// Optional parameter [removeViaComments] indicates whether dead code should
   /// be removed in its entirety (the default) or removed by commenting it out.
-  Future<void> _checkSingleFileChanges(String content, String expected,
+  Future<void> _checkSingleFileChanges(String content, dynamic expected,
       {Map<String, String> migratedInput = const {},
       bool removeViaComments = false,
       bool warnOnWeakCode = false,
@@ -548,6 +548,58 @@
         {path1: file1, path2: file2}, {path1: expected1, path2: anything});
   }
 
+  Future<void> test_built_value_nullable_getter() async {
+    addBuiltValuePackage();
+    var root = '$projectPath/lib';
+    var path1 = convertPath('$root/lib.dart');
+    var file1 = r'''
+import 'package:built_value/built_value.dart';
+
+part 'lib.g.dart';
+
+abstract class Foo implements Built<Foo, FooBuilder> {
+  @nullable
+  int get value;
+  Foo._();
+  factory Foo([void Function(FooBuilder) updates]) = _$Foo;
+}
+''';
+    var expected1 = r'''
+import 'package:built_value/built_value.dart';
+
+part 'lib.g.dart';
+
+abstract class Foo implements Built<Foo, FooBuilder> {
+  int? get value;
+  Foo._();
+  factory Foo([void Function(FooBuilder) updates]) = _$Foo;
+}
+''';
+    // Note: in a real-world scenario the generated file would be in a different
+    // directory but we don't need to simulate that detail for this test.  Also,
+    // the generated file would have a lot more code in it, but we don't need to
+    // simulate all the details of what is generated.
+    var path2 = convertPath('$root/lib.g.dart');
+    var file2 = r'''
+part of 'lib.dart';
+
+class _$Foo extends Foo {
+  @override
+  final int value;
+
+  factory _$Foo([void Function(FooBuilder) updates]) => throw '';
+
+  _$Foo._({this.value}) : super._();
+}
+
+class FooBuilder implements Builder<Foo, FooBuilder> {
+  int get value => throw '';
+}
+''';
+    await _checkMultipleFileChanges(
+        {path1: file1, path2: file2}, {path1: expected1, path2: anything});
+  }
+
   Future<void> test_call_already_migrated_extension() async {
     var content = '''
 import 'already_migrated.dart';
@@ -1164,6 +1216,41 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  Future<void> test_constructor_field_formal_resolves_to_getter() async {
+    var content = '''
+class C {
+  int get i => 0;
+  C(this.i);
+}
+''';
+    // It doesn't matter what the migration produces; we just want to make sure
+    // there isn't a crash.
+    await _checkSingleFileChanges(content, anything, allowErrors: true);
+  }
+
+  Future<void> test_constructor_field_formal_resolves_to_setter() async {
+    var content = '''
+class C {
+  set i(int value) {}
+  C(this.i);
+}
+''';
+    // It doesn't matter what the migration produces; we just want to make sure
+    // there isn't a crash.
+    await _checkSingleFileChanges(content, anything, allowErrors: true);
+  }
+
+  Future<void> test_constructor_field_formal_unresolved() async {
+    var content = '''
+class C {
+  C(this.i);
+}
+''';
+    // It doesn't matter what the migration produces; we just want to make sure
+    // there isn't a crash.
+    await _checkSingleFileChanges(content, anything, allowErrors: true);
+  }
+
   Future<void> test_constructor_optional_param_factory() async {
     var content = '''
 class C {
diff --git a/pkg/nnbd_migration/test/edit_plan_test.dart b/pkg/nnbd_migration/test/edit_plan_test.dart
index 3cf4039..135ec47 100644
--- a/pkg/nnbd_migration/test/edit_plan_test.dart
+++ b/pkg/nnbd_migration/test/edit_plan_test.dart
@@ -903,6 +903,24 @@
   int? x}) {}''');
   }
 
+  Future<void> test_remove_metadata_from_method_declaration() async {
+    await analyze('''
+class C {
+  @deprecated
+  f() {}
+}
+''');
+    var deprecated = findNode.annotation('@deprecated');
+    checkPlan(
+        planner!.passThrough(deprecated.parent,
+            innerPlans: [planner!.removeNode(deprecated)]),
+        '''
+class C {
+  f() {}
+}
+''');
+  }
+
   Future<void> test_remove_parameter() async {
     await analyze('f(int x, int y, int z) => null;');
     var parameter = findNode.simple('y').parent!;
diff --git a/pkg/nnbd_migration/test/preview/preview_site_test.dart b/pkg/nnbd_migration/test/preview/preview_site_test.dart
index aeee9da..872d4d0 100644
--- a/pkg/nnbd_migration/test/preview/preview_site_test.dart
+++ b/pkg/nnbd_migration/test/preview/preview_site_test.dart
@@ -65,7 +65,7 @@
     // Add a source change for analysis_options, which has no UnitInfo.
     dartfixListener!.addSourceFileEdit(
         'enable experiment',
-        Location(analysisOptionsPath, 9, 0, 1, 9, 1, 9),
+        Location(analysisOptionsPath, 9, 0, 1, 9, endLine: 1, endColumn: 9),
         SourceFileEdit(analysisOptionsPath, 0, edits: [
           SourceEdit(9, 0, '\n  enable-experiment:\n  - non-nullable')
         ]));
@@ -100,7 +100,7 @@
     file.writeAsStringSync(currentContent);
     dartfixListener!.addSourceFileEdit(
         'test change',
-        Location(path, 10, 0, 1, 10, 1, 10),
+        Location(path, 10, 0, 1, 10, endLine: 1, endColumn: 10),
         SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')]));
     expect(() => site.performApply([]), throwsA(isA<StateError>()));
     expect(file.readAsStringSync(), currentContent);
@@ -115,7 +115,7 @@
     site.unitInfoMap[path] = UnitInfo(path)..diskContent = content;
     dartfixListener!.addSourceFileEdit(
         'test change',
-        Location(path, 10, 0, 1, 10, 1, 10),
+        Location(path, 10, 0, 1, 10, endLine: 1, endColumn: 10),
         SourceFileEdit(path, 0, edits: [
           SourceEdit(10, 0, 'List args'),
           SourceEdit(13, 0, '\n  print(args);\n')
@@ -136,7 +136,7 @@
     site.unitInfoMap[path] = UnitInfo(path)..diskContent = content;
     dartfixListener!.addSourceFileEdit(
         'test change',
-        Location(path, 10, 0, 1, 10, 1, 10),
+        Location(path, 10, 0, 1, 10, endLine: 1, endColumn: 10),
         SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')]));
     site.performApply([]);
     expect(file.readAsStringSync(), 'void main(List args) {}');
@@ -430,7 +430,7 @@
       ..wasExplicitlyOptedOut = true;
     dartfixListener!.addSourceFileEdit(
         'remove DLV comment',
-        Location(path, 0, 14, 1, 1, 3, 1),
+        Location(path, 0, 14, 1, 1, endLine: 3, endColumn: 1),
         SourceFileEdit(path, 0, edits: [SourceEdit(0, 14, '')]));
     var navigationTree =
         NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
@@ -471,7 +471,7 @@
       ..wasExplicitlyOptedOut = false;
     dartfixListener!.addSourceFileEdit(
         'test change',
-        Location(path, 10, 0, 1, 10, 1, 10),
+        Location(path, 10, 0, 1, 10, endLine: 1, endColumn: 10),
         SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')]));
     var navigationTree =
         NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
@@ -525,11 +525,11 @@
       ..wasExplicitlyOptedOut = false;
     dartfixListener!.addSourceFileEdit(
         'test change',
-        Location(pathA, 10, 0, 1, 10, 1, 10),
+        Location(pathA, 10, 0, 1, 10, endLine: 1, endColumn: 10),
         SourceFileEdit(pathA, 0, edits: [SourceEdit(10, 0, 'List args')]));
     dartfixListener!.addSourceFileEdit(
         'test change',
-        Location(pathB, 10, 0, 1, 10, 1, 10),
+        Location(pathB, 10, 0, 1, 10, endLine: 1, endColumn: 10),
         SourceFileEdit(pathB, 0, edits: [SourceEdit(10, 0, 'List args')]));
     var navigationTree =
         NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
@@ -563,7 +563,7 @@
       ..wasExplicitlyOptedOut = true;
     dartfixListener!.addSourceFileEdit(
         'remove DLV comment',
-        Location(path, 0, 14, 1, 1, 3, 1),
+        Location(path, 0, 14, 1, 1, endLine: 3, endColumn: 1),
         SourceFileEdit(path, 0, edits: [SourceEdit(0, 14, '')]));
     var navigationTree =
         NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index e84e660..7271f50 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -846,8 +846,8 @@
         isStatic: true)
       ..fileOffset = compound.fileOffset
       ..isNonNullableByDefault = true
-      ..addAnnotation(ConstantExpression(InstanceConstant(pragmaClass.reference,
-          /*type_arguments=*/ [], {
+      ..addAnnotation(ConstantExpression(
+          InstanceConstant(pragmaClass.reference, /*type_arguments=*/ [], {
         pragmaName.getterReference: StringConstant("vm:prefer-inline"),
         pragmaOptions.getterReference: NullConstant(),
       })));
diff --git a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
index 2d16a7b..f24d9af 100644
--- a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
+++ b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
@@ -402,7 +402,7 @@
     //    as required positional parameters, alphabetically by name.
     final List<VariableDeclaration> sortedNamed = function.namedParameters
         .toList()
-          ..sort((var1, var2) => var1.name.compareTo(var2.name));
+      ..sort((var1, var2) => var1.name.compareTo(var2.name));
     for (VariableDeclaration variable in sortedNamed) {
       final _ParameterInfo param = info.named[variable.name];
       if (param.isAlwaysPassed) {
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index e9473dd..285990d 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -9,6 +9,7 @@
 
 import 'package:kernel/target/targets.dart';
 import 'package:kernel/ast.dart' hide Statement, StatementVisitor;
+import 'package:kernel/ast.dart' as ast show Statement;
 import 'package:kernel/clone.dart' show CloneVisitorNotMembers;
 import 'package:kernel/core_types.dart' show CoreTypes;
 import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
@@ -1030,20 +1031,35 @@
   }
 
   bool _isThrowExpression(Expression expr) {
-    while (expr is Let) {
-      expr = (expr as Let).body;
+    for (;;) {
+      if (expr is Let) {
+        expr = (expr as Let).body;
+      } else if (expr is BlockExpression) {
+        expr = (expr as BlockExpression).value;
+      } else {
+        break;
+      }
     }
     return expr is Throw;
   }
 
-  TreeNode _evaluateArguments(List<Expression> args, Expression result) {
-    Expression node = result;
-    for (var arg in args.reversed) {
+  Expression _evaluateArguments(List<Expression> args, Expression result) {
+    final List<ast.Statement> statements = <ast.Statement>[];
+    for (var arg in args) {
       if (mayHaveSideEffects(arg)) {
-        node = Let(VariableDeclaration(null, initializer: arg), node);
+        statements.add(ExpressionStatement(arg));
       }
     }
-    return node;
+    if (statements.isEmpty) {
+      return result;
+    }
+    // Merge nested BlockExpression nodes.
+    Expression value = result;
+    if (result is BlockExpression) {
+      statements.addAll(result.body.statements);
+      value = result.value;
+    }
+    return BlockExpression(Block(statements), value);
   }
 
   TreeNode _makeUnreachableCall(List<Expression> args) {
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
index 92746ce..599ca03 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
@@ -15,7 +15,10 @@
 [@vm.procedure-attributes.metadata=getterCalledDynamically:false,hasTearOffUses:false,methodOrSetterSelectorId:4] [@vm.unboxing-info.metadata=(b)->d]  operator []([@vm.inferred-type.metadata=!] core::int* i) → core::double*
     return [@vm.direct-call.metadata=dart.typed_data::_Float64List.[]] [@vm.inferred-type.metadata=dart.core::_Double (skip check)] [@vm.direct-call.metadata=#lib::_Vector._elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements}{core::List<core::double*>*}.{core::List::[]}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}([@vm.direct-call.metadata=#lib::_Vector._offset] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] this.{self::_Vector::_offset}{core::int*}){(core::num*) →* core::int*}){(core::int*) →* core::double*};
 [@vm.procedure-attributes.metadata=getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5]  operator []=([@vm.inferred-type.metadata=dart.core::_OneByteString] core::int* i, core::double* value) → void {
-    let dynamic #t1 = [@vm.direct-call.metadata=#lib::_Vector._elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements}{core::List<core::double*>*} in let dynamic #t2 = [@vm.direct-call.metadata=#lib::_Vector._offset] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] this.{self::_Vector::_offset}{core::int*} in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
+    block {
+      [@vm.direct-call.metadata=#lib::_Vector._elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements}{core::List<core::double*>*};
+      [@vm.direct-call.metadata=#lib::_Vector._offset] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] this.{self::_Vector::_offset}{core::int*};
+    } =>throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
   }
 [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:6] [@vm.unboxing-info.metadata=(b)->d]  operator *([@vm.inferred-type.metadata=#lib::_Vector?] self::_Vector* a) → core::double* {
     core::double* result = 0.0;
@@ -27,9 +30,9 @@
 [@vm.inferred-type.metadata=#lib::_Vector?]static field self::_Vector* v = new self::_Vector::•();
 [@vm.inferred-type.metadata=dart.core::_Double?]static field core::double* x = 0.0;
 static method main(core::List<core::String*>* args) → dynamic {
-  core::Stopwatch* timer = let final core::Stopwatch* #t3 = new core::Stopwatch::•() in block {
-    [@vm.direct-call.metadata=dart.core::Stopwatch.start] [@vm.inferred-type.metadata=!? (skip check)] #t3.{core::Stopwatch::start}(){() →* void};
-  } =>#t3;
+  core::Stopwatch* timer = let final core::Stopwatch* #t1 = new core::Stopwatch::•() in block {
+    [@vm.direct-call.metadata=dart.core::Stopwatch.start] [@vm.inferred-type.metadata=!? (skip check)] #t1.{core::Stopwatch::start}(){() →* void};
+  } =>#t1;
   for (core::int* i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(100000000){(core::num*) →* core::bool*}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num*) →* core::int*}) {
     self::x = [@vm.direct-call.metadata=dart.core::_Double.+??] [@vm.inferred-type.metadata=dart.core::_Double (skip check)] [@vm.inferred-type.metadata=dart.core::_Double?] self::x.{core::double::+}([@vm.direct-call.metadata=#lib::_Vector.*??] [@vm.inferred-type.metadata=dart.core::_Double (skip check)] [@vm.inferred-type.metadata=#lib::_Vector?] self::v.{self::_Vector::*}([@vm.inferred-type.metadata=#lib::_Vector?] self::v){(self::_Vector*) →* core::double*}){(core::num*) →* core::double*};
   }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/null_test_elimination.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/null_test_elimination.dart.expect
index 7e7de62..2fa9807 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/null_test_elimination.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/null_test_elimination.dart.expect
@@ -13,7 +13,9 @@
 }
 [@vm.inferred-type.metadata=#lib::A?]static field self::A* staticField = new self::A::•("hi", "bye");
 static method testNonNullableIf1([@vm.inferred-type.metadata=#lib::A?] self::A* a) → dynamic {
-  if(let dynamic #t1 = [@vm.direct-call.metadata=#lib::A.nonNullable??] [@vm.inferred-type.metadata=dart.core::_OneByteString] a.{self::A::nonNullable}{core::String*} in false) {
+  if( block {
+    [@vm.direct-call.metadata=#lib::A.nonNullable??] [@vm.inferred-type.metadata=dart.core::_OneByteString] a.{self::A::nonNullable}{core::String*};
+  } =>false) {
     core::print("null");
   }
 }
@@ -23,12 +25,16 @@
   }
 }
 static method testAlwaysNullIf1([@vm.inferred-type.metadata=#lib::A?] self::A* a) → dynamic {
-  if(let dynamic #t2 = [@vm.direct-call.metadata=#lib::A.alwaysNull??] [@vm.inferred-type.metadata=dart.core::Null? (value: null)] a.{self::A::alwaysNull}{core::String*} in true) {
+  if( block {
+    [@vm.direct-call.metadata=#lib::A.alwaysNull??] [@vm.inferred-type.metadata=dart.core::Null? (value: null)] a.{self::A::alwaysNull}{core::String*};
+  } =>true) {
     core::print("null");
   }
 }
 static method testNonNullableIf2([@vm.inferred-type.metadata=#lib::A?] self::A* a) → dynamic {
-  if(!(let dynamic #t3 = [@vm.direct-call.metadata=#lib::A.nonNullable??] [@vm.inferred-type.metadata=dart.core::_OneByteString] a.{self::A::nonNullable}{core::String*} in false) && _in::unsafeCast<core::bool*>([@vm.inferred-type.metadata=dart.core::bool] self::someCondition())) {
+  if(!( block {
+    [@vm.direct-call.metadata=#lib::A.nonNullable??] [@vm.inferred-type.metadata=dart.core::_OneByteString] a.{self::A::nonNullable}{core::String*};
+  } =>false) && _in::unsafeCast<core::bool*>([@vm.inferred-type.metadata=dart.core::bool] self::someCondition())) {
     core::print("not null");
   }
 }
@@ -38,16 +44,22 @@
   }
 }
 static method testAlwaysNullIf2([@vm.inferred-type.metadata=#lib::A?] self::A* a) → dynamic {
-  if(!(let dynamic #t4 = [@vm.direct-call.metadata=#lib::A.alwaysNull??] [@vm.inferred-type.metadata=dart.core::Null? (value: null)] a.{self::A::alwaysNull}{core::String*} in true) && _in::unsafeCast<core::bool*>([@vm.inferred-type.metadata=dart.core::bool] self::someCondition())) {
+  if(!( block {
+    [@vm.direct-call.metadata=#lib::A.alwaysNull??] [@vm.inferred-type.metadata=dart.core::Null? (value: null)] a.{self::A::alwaysNull}{core::String*};
+  } =>true) && _in::unsafeCast<core::bool*>([@vm.inferred-type.metadata=dart.core::bool] self::someCondition())) {
     core::print("not null");
   }
 }
 static method testNonNullableCondExpr([@vm.inferred-type.metadata=#lib::A?] self::A* a) → dynamic
-  return !(let dynamic #t5 = [@vm.direct-call.metadata=#lib::A.nonNullable??] [@vm.inferred-type.metadata=dart.core::_OneByteString] a.{self::A::nonNullable}{core::String*} in false) ?{core::String*} "not null" : "null";
+  return !( block {
+    [@vm.direct-call.metadata=#lib::A.nonNullable??] [@vm.inferred-type.metadata=dart.core::_OneByteString] a.{self::A::nonNullable}{core::String*};
+  } =>false) ?{core::String*} "not null" : "null";
 static method testNullableCondExpr([@vm.inferred-type.metadata=#lib::A?] self::A* a) → dynamic
   return !([@vm.direct-call.metadata=#lib::A.nullable??] [@vm.inferred-type.metadata=dart.core::_OneByteString?] a.{self::A::nullable}{core::String*} == null) ?{core::String*} "not null" : "null";
 static method testAlwaysNullCondExpr([@vm.inferred-type.metadata=#lib::A?] self::A* a) → dynamic
-  return !(let dynamic #t6 = [@vm.direct-call.metadata=#lib::A.alwaysNull??] [@vm.inferred-type.metadata=dart.core::Null? (value: null)] a.{self::A::alwaysNull}{core::String*} in true) ?{core::String*} "not null" : "null";
+  return !( block {
+    [@vm.direct-call.metadata=#lib::A.alwaysNull??] [@vm.inferred-type.metadata=dart.core::Null? (value: null)] a.{self::A::alwaysNull}{core::String*};
+  } =>true) ?{core::String*} "not null" : "null";
 static method someCondition() → dynamic
   return [@vm.inferred-type.metadata=dart.core::bool] [@vm.inferred-type.metadata=int?] core::int::parse("1") =={core::num::==}{(core::Object*) →* core::bool*} 1;
 static method main() → void {
diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md
index 4d071d5..3c1a964 100644
--- a/pkg/vm_service/CHANGELOG.md
+++ b/pkg/vm_service/CHANGELOG.md
@@ -1,8 +1,12 @@
 # Changelog
 
 ## 7.1.1
-- Update to version `3.47` of the spec.
+- Update to version `3.48` of the spec.
 - Added `shows` and `hides` properties to `LibraryDependency`.
+- Added `Profiler` stream, `UserTagChanged` event kind, and `updatedTag` and
+  `previousTag` properties to `Event`.
+- Fixed bug where a response without a type would cause a null type failure
+  (dart-lang/sdk#46559).
 
 ## 7.1.0
 - Update to version `3.46` of the spec.
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 903bb0c..70c6db8 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -44,7 +44,7 @@
 
   if (json is List) {
     return json.map((e) => createServiceObject(e, expectedTypes)).toList();
-  } else if (json is Map) {
+  } else if (json is Map<String, dynamic>) {
     String? type = json['type'];
 
     // Not a Response type.
@@ -53,7 +53,7 @@
       if (expectedTypes.length == 1) {
         type = expectedTypes.first;
       } else {
-        return null;
+        return Response.parse(json);
       }
     } else if (_isNullInstance(json) &&
         (!expectedTypes.contains('InstanceRef'))) {
@@ -824,8 +824,7 @@
   /// returned.
   Future<SourceReport> getSourceReport(
     String isolateId,
-    /*List<SourceReportKind>*/
-    List<String> reports, {
+    /*List<SourceReportKind>*/ List<String> reports, {
     String? scriptId,
     int? tokenPos,
     int? endTokenPos,
@@ -1923,8 +1922,7 @@
   @override
   Future<SourceReport> getSourceReport(
     String isolateId,
-    /*List<SourceReportKind>*/
-    List<String> reports, {
+    /*List<SourceReportKind>*/ List<String> reports, {
     String? scriptId,
     int? tokenPos,
     int? endTokenPos,
@@ -2196,7 +2194,7 @@
       request.completeError(RPCError.parse(request.method, json['error']));
     } else {
       Map<String, dynamic> result = json['result'] as Map<String, dynamic>;
-      String type = result['type'];
+      String? type = result['type'];
       if (type == 'Sentinel') {
         request.completeError(SentinelException.parse(request.method, result));
       } else if (_typeFactories[type] == null) {
@@ -4307,8 +4305,7 @@
   List<BoundVariable>? vars;
 
   @optional
-  /*FrameKind*/
-  String? kind;
+  /*FrameKind*/ String? kind;
 
   Frame({
     required this.index,
@@ -5447,18 +5444,19 @@
       'id: ${id}, number: ${number}, name: ${name}, isSystemIsolateGroup: ${isSystemIsolateGroup}]';
 }
 
-/// An `Isolate` object provides information about one isolate in the VM.
+/// An `IsolateGroup` object provides information about an isolate group in the
+/// VM.
 class IsolateGroup extends Response implements IsolateGroupRef {
   static IsolateGroup? parse(Map<String, dynamic>? json) =>
       json == null ? null : IsolateGroup._fromJson(json);
 
-  /// The id which is passed to the getIsolate RPC to reload this isolate.
+  /// The id which is passed to the getIsolateGroup RPC to reload this isolate.
   String? id;
 
   /// A numeric id for this isolate, represented as a string. Unique.
   String? number;
 
-  /// A name identifying this isolate. Not guaranteed to be unique.
+  /// A name identifying this isolate group. Not guaranteed to be unique.
   String? name;
 
   /// Specifies whether the isolate group was spawned by the VM or embedder for
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml
index 9ea0b77..5aad6b6 100644
--- a/pkg/vm_service/pubspec.yaml
+++ b/pkg/vm_service/pubspec.yaml
@@ -3,7 +3,7 @@
   A library to communicate with a service implementing the Dart VM
   service protocol.
 
-version: 7.1.0
+version: 7.1.1
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
 
diff --git a/pkg/vm_service/test/library_dependency_test.dart b/pkg/vm_service/test/library_dependency_test.dart
index 5f308ef..5745ca1 100644
--- a/pkg/vm_service/test/library_dependency_test.dart
+++ b/pkg/vm_service/test/library_dependency_test.dart
@@ -8,7 +8,6 @@
 import 'package:test/test.dart';
 import 'package:vm_service/vm_service.dart';
 
-import 'common/service_test_common.dart';
 import 'common/test_helper.dart';
 
 export 'dart:io' show Socket hide SecureSocket;
diff --git a/pkg/vm_service/test/regress_46559_test.dart b/pkg/vm_service/test/regress_46559_test.dart
new file mode 100644
index 0000000..84fceae
--- /dev/null
+++ b/pkg/vm_service/test/regress_46559_test.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+
+import 'package:vm_service/vm_service.dart';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+Future<ServiceExtensionResponse> echo(
+    String method, Map<String, String> args) async {
+  print('In service extension');
+  return ServiceExtensionResponse.result(json.encode(args));
+}
+
+testMain() {
+  registerExtension('ext.foo', echo);
+  debugger();
+}
+
+final tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  resumeIsolate,
+  (VmService vm, IsolateRef isolateRef) async {
+    print('waiting for response');
+    final response = await vm.callServiceExtension(
+      'ext.foo',
+      isolateId: isolateRef.id!,
+      args: {'foo': 'bar'},
+    );
+    print('got response');
+    print(response.json);
+  },
+];
+
+main([args = const <String>[]]) async => await runIsolateTests(
+      args,
+      tests,
+      'regress_46559_test.dart',
+      testeeConcurrent: testMain,
+    );
diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart
index 2cf8fd8..844b1ff 100644
--- a/pkg/vm_service/tool/dart/generate_dart.dart
+++ b/pkg/vm_service/tool/dart/generate_dart.dart
@@ -200,7 +200,7 @@
       request.completeError(RPCError.parse(request.method, json['error']));
     } else {
       Map<String, dynamic> result = json['result'] as Map<String, dynamic>;
-      String type = result['type'];
+      String? type = result['type'];
       if (type == 'Sentinel') {
         request.completeError(SentinelException.parse(request.method, result));
       } else if (_typeFactories[type] == null) {
@@ -517,7 +517,7 @@
 
   if (json is List) {
     return json.map((e) => createServiceObject(e, expectedTypes)).toList();
-  } else if (json is Map) {
+  } else if (json is Map<String, dynamic>) {
     String? type = json['type'];
 
     // Not a Response type.
@@ -526,7 +526,7 @@
       if (expectedTypes.length == 1) {
         type = expectedTypes.first;
       } else {
-        return null;
+        return Response.parse(json);
       }
     } else if (_isNullInstance(json) && (!expectedTypes.contains('InstanceRef'))) {
       // Replace null instances with null when we don't expect an instance to
diff --git a/runtime/bin/dart_io_api_impl.cc b/runtime/bin/dart_io_api_impl.cc
index a890132..8ec2c1d 100644
--- a/runtime/bin/dart_io_api_impl.cc
+++ b/runtime/bin/dart_io_api_impl.cc
@@ -31,6 +31,7 @@
 
 void CleanupDartIo() {
   EventHandler::Stop();
+  Process::TerminateExitCodeHandler();
 #if !defined(DART_IO_SECURE_SOCKET_DISABLED)
   SSLFilter::Cleanup();
 #endif
diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
index 885c48f..adcdc07 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
@@ -1098,6 +1098,9 @@
   if (strcmp(name, "ReturnIntPtr") == 0) {
     return reinterpret_cast<void*>(ReturnIntPtr);
   }
+  if (strcmp(name, "IsThreadInGenerated") == 0) {
+    return reinterpret_cast<void*>(IsThreadInGenerated);
+  }
   // This should be unreachable in tests.
   ENSURE(false);
 }
diff --git a/runtime/bin/platform.cc b/runtime/bin/platform.cc
index 1b04cfc..3f9ff26 100644
--- a/runtime/bin/platform.cc
+++ b/runtime/bin/platform.cc
@@ -12,6 +12,8 @@
 namespace dart {
 namespace bin {
 
+AcqRelAtomic<const char*> Platform::resolved_executable_name_ = nullptr;
+
 void FUNCTION_NAME(Platform_NumberOfProcessors)(Dart_NativeArguments args) {
   Dart_SetReturnValue(args, Dart_NewInteger(Platform::NumberOfProcessors()));
 }
diff --git a/runtime/bin/platform.h b/runtime/bin/platform.h
index bf2d6b1..a34db67 100644
--- a/runtime/bin/platform.h
+++ b/runtime/bin/platform.h
@@ -6,6 +6,8 @@
 #define RUNTIME_BIN_PLATFORM_H_
 
 #include "bin/builtin.h"
+
+#include "platform/atomic.h"
 #include "platform/globals.h"
 #include "platform/utils.h"
 
@@ -83,14 +85,19 @@
   }
   static const char* GetExecutableName();
   static const char* GetResolvedExecutableName() {
-    if (resolved_executable_name_ == NULL) {
+    if (resolved_executable_name_.load() == nullptr) {
       // Try to resolve the executable path using platform specific APIs.
       const char* resolved_name = Platform::ResolveExecutablePath();
-      if (resolved_name != NULL) {
-        resolved_executable_name_ = Utils::StrDup(resolved_name);
+      if (resolved_name != nullptr) {
+        char* resolved_name_copy = Utils::StrDup(resolved_name);
+        const char* expect_old_is_null = nullptr;
+        if (!resolved_executable_name_.compare_exchange_strong(
+                expect_old_is_null, resolved_name_copy)) {
+          free(resolved_name_copy);
+        }
       }
     }
-    return resolved_executable_name_;
+    return resolved_executable_name_.load();
   }
 
   // Stores and gets the flags passed to the executable.
@@ -108,8 +115,12 @@
  private:
   // The path to the executable.
   static const char* executable_name_;
+
   // The path to the resolved executable.
-  static char* resolved_executable_name_;
+  //
+  // We use require-release semantics to ensure initializing stores to the
+  // string are visible when the string becomes visible.
+  static AcqRelAtomic<const char*> resolved_executable_name_;
 
   static int script_index_;
   static char** argv_;  // VM flags are argv_[1 ... script_index_ - 1]
diff --git a/runtime/bin/platform_android.cc b/runtime/bin/platform_android.cc
index 6e5a34a..a9a5cda 100644
--- a/runtime/bin/platform_android.cc
+++ b/runtime/bin/platform_android.cc
@@ -22,7 +22,6 @@
 namespace bin {
 
 const char* Platform::executable_name_ = NULL;
-char* Platform::resolved_executable_name_ = NULL;
 int Platform::script_index_ = 1;
 char** Platform::argv_ = NULL;
 
diff --git a/runtime/bin/platform_fuchsia.cc b/runtime/bin/platform_fuchsia.cc
index 5c23480..47c1907 100644
--- a/runtime/bin/platform_fuchsia.cc
+++ b/runtime/bin/platform_fuchsia.cc
@@ -23,7 +23,6 @@
 namespace bin {
 
 const char* Platform::executable_name_ = NULL;
-char* Platform::resolved_executable_name_ = NULL;
 int Platform::script_index_ = 1;
 char** Platform::argv_ = NULL;
 
diff --git a/runtime/bin/platform_linux.cc b/runtime/bin/platform_linux.cc
index d9443bb..adbfac1 100644
--- a/runtime/bin/platform_linux.cc
+++ b/runtime/bin/platform_linux.cc
@@ -21,7 +21,6 @@
 namespace bin {
 
 const char* Platform::executable_name_ = NULL;
-char* Platform::resolved_executable_name_ = NULL;
 int Platform::script_index_ = 1;
 char** Platform::argv_ = NULL;
 
diff --git a/runtime/bin/platform_macos.cc b/runtime/bin/platform_macos.cc
index 0479776..8083949 100644
--- a/runtime/bin/platform_macos.cc
+++ b/runtime/bin/platform_macos.cc
@@ -29,7 +29,6 @@
 namespace bin {
 
 const char* Platform::executable_name_ = NULL;
-char* Platform::resolved_executable_name_ = NULL;
 int Platform::script_index_ = 1;
 char** Platform::argv_ = NULL;
 
diff --git a/runtime/bin/platform_win.cc b/runtime/bin/platform_win.cc
index 59e1b74..515a569 100644
--- a/runtime/bin/platform_win.cc
+++ b/runtime/bin/platform_win.cc
@@ -25,7 +25,6 @@
 namespace bin {
 
 const char* Platform::executable_name_ = NULL;
-char* Platform::resolved_executable_name_ = NULL;
 int Platform::script_index_ = 1;
 char** Platform::argv_ = NULL;
 
diff --git a/runtime/vm/elf.cc b/runtime/vm/elf.cc
index a31f66b..12210a4 100644
--- a/runtime/vm/elf.cc
+++ b/runtime/vm/elf.cc
@@ -893,9 +893,6 @@
 
 class NoteSection : public BitsContainer {
  public:
-  // While the build ID section does not need to be writable, the first load
-  // segment in our ELF files is writable (see Elf::WriteProgramTable). Thus,
-  // this ensures we can put it in that segment right after the program table.
   NoteSection()
       : BitsContainer(elf::SectionHeaderType::SHT_NOTE,
                       /*allocate=*/true,
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index b0b12d8..a663381 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -10768,7 +10768,9 @@
   }
   ASSERT(original.IsOriginal());
   Field& clone = Field::Handle();
-  clone ^= Object::Clone(*this, Heap::kOld);
+  // Using relaxed loading is fine because concurrent fields changes are all
+  // guarded, will be reconciled during optimized code installation.
+  clone ^= Object::Clone(*this, Heap::kOld, /*load_with_relaxed_atomics=*/true);
   clone.SetOriginal(original);
   clone.InheritKernelOffsetFrom(original);
   return clone.ptr();
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index 96e6771..e22b3e6 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -182,8 +182,10 @@
   if (cid == kExternalTwoByteStringCid) return false;
   if (cid == kMintCid) return false;
   if (cid == kDoubleCid) return false;
+  if (cid == kBoolCid) return false;
   if (cid == kSendPortCid) return false;
   if (cid == kCapabilityCid) return false;
+  if (cid == kNullCid) return false;
 
   // These are shared and use identity hash codes. If they are used as a key in
   // a map or a value in a set, they will already have the identity hash code
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 333df32..b11e1a4 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -3666,30 +3666,29 @@
   TRACE_RUNTIME_CALL("AllocateHandle returning %p", return_value);
   return return_value;
 }
+
 DEFINE_RAW_LEAF_RUNTIME_ENTRY(
     AllocateHandle,
     1,
     false /* is_float */,
     reinterpret_cast<RuntimeFunction>(&DLRT_AllocateHandle));
 
+#if defined(USING_THREAD_SANITIZER)
+#define TSAN_ACQUIRE reinterpret_cast<RuntimeFunction>(&__tsan_acquire)
+#define TSAN_RELEASE reinterpret_cast<RuntimeFunction>(&__tsan_release)
+#else
+#define TSAN_ACQUIRE nullptr
+#define TSAN_RELEASE nullptr
+#endif
+
 DEFINE_RAW_LEAF_RUNTIME_ENTRY(TsanLoadAcquire,
                               /*argument_count=*/1,
                               /*is_float=*/false,
-#if defined(USING_THREAD_SANITIZER)
-                              reinterpret_cast<RuntimeFunction>(&__tsan_acquire)
-#else
-                              nullptr
-#endif
-);
+                              TSAN_ACQUIRE);
 
 DEFINE_RAW_LEAF_RUNTIME_ENTRY(TsanStoreRelease,
                               /*argument_count=*/1,
                               /*is_float=*/false,
-#if defined(USING_THREAD_SANITIZER)
-                              reinterpret_cast<RuntimeFunction>(&__tsan_release)
-#else
-                              nullptr
-#endif
-);
+                              TSAN_RELEASE);
 
 }  // namespace dart
diff --git a/tests/ffi/ffi_native_test.dart b/tests/ffi/ffi_native_test.dart
index 6997360..df21804 100644
--- a/tests/ffi/ffi_native_test.dart
+++ b/tests/ffi/ffi_native_test.dart
@@ -4,6 +4,9 @@
 //
 // SharedObjects=ffi_test_functions
 
+// NOTE: There is no `test/ffi_2/...` version of this test since annotations
+// with type arguments isn't supported in that version of Dart.
+
 import 'dart:ffi';
 
 import 'package:expect/expect.dart';
@@ -22,11 +25,32 @@
 @FfiNative<IntPtr Function(IntPtr)>('ReturnIntPtr', isLeaf: true)
 external int returnIntPtrLeaf(int x);
 
+@FfiNative<IntPtr Function()>('IsThreadInGenerated')
+external int isThreadInGenerated();
+
+@FfiNative<IntPtr Function()>('IsThreadInGenerated', isLeaf: true)
+external int isThreadInGeneratedLeaf();
+
+// Error: FFI leaf call must not have Handle return type.
+@FfiNative<Handle Function()>("foo", isLeaf: true)  //# 01: compile-time error
+external Object foo();  //# 01: compile-time error
+
+// Error: FFI leaf call must not have Handle argument types.
+@FfiNative<Void Function(Handle)>("bar", isLeaf: true)  //# 02: compile-time error
+external void bar(Object);  //# 02: compile-time error
+
 void main() {
   // Register test resolver for top-level functions above.
   final root_lib_url = getRootLibraryUrl();
   setFfiNativeResolverForTest(root_lib_url);
 
+  // Test we can call FfiNative functions.
   Expect.equals(123, returnIntPtr(123));
   Expect.equals(123, returnIntPtrLeaf(123));
+
+  // Test FfiNative leaf calls remain in generated code.
+  // Regular calls should transition generated -> native.
+  Expect.equals(0, isThreadInGenerated());
+  // Leaf calls should remain in generated state.
+  Expect.equals(1, isThreadInGeneratedLeaf());
 }
diff --git a/tests/web/regress/46589_test.dart b/tests/web/regress/46589_test.dart
new file mode 100644
index 0000000..c4070d5
--- /dev/null
+++ b/tests/web/regress/46589_test.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+
+typedef MyVoid = void;
+
+bool isVoid<X>() {
+  return X == MyVoid;
+}
+
+void main() {
+  Expect.isFalse(isVoid<int>());
+  Expect.isTrue(isVoid<void>());
+}
diff --git a/tests/web_2/regress/46589_test.dart b/tests/web_2/regress/46589_test.dart
new file mode 100644
index 0000000..c4070d5
--- /dev/null
+++ b/tests/web_2/regress/46589_test.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+
+typedef MyVoid = void;
+
+bool isVoid<X>() {
+  return X == MyVoid;
+}
+
+void main() {
+  Expect.isFalse(isVoid<int>());
+  Expect.isTrue(isVoid<void>());
+}
diff --git a/tools/VERSION b/tools/VERSION
index a4eae4a..10aac58 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 322
+PRERELEASE 323
 PRERELEASE_PATCH 0
\ No newline at end of file