Version 2.10.0-91.0.dev

Merge commit 'd313fb70b6720c4c1b67ccc191040b23a7cce498' into 'dev'
diff --git a/DEPS b/DEPS
index 2e5429d..38e8121 100644
--- a/DEPS
+++ b/DEPS
@@ -99,7 +99,7 @@
   "chromedriver_tag": "83.0.4103.39",
   "dartdoc_rev" : "291ebc50072746bc59ccab59115a298915218428",
   "ffi_rev": "454ab0f9ea6bd06942a983238d8a6818b1357edb",
-  "fixnum_rev": "300c3f025e94a72b7b6770e15f76a4de15f77668",
+  "fixnum_rev": "16d3890c6dc82ca629659da1934e412292508bba",
   "glob_rev": "e9f4e6b7ae8abe5071461cf8f47191bb19cf7ef6",
   "html_rev": "083a36cd801a4b787ba156b7c6e4c8b2e2daed4a",
   "http_io_rev": "2fa188caf7937e313026557713f7feffedd4978b",
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index 7e5dc01..8af47db 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -109,7 +109,7 @@
 <body>
 <h1>Analysis Server API Specification</h1>
 <h1 style="color:#999999">Version
-  1.28.0
+  1.29.0
 </h1>
 <p>
   This document contains a specification of the API provided by the
@@ -4810,24 +4810,34 @@
       </dd><dt class="field"><b>offset: int</b></dt><dd>
         
         <p>
-          The offset of the region to which the user can navigate.
+          The offset of the name of the target to which the user can navigate.
         </p>
       </dd><dt class="field"><b>length: int</b></dt><dd>
         
         <p>
-          The length of the region to which the user can navigate.
+          The length of the name of the target to which the user can navigate.
         </p>
       </dd><dt class="field"><b>startLine: int</b></dt><dd>
         
         <p>
           The one-based index of the line containing the first character of the
-          region.
+          name of the target.
         </p>
       </dd><dt class="field"><b>startColumn: int</b></dt><dd>
         
         <p>
           The one-based index of the column containing the first character of
-          the region.
+          the name of the target.
+        </p>
+      </dd><dt class="field"><b>codeOffset: int<span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          The offset of the target code to which the user can navigate.
+        </p>
+      </dd><dt class="field"><b>codeLength: int<span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          The length of the target code to which the user can navigate.
         </p>
       </dd></dl></dd><dt class="typeDefinition"><a name="type_Occurrences">Occurrences: object</a></dt><dd>
     <p>
diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart
index 2ba9eea..8bfb877 100644
--- a/pkg/analysis_server/lib/protocol/protocol_constants.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart
@@ -6,7 +6,7 @@
 // To regenerate the file, use the script
 // "pkg/analysis_server/tool/spec/generate_files".
 
-const String PROTOCOL_VERSION = '1.28.0';
+const String PROTOCOL_VERSION = '1.29.0';
 
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
index 43bf70c..a6399a9 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
@@ -12,12 +12,13 @@
 import 'package:analysis_server/src/plugin/result_merger.dart';
 import 'package:analysis_server/src/protocol_server.dart' show NavigationTarget;
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/source/line_info.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
 import 'package:analyzer_plugin/utilities/navigation/navigation_dart.dart';
 
-class DefinitionHandler
-    extends MessageHandler<TextDocumentPositionParams, List<Location>>
+class DefinitionHandler extends MessageHandler<TextDocumentPositionParams,
+        Either2<List<Location>, List<LocationLink>>>
     with LspPluginRequestHandlerMixin {
   DefinitionHandler(LspAnalysisServer server) : super(server);
   @override
@@ -45,13 +46,15 @@
   }
 
   Future<AnalysisNavigationParams> getServerResult(
-      String path, int offset) async {
-    final collector = NavigationCollectorImpl();
+      bool clientSupportsLocationLink, String path, int offset) async {
+    final collector = NavigationCollectorImpl(
+        collectCodeLocations: clientSupportsLocationLink);
 
     final result = await server.getResolvedUnit(path);
     if (result?.state == ResultState.VALID) {
       computeDartNavigation(
           server.resourceProvider, collector, result.unit, offset, 0);
+      collector.createRegions();
     }
 
     return AnalysisNavigationParams(
@@ -59,24 +62,32 @@
   }
 
   @override
-  Future<ErrorOr<List<Location>>> handle(
+  Future<ErrorOr<Either2<List<Location>, List<LocationLink>>>> handle(
       TextDocumentPositionParams params, CancellationToken token) async {
+    final definitionCapabilities =
+        server?.clientCapabilities?.textDocument?.definition;
+
+    final clientSupportsLocationLink =
+        definitionCapabilities?.linkSupport == true;
+
     final pos = params.position;
     final path = pathOfDoc(params.textDocument);
 
     return path.mapResult((path) async {
       final lineInfo = server.getLineInfo(path);
-      // If there is no lineInfo, the request canot be translated from LSP line/col
+      // If there is no lineInfo, the request cannot be translated from LSP line/col
       // to server offset/length.
       if (lineInfo == null) {
-        return success(const []);
+        return success(
+          Either2<List<Location>, List<LocationLink>>.t1(const []),
+        );
       }
 
       final offset = toOffset(lineInfo, pos);
 
       return offset.mapResult((offset) async {
         final allResults = [
-          await getServerResult(path, offset),
+          await getServerResult(clientSupportsLocationLink, path, offset),
           ...await getPluginResults(path, offset),
         ];
 
@@ -84,28 +95,84 @@
         final mergedResults = merger.mergeNavigation(allResults);
         final mergedTargets = mergedResults?.targets ?? [];
 
-        Location toLocation(NavigationTarget target) {
-          final targetFilePath = mergedResults.files[target.fileIndex];
-          final lineInfo = server.getLineInfo(targetFilePath);
-          return navigationTargetToLocation(targetFilePath, target, lineInfo);
+        // Convert and filter the results using the correct type of Location class
+        // depending on the client capabilities.
+        if (clientSupportsLocationLink) {
+          final convertedResults = convert(
+            mergedTargets,
+            (target) => _toLocationLink(mergedResults, lineInfo, target),
+          ).toList();
+
+          final results = _filterResults(
+            convertedResults,
+            params.textDocument.uri,
+            pos.line,
+            (element) => element.targetUri,
+            (element) => element.targetSelectionRange,
+          );
+
+          return success(
+            Either2<List<Location>, List<LocationLink>>.t2(results),
+          );
+        } else {
+          final convertedResults = convert(
+            mergedTargets,
+            (target) => _toLocation(mergedResults, target),
+          ).toList();
+
+          final results = _filterResults(
+            convertedResults,
+            params.textDocument.uri,
+            pos.line,
+            (element) => element.uri,
+            (element) => element.range,
+          );
+
+          return success(
+            Either2<List<Location>, List<LocationLink>>.t1(results),
+          );
         }
-
-        final results = convert(mergedTargets, toLocation).toList();
-
-        // If we fetch navigation on a keyword like `var`, the results will include
-        // both the definition and also the variable name. This will cause the editor
-        // to show the user both options unnecessarily (the variable name is always
-        // adjacent to the var keyword, so providing navigation to it is not useful).
-        // To prevent this, filter the list to only those on different lines (or
-        // different files).
-        final otherResults = results
-            .where((element) =>
-                element.uri != params.textDocument.uri ||
-                element.range.start.line != pos.line)
-            .toList();
-
-        return success(otherResults.isNotEmpty ? otherResults : results);
       });
     });
   }
+
+  /// Helper that selects the correct results (filtering out at the same
+  /// line/location) generically, handling either type of Location class.
+  List<T> _filterResults<T>(
+    List<T> results,
+    String sourceUri,
+    int sourceLineNumber,
+    String Function(T) uriSelector,
+    Range Function(T) rangeSelector,
+  ) {
+    // If we fetch navigation on a keyword like `var`, the results will include
+    // both the definition and also the variable name. This will cause the editor
+    // to show the user both options unnecessarily (the variable name is always
+    // adjacent to the var keyword, so providing navigation to it is not useful).
+    // To prevent this, filter the list to only those on different lines (or
+    // different files).
+    final otherResults = results
+        .where((element) =>
+            uriSelector(element) != sourceUri ||
+            rangeSelector(element).start.line != sourceLineNumber)
+        .toList();
+
+    return otherResults.isNotEmpty ? otherResults : results;
+  }
+
+  Location _toLocation(
+      AnalysisNavigationParams mergedResults, NavigationTarget target) {
+    final targetFilePath = mergedResults.files[target.fileIndex];
+    final targetLineInfo = server.getLineInfo(targetFilePath);
+    return navigationTargetToLocation(targetFilePath, target, targetLineInfo);
+  }
+
+  LocationLink _toLocationLink(AnalysisNavigationParams mergedResults,
+      LineInfo sourceLineInfo, NavigationTarget target) {
+    final region = mergedResults.regions.first;
+    final targetFilePath = mergedResults.files[target.fileIndex];
+    final targetLineInfo = server.getLineInfo(targetFilePath);
+    return navigationTargetToLocationLink(
+        region, sourceLineInfo, targetFilePath, target, targetLineInfo);
+  }
 }
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index 6249c90..0f44750 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -33,6 +33,8 @@
 import 'package:analyzer/src/services/available_declarations.dart';
 import 'package:analyzer/src/services/available_declarations.dart' as dec;
 import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
+import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart'
+    as server;
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart' as server;
 import 'package:meta/meta.dart';
 
@@ -538,15 +540,42 @@
 bool isDartDocument(lsp.TextDocumentIdentifier doc) =>
     doc?.uri?.endsWith('.dart');
 
-lsp.Location navigationTargetToLocation(String targetFilePath,
-    server.NavigationTarget target, server.LineInfo lineInfo) {
-  if (lineInfo == null) {
+lsp.Location navigationTargetToLocation(
+  String targetFilePath,
+  server.NavigationTarget target,
+  server.LineInfo targetLineInfo,
+) {
+  if (targetLineInfo == null) {
     return null;
   }
 
   return lsp.Location(
     uri: Uri.file(targetFilePath).toString(),
-    range: toRange(lineInfo, target.offset, target.length),
+    range: toRange(targetLineInfo, target.offset, target.length),
+  );
+}
+
+lsp.LocationLink navigationTargetToLocationLink(
+  server.NavigationRegion region,
+  server.LineInfo regionLineInfo,
+  String targetFilePath,
+  server.NavigationTarget target,
+  server.LineInfo targetLineInfo,
+) {
+  if (regionLineInfo == null || targetLineInfo == null) {
+    return null;
+  }
+
+  final nameRange = toRange(targetLineInfo, target.offset, target.length);
+  final codeRange = target.codeOffset != null && target.codeLength != null
+      ? toRange(targetLineInfo, target.codeOffset, target.codeLength)
+      : nameRange;
+
+  return lsp.LocationLink(
+    originSelectionRange: toRange(regionLineInfo, region.offset, region.length),
+    targetUri: Uri.file(targetFilePath).toString(),
+    targetRange: codeRange,
+    targetSelectionRange: nameRange,
   );
 }
 
diff --git a/pkg/analysis_server/lib/src/plugin/result_merger.dart b/pkg/analysis_server/lib/src/plugin/result_merger.dart
index b4ca545..91a5a65 100644
--- a/pkg/analysis_server/lib/src/plugin/result_merger.dart
+++ b/pkg/analysis_server/lib/src/plugin/result_merger.dart
@@ -284,7 +284,8 @@
         var newIndex = fileMap[target.fileIndex];
         if (target.fileIndex != newIndex) {
           target = NavigationTarget(target.kind, newIndex, target.offset,
-              target.length, target.startLine, target.startColumn);
+              target.length, target.startLine, target.startColumn,
+              codeOffset: target.codeOffset, codeLength: target.codeLength);
         }
         var index = mergedTargets.indexOf(target);
         if (index < 0) {
diff --git a/pkg/analysis_server/test/integration/edit/get_assists_test.dart b/pkg/analysis_server/test/integration/edit/get_assists_test.dart
index a84f534..b500903 100644
--- a/pkg/analysis_server/test/integration/edit/get_assists_test.dart
+++ b/pkg/analysis_server/test/integration/edit/get_assists_test.dart
@@ -21,7 +21,7 @@
     var text = r'''
 import 'dart:async';
 
-Future f;
+Completer c;
 ''';
     writeFile(pathname, text);
     standardAnalysisSetup();
diff --git a/pkg/analysis_server/test/integration/edit/organize_directives_test.dart b/pkg/analysis_server/test/integration/edit/organize_directives_test.dart
index 8a74524..2bf4c1f 100644
--- a/pkg/analysis_server/test/integration/edit/organize_directives_test.dart
+++ b/pkg/analysis_server/test/integration/edit/organize_directives_test.dart
@@ -21,7 +21,7 @@
 import 'dart:math';
 import 'dart:async';
 
-Future foo;
+Completer foo;
 int minified(int x, int y) => min(x, y);
 ''';
     writeFile(pathname, text);
@@ -40,7 +40,7 @@
 import 'dart:async';
 import 'dart:math';
 
-Future foo;
+Completer foo;
 int minified(int x, int y) => min(x, y);
 ''';
     writeFile(pathname, text);
@@ -57,7 +57,7 @@
 import 'dart:async'
 import 'dart:math';
 
-Future foo;
+Completer foo;
 int minified(int x, int y) => min(x, y);
 ''';
     writeFile(pathname, text);
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index f4736c1..ffeed70 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -1119,6 +1119,8 @@
 ///   "length": int
 ///   "startLine": int
 ///   "startColumn": int
+///   "codeOffset": optional int
+///   "codeLength": optional int
 /// }
 final Matcher isNavigationTarget =
     LazyMatcher(() => MatchesJsonObject('NavigationTarget', {
@@ -1128,6 +1130,9 @@
           'length': isInt,
           'startLine': isInt,
           'startColumn': isInt
+        }, optionalFields: {
+          'codeOffset': isInt,
+          'codeLength': isInt
         }));
 
 /// Occurrences
diff --git a/pkg/analysis_server/test/lsp/code_actions_source_test.dart b/pkg/analysis_server/test/lsp/code_actions_source_test.dart
index d5529b4..1c15d8b 100644
--- a/pkg/analysis_server/test/lsp/code_actions_source_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_source_test.dart
@@ -25,14 +25,14 @@
 import 'dart:async';
 import 'dart:convert';
 
-Future foo;
+Completer foo;
 int minified(int x, int y) => min(x, y);
     ''';
     const expectedContent = '''
 import 'dart:async';
 import 'dart:math';
 
-Future foo;
+Completer foo;
 int minified(int x, int y) => min(x, y);
     ''';
     await newFile(mainFilePath, content: content);
@@ -54,14 +54,14 @@
 import 'dart:async';
 import 'dart:convert';
 
-Future foo;
+Completer foo;
 int minified(int x, int y) => min(x, y);
     ''';
     const expectedContent = '''
 import 'dart:async';
 import 'dart:math';
 
-Future foo;
+Completer foo;
 int minified(int x, int y) => min(x, y);
     ''';
     await newFile(mainFilePath, content: content);
@@ -133,7 +133,7 @@
 import 'dart:async';
 import 'dart:math';
 
-Future foo;
+Completer foo;
 int minified(int x, int y) => min(x, y);
     ''';
     await newFile(mainFilePath, content: content);
diff --git a/pkg/analysis_server/test/lsp/definition_test.dart b/pkg/analysis_server/test/lsp/definition_test.dart
index a70f005..a243579 100644
--- a/pkg/analysis_server/test/lsp/definition_test.dart
+++ b/pkg/analysis_server/test/lsp/definition_test.dart
@@ -46,8 +46,8 @@
     await initialize();
     await openFile(mainFileUri, withoutMarkers(mainContents));
     await openFile(referencedFileUri, withoutMarkers(referencedContents));
-    final res =
-        await getDefinition(mainFileUri, positionFromMarker(mainContents));
+    final res = await getDefinitionAsLocation(
+        mainFileUri, positionFromMarker(mainContents));
 
     expect(res, hasLength(1));
     var loc = res.single;
@@ -69,7 +69,7 @@
 
     newFile(pluginAnalyzedFilePath);
     await initialize();
-    final res = await getDefinition(
+    final res = await getDefinitionAsLocation(
         pluginAnalyzedFileUri, lsp.Position(line: 0, character: 0));
 
     expect(res, hasLength(1));
@@ -82,11 +82,97 @@
     expect(loc.uri, equals(pluginAnalyzedFileUri.toString()));
   }
 
+  Future<void> test_locationLink_field() async {
+    final mainContents = '''
+    import 'referenced.dart';
+
+    main() {
+      Icons.[[ad^d]]();
+    }
+    ''';
+
+    final referencedContents = '''
+    void unrelatedFunction() {}
+
+    class Icons {
+      /// `targetRange` should not include the dartDoc but should include the full
+      /// function body. `targetSelectionRange` will be just the name.
+      [[String add = "Test"]];
+    }
+
+    void otherUnrelatedFunction() {}
+    ''';
+
+    final referencedFileUri =
+        Uri.file(join(projectFolderPath, 'lib', 'referenced.dart'));
+
+    await initialize(
+        textDocumentCapabilities:
+            withLocationLinkSupport(emptyTextDocumentClientCapabilities));
+    await openFile(mainFileUri, withoutMarkers(mainContents));
+    await openFile(referencedFileUri, withoutMarkers(referencedContents));
+    final res = await getDefinitionAsLocationLinks(
+        mainFileUri, positionFromMarker(mainContents));
+
+    expect(res, hasLength(1));
+    var loc = res.single;
+    expect(loc.originSelectionRange, equals(rangeFromMarkers(mainContents)));
+    expect(loc.targetUri, equals(referencedFileUri.toString()));
+    expect(loc.targetRange, equals(rangeFromMarkers(referencedContents)));
+    expect(
+      loc.targetSelectionRange,
+      equals(rangeOfString(referencedContents, 'add')),
+    );
+  }
+
+  Future<void> test_locationLink_function() async {
+    final mainContents = '''
+    import 'referenced.dart';
+
+    main() {
+      [[fo^o]]();
+    }
+    ''';
+
+    final referencedContents = '''
+    void unrelatedFunction() {}
+
+    /// `targetRange` should not include the dartDoc but should include the full
+    /// function body. `targetSelectionRange` will be just the name.
+    [[void foo() {
+      // Contents of function
+    }]]
+
+    void otherUnrelatedFunction() {}
+    ''';
+
+    final referencedFileUri =
+        Uri.file(join(projectFolderPath, 'lib', 'referenced.dart'));
+
+    await initialize(
+        textDocumentCapabilities:
+            withLocationLinkSupport(emptyTextDocumentClientCapabilities));
+    await openFile(mainFileUri, withoutMarkers(mainContents));
+    await openFile(referencedFileUri, withoutMarkers(referencedContents));
+    final res = await getDefinitionAsLocationLinks(
+        mainFileUri, positionFromMarker(mainContents));
+
+    expect(res, hasLength(1));
+    var loc = res.single;
+    expect(loc.originSelectionRange, equals(rangeFromMarkers(mainContents)));
+    expect(loc.targetUri, equals(referencedFileUri.toString()));
+    expect(loc.targetRange, equals(rangeFromMarkers(referencedContents)));
+    expect(
+      loc.targetSelectionRange,
+      equals(rangeOfString(referencedContents, 'foo')),
+    );
+  }
+
   Future<void> test_nonDartFile() async {
     newFile(pubspecFilePath, content: simplePubspecContent);
     await initialize();
 
-    final res = await getDefinition(pubspecFileUri, startOfDocPos);
+    final res = await getDefinitionAsLocation(pubspecFileUri, startOfDocPos);
     expect(res, isEmpty);
   }
 
@@ -97,7 +183,8 @@
 
     await initialize();
     await openFile(mainFileUri, withoutMarkers(contents));
-    final res = await getDefinition(mainFileUri, positionFromMarker(contents));
+    final res = await getDefinitionAsLocation(
+        mainFileUri, positionFromMarker(contents));
 
     expect(res, hasLength(1));
     var loc = res.single;
@@ -114,7 +201,8 @@
 
     await initialize();
     await openFile(mainFileUri, withoutMarkers(contents));
-    final res = await getDefinition(mainFileUri, positionFromMarker(contents));
+    final res = await getDefinitionAsLocation(
+        mainFileUri, positionFromMarker(contents));
 
     expect(res, hasLength(1));
     var loc = res.single;
@@ -131,7 +219,8 @@
 
     newFile(mainFilePath, content: withoutMarkers(contents));
     await initialize();
-    final res = await getDefinition(mainFileUri, positionFromMarker(contents));
+    final res = await getDefinitionAsLocation(
+        mainFileUri, positionFromMarker(contents));
 
     expect(res, hasLength(1));
     var loc = res.single;
@@ -148,7 +237,8 @@
 
     await initialize();
     await openFile(mainFileUri, withoutMarkers(contents));
-    final res = await getDefinition(mainFileUri, positionFromMarker(contents));
+    final res = await getDefinitionAsLocation(
+        mainFileUri, positionFromMarker(contents));
 
     expect(res, hasLength(1));
     var loc = res.single;
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index 899b399..4f9bb53 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -340,6 +340,15 @@
     });
   }
 
+  TextDocumentClientCapabilities withLocationLinkSupport(
+    TextDocumentClientCapabilities source,
+  ) {
+    return extendTextDocumentCapabilities(source, {
+      'definition': {'linkSupport': true},
+      'implementation': {'linkSupport': true}
+    });
+  }
+
   TextDocumentClientCapabilities withSignatureHelpContentFormat(
     TextDocumentClientCapabilities source,
     List<MarkupKind> formats,
@@ -714,7 +723,8 @@
     return expectSuccessfulResponseTo<List<CompletionItem>>(request);
   }
 
-  Future<List<Location>> getDefinition(Uri uri, Position pos) {
+  Future<Either2<List<Location>, List<LocationLink>>> getDefinition(
+      Uri uri, Position pos) {
     final request = makeRequest(
       Method.textDocument_definition,
       TextDocumentPositionParams(
@@ -722,7 +732,24 @@
         position: pos,
       ),
     );
-    return expectSuccessfulResponseTo<List<Location>>(request);
+    return expectSuccessfulResponseTo(request);
+  }
+
+  Future<List<Location>> getDefinitionAsLocation(Uri uri, Position pos) async {
+    final results = await getDefinition(uri, pos);
+    return results.map(
+      (locations) => locations,
+      (locationLinks) => throw 'Expected List<Location> got List<LocationLink>',
+    );
+  }
+
+  Future<List<LocationLink>> getDefinitionAsLocationLinks(
+      Uri uri, Position pos) async {
+    final results = await getDefinition(uri, pos);
+    return results.map(
+      (locations) => throw 'Expected List<LocationLink> got List<Location>',
+      (locationLinks) => locationLinks,
+    );
   }
 
   Future<DartDiagnosticServer> getDiagnosticServer() {
@@ -1060,6 +1087,18 @@
     }
   }
 
+  /// Returns the range of [searchText] in [content].
+  Range rangeOfString(String content, String searchText) {
+    content = withoutMarkers(content);
+    final startOffset = content.indexOf(searchText);
+    return startOffset == -1
+        ? null
+        : Range(
+            start: positionFromOffset(startOffset, content),
+            end: positionFromOffset(startOffset + searchText.length, content),
+          );
+  }
+
   /// Returns all ranges surrounded by `[[markers]]` in the provided string,
   /// excluding the markers themselves (as well as position markers `^` from
   /// the offsets).
diff --git a/pkg/analysis_server/test/services/correction/organize_directives_test.dart b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
index 3ce8fbc..e5180c5 100644
--- a/pkg/analysis_server/test/services/correction/organize_directives_test.dart
+++ b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
@@ -83,14 +83,14 @@
 import 'dart:async';
 
 main() {
-  Future f;
+  Completer f;
 }''');
     // validate change
     _assertOrganize(r'''
 import 'dart:async';
 
 main() {
-  Future f;
+  Completer f;
 }''', removeUnused: true);
   }
 
@@ -164,7 +164,7 @@
 class A {}
 
 main() {
-  Future f;
+  Completer f;
 }''');
     // validate change
     _assertOrganize(r'''
@@ -173,7 +173,7 @@
 class A {}
 
 main() {
-  Future f;
+  Completer f;
 }''', removeUnused: true);
   }
 
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 a0e743f..bb31233 100644
--- a/pkg/analysis_server/test/src/plugin/result_merger_test.dart
+++ b/pkg/analysis_server/test/src/plugin/result_merger_test.dart
@@ -164,7 +164,8 @@
 
   void test_mergeNavigation() {
     NavigationTarget target(int fileIndex, int offset) {
-      return NavigationTarget(ElementKind.CLASS, fileIndex, offset, 1, 0, 0);
+      return NavigationTarget(ElementKind.CLASS, fileIndex, offset, 1, 0, 0,
+          codeOffset: offset, codeLength: 1);
     }
 
     //
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_async_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_async_test.dart
index a7e5fb1..d47cf3a 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_async_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_async_test.dart
@@ -25,7 +25,6 @@
 
   Future<void> test_asyncFor() async {
     await resolveTestUnit('''
-import 'dart:async';
 void main(Stream<String> names) {
   await for (String name in names) {
     print(name);
@@ -33,7 +32,6 @@
 }
 ''');
     await assertHasFix('''
-import 'dart:async';
 Future<void> main(Stream<String> names) async {
   await for (String name in names) {
     print(name);
@@ -78,15 +76,11 @@
 
   Future<void> test_closure() async {
     await resolveTestUnit('''
-import 'dart:async';
-
 void takeFutureCallback(Future callback()) {}
 
 void doStuff() => takeFutureCallback(() => await 1);
 ''');
     await assertHasFix('''
-import 'dart:async';
-
 void takeFutureCallback(Future callback()) {}
 
 void doStuff() => takeFutureCallback(() async => await 1);
@@ -115,7 +109,6 @@
 
   Future<void> test_returnFuture_alreadyFuture() async {
     await resolveTestUnit('''
-import 'dart:async';
 foo() {}
 Future<int> main() {
   await foo();
@@ -123,7 +116,6 @@
 }
 ''');
     await assertHasFix('''
-import 'dart:async';
 foo() {}
 Future<int> main() async {
   await foo();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
index 7ac2650..f3f154c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
@@ -72,7 +72,7 @@
 import 'dart:math'; import 'dart:async';
 
 main() {
-  Future f;
+  Completer f;
   print(f);
 }
 ''');
@@ -80,7 +80,7 @@
 import 'dart:async';
 
 main() {
-  Future f;
+  Completer f;
   print(f);
 }
 ''');
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/NavigationTarget.java b/pkg/analysis_server/tool/spec/generated/java/types/NavigationTarget.java
index a52c112..6954edf 100644
--- a/pkg/analysis_server/tool/spec/generated/java/types/NavigationTarget.java
+++ b/pkg/analysis_server/tool/spec/generated/java/types/NavigationTarget.java
@@ -46,37 +46,49 @@
   private final int fileIndex;
 
   /**
-   * The offset of the region to which the user can navigate.
+   * The offset of the name of the target to which the user can navigate.
    */
   private final int offset;
 
   /**
-   * The length of the region to which the user can navigate.
+   * The length of the name of the target to which the user can navigate.
    */
   private final int length;
 
   /**
-   * The one-based index of the line containing the first character of the region.
+   * The one-based index of the line containing the first character of the name of the target.
    */
   private final int startLine;
 
   /**
-   * The one-based index of the column containing the first character of the region.
+   * The one-based index of the column containing the first character of the name of the target.
    */
   private final int startColumn;
 
+  /**
+   * The offset of the target code to which the user can navigate.
+   */
+  private final Integer codeOffset;
+
+  /**
+   * The length of the target code to which the user can navigate.
+   */
+  private final Integer codeLength;
+
   private String file;
 
   /**
    * Constructor for {@link NavigationTarget}.
    */
-  public NavigationTarget(String kind, int fileIndex, int offset, int length, int startLine, int startColumn) {
+  public NavigationTarget(String kind, int fileIndex, int offset, int length, int startLine, int startColumn, Integer codeOffset, Integer codeLength) {
     this.kind = kind;
     this.fileIndex = fileIndex;
     this.offset = offset;
     this.length = length;
     this.startLine = startLine;
     this.startColumn = startColumn;
+    this.codeOffset = codeOffset;
+    this.codeLength = codeLength;
   }
 
   @Override
@@ -89,7 +101,9 @@
         other.offset == offset &&
         other.length == length &&
         other.startLine == startLine &&
-        other.startColumn == startColumn;
+        other.startColumn == startColumn &&
+        ObjectUtilities.equals(other.codeOffset, codeOffset) &&
+        ObjectUtilities.equals(other.codeLength, codeLength);
     }
     return false;
   }
@@ -101,7 +115,9 @@
     int length = jsonObject.get("length").getAsInt();
     int startLine = jsonObject.get("startLine").getAsInt();
     int startColumn = jsonObject.get("startColumn").getAsInt();
-    return new NavigationTarget(kind, fileIndex, offset, length, startLine, startColumn);
+    Integer codeOffset = jsonObject.get("codeOffset") == null ? null : jsonObject.get("codeOffset").getAsInt();
+    Integer codeLength = jsonObject.get("codeLength") == null ? null : jsonObject.get("codeLength").getAsInt();
+    return new NavigationTarget(kind, fileIndex, offset, length, startLine, startColumn, codeOffset, codeLength);
   }
 
   public static List<NavigationTarget> fromJsonArray(JsonArray jsonArray) {
@@ -121,6 +137,20 @@
   }
 
   /**
+   * The length of the target code to which the user can navigate.
+   */
+  public Integer getCodeLength() {
+    return codeLength;
+  }
+
+  /**
+   * The offset of the target code to which the user can navigate.
+   */
+  public Integer getCodeOffset() {
+    return codeOffset;
+  }
+
+  /**
    * The index of the file (in the enclosing navigation response) to navigate to.
    */
   public int getFileIndex() {
@@ -135,28 +165,28 @@
   }
 
   /**
-   * The length of the region to which the user can navigate.
+   * The length of the name of the target to which the user can navigate.
    */
   public int getLength() {
     return length;
   }
 
   /**
-   * The offset of the region to which the user can navigate.
+   * The offset of the name of the target to which the user can navigate.
    */
   public int getOffset() {
     return offset;
   }
 
   /**
-   * The one-based index of the column containing the first character of the region.
+   * The one-based index of the column containing the first character of the name of the target.
    */
   public int getStartColumn() {
     return startColumn;
   }
 
   /**
-   * The one-based index of the line containing the first character of the region.
+   * The one-based index of the line containing the first character of the name of the target.
    */
   public int getStartLine() {
     return startLine;
@@ -171,6 +201,8 @@
     builder.append(length);
     builder.append(startLine);
     builder.append(startColumn);
+    builder.append(codeOffset);
+    builder.append(codeLength);
     return builder.toHashCode();
   }
 
@@ -186,6 +218,12 @@
     jsonObject.addProperty("length", length);
     jsonObject.addProperty("startLine", startLine);
     jsonObject.addProperty("startColumn", startColumn);
+    if (codeOffset != null) {
+      jsonObject.addProperty("codeOffset", codeOffset);
+    }
+    if (codeLength != null) {
+      jsonObject.addProperty("codeLength", codeLength);
+    }
     return jsonObject;
   }
 
@@ -204,7 +242,11 @@
     builder.append("startLine=");
     builder.append(startLine + ", ");
     builder.append("startColumn=");
-    builder.append(startColumn);
+    builder.append(startColumn + ", ");
+    builder.append("codeOffset=");
+    builder.append(codeOffset + ", ");
+    builder.append("codeLength=");
+    builder.append(codeLength);
     builder.append("]");
     return builder.toString();
   }
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index 5a3e9d2..0247bb1 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -7,7 +7,7 @@
 <body>
 <h1>Analysis Server API Specification</h1>
 <h1 style="color:#999999">Version
-  <version>1.28.0</version>
+  <version>1.29.0</version>
 </h1>
 <p>
   This document contains a specification of the API provided by the
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
index 2ba9eea..8bfb877 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
@@ -6,7 +6,7 @@
 // To regenerate the file, use the script
 // "pkg/analysis_server/tool/spec/generate_files".
 
-const String PROTOCOL_VERSION = '1.28.0';
+const String PROTOCOL_VERSION = '1.29.0';
 
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
index 191f7af..ead4938 100644
--- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart
+++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
@@ -737,6 +737,10 @@
       if (superConstructor != null) {
         superArguments ??= astFactory.nodeList<Expression>(null);
 
+        if (constructor is ConstructorMember && constructor.isLegacy) {
+          superConstructor = Member.legacy(superConstructor);
+        }
+
         evaluateSuperConstructorCall(node, fieldMap, superConstructor,
             superArguments, initializerVisitor, externalErrorReporter);
       }
diff --git a/pkg/analyzer/lib/src/dart/element/extensions.dart b/pkg/analyzer/lib/src/dart/element/extensions.dart
index bd4bd24..1fccef8 100644
--- a/pkg/analyzer/lib/src/dart/element/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/element/extensions.dart
@@ -8,6 +8,23 @@
 import 'package:analyzer/src/generated/utilities_dart.dart';
 
 extension ElementExtension on Element {
+  /// Return `true` if this element is an instance member.
+  ///
+  /// Only [MethodElement]s and [PropertyAccessorElement]s are supported.
+  /// We intentionally exclude [ConstructorElement]s - they can only be
+  /// invoked in instance creation expressions, and [FieldElement]s - they
+  /// cannot be invoked directly and are always accessed using corresponding
+  /// [PropertyAccessorElement]s.
+  bool get isInstanceMember {
+    var this_ = this;
+    var enclosing = this_.enclosingElement;
+    if (enclosing is ClassElement || enclosing is ExtensionElement) {
+      return this_ is MethodElement && !this_.isStatic ||
+          this_ is PropertyAccessorElement && !this_.isStatic;
+    }
+    return false;
+  }
+
   /// Return `true` if this element, the enclosing class (if there is one), or
   /// the enclosing library, has been annotated with the `@doNotStore`
   /// annotation.
diff --git a/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart b/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart
index 9fedd9c..19e6a7e 100644
--- a/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart
@@ -4,6 +4,7 @@
 
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/extensions.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:meta/meta.dart';
 
@@ -30,7 +31,7 @@
             requested: _resolver.toLegacyElement(scopeSetter),
           );
         }
-        if (!_isInstanceMember(scopeGetter)) {
+        if (!scopeGetter.isInstanceMember) {
           return LexicalLookupResult(
             recovery: _resolver.toLegacyElement(scopeGetter),
           );
@@ -86,11 +87,6 @@
       }
     }
   }
-
-  static bool _isInstanceMember(Element element) {
-    var enclosing = element.enclosingElement;
-    return enclosing is ClassElement || enclosing is ExtensionElement;
-  }
 }
 
 class LexicalLookupResult {
diff --git a/pkg/analyzer/test/src/dart/resolution/assignment_test.dart b/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
index 9700848..c499ea1 100644
--- a/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
@@ -1941,6 +1941,80 @@
     assertType(assignment.rightHandSide, 'int');
   }
 
+  test_simpleIdentifier_staticGetter_superSetter_simple() async {
+    await assertErrorsInCode('''
+class A {
+  set x(num _) {}
+}
+
+class B extends A {
+  static int get x => 1;
+
+  void f() {
+    x = 2;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, 68, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 94, 1),
+    ]);
+
+    var assignment = findNode.assignment('x = 2');
+    assertAssignment(
+      assignment,
+      readElement: null,
+      readType: null,
+      writeElement: findElement.getter('x', of: 'B'),
+      writeType: 'dynamic',
+      operatorElement: null,
+      type: 'int',
+    );
+
+    assertSimpleIdentifier(
+      assignment.leftHandSide,
+      readElement: null,
+      writeElement: findElement.getter('x', of: 'B'),
+      type: null,
+    );
+  }
+
+  test_simpleIdentifier_staticMethod_superSetter_simple() async {
+    await assertErrorsInCode('''
+class A {
+  set x(num _) {}
+}
+
+class B extends A {
+  static void x() {}
+
+  void f() {
+    x = 2;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, 65, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_METHOD, 90, 1),
+    ]);
+
+    var assignment = findNode.assignment('x = 2');
+    assertAssignment(
+      assignment,
+      readElement: null,
+      readType: null,
+      writeElement: findElement.method('x', of: 'B'),
+      writeType: 'dynamic',
+      operatorElement: null,
+      type: 'int',
+    );
+
+    assertSimpleIdentifier(
+      assignment.leftHandSide,
+      readElement: null,
+      writeElement: findElement.method('x', of: 'B'),
+      type: null,
+    );
+  }
+
   test_simpleIdentifier_superSetter_simple() async {
     await assertNoErrorsInCode(r'''
 class A {
@@ -2125,6 +2199,45 @@
     assertType(assignment.rightHandSide, 'int');
   }
 
+  test_simpleIdentifier_topGetter_superSetter_simple() async {
+    await assertErrorsInCode('''
+class A {
+  set x(num _) {}
+}
+
+int get x => 1;
+
+class B extends A {
+
+  void f() {
+    x = 2;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 86, 1),
+    ]);
+
+    var assignment = findNode.assignment('x = 2');
+    assertAssignment(
+      assignment,
+      readElement: null,
+      readType: null,
+      writeElement: findElement.topGet('x'),
+      writeType: 'dynamic',
+      operatorElement: null,
+      type: 'int',
+    );
+
+    assertSimpleIdentifier(
+      assignment.leftHandSide,
+      readElement: null,
+      writeElement: findElement.topGet('x'),
+      type: null,
+    );
+
+    assertType(assignment.rightHandSide, 'int');
+  }
+
   test_simpleIdentifier_topGetter_topSetter_compound() async {
     await assertNoErrorsInCode('''
 int get x => 0;
diff --git a/pkg/analyzer/test/src/dart/resolution/constant_test.dart b/pkg/analyzer/test/src/dart/resolution/constant_test.dart
index 0d0a042..cfe11e5 100644
--- a/pkg/analyzer/test/src/dart/resolution/constant_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/constant_test.dart
@@ -237,6 +237,49 @@
 @reflectiveTest
 class ConstantResolutionWithNullSafetyTest extends PubPackageResolutionTest
     with WithNullSafetyMixin {
+  test_constructor_nullSafe_fromLegacy_super() async {
+    newFile('$testPackageLibPath/a.dart', content: r'''
+class A {
+  const A(List<Object> a);
+}
+
+class B extends A {
+  const B(List<Object> a) : super(a);
+}
+''');
+
+    await assertNoErrorsInCode(r'''
+// @dart = 2.8
+import 'a.dart';
+
+const a = <dynamic>[];
+const b = B(a);
+''');
+
+    var b = findElement.topVar('b');
+    assertType(b.computeConstantValue().type, 'B*');
+  }
+
+  test_constructor_nullSafe_fromLegacy_this() async {
+    newFile('$testPackageLibPath/a.dart', content: r'''
+class A {
+  const A(List<Object> a) : this(a);
+  const A.second(List<Object> a);
+}
+''');
+
+    await assertNoErrorsInCode(r'''
+// @dart = 2.8
+import 'a.dart';
+
+const a = <dynamic>[];
+const b = A(a);
+''');
+
+    var b = findElement.topVar('b');
+    assertType(b.computeConstantValue().type, 'A*');
+  }
+
   test_context_eliminateTypeVariables() async {
     await assertNoErrorsInCode(r'''
 class A<T> {
diff --git a/pkg/analyzer_plugin/doc/api.html b/pkg/analyzer_plugin/doc/api.html
index a95d2c0..76ba245 100644
--- a/pkg/analyzer_plugin/doc/api.html
+++ b/pkg/analyzer_plugin/doc/api.html
@@ -1693,24 +1693,34 @@
       </dd><dt class="field"><b>offset: int</b></dt><dd>
         
         <p>
-          The offset of the region to which the user can navigate.
+          The offset of the name of the target to which the user can navigate.
         </p>
       </dd><dt class="field"><b>length: int</b></dt><dd>
         
         <p>
-          The length of the region to which the user can navigate.
+          The length of the name of the target to which the user can navigate.
         </p>
       </dd><dt class="field"><b>startLine: int</b></dt><dd>
         
         <p>
           The one-based index of the line containing the first character of the
-          region.
+          name of the target.
         </p>
       </dd><dt class="field"><b>startColumn: int</b></dt><dd>
         
         <p>
           The one-based index of the column containing the first character of
-          the region.
+          the name of the target.
+        </p>
+      </dd><dt class="field"><b>codeOffset: int<span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          The offset of the target code to which the user can navigate.
+        </p>
+      </dd><dt class="field"><b>codeLength: int<span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          The length of the target code to which the user can navigate.
         </p>
       </dd></dl></dd><dt class="typeDefinition"><a name="type_Occurrences">Occurrences: object</a></dt><dd>
     <p>
diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
index 1179121..7e2bab3 100644
--- a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
+++ b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
@@ -3690,6 +3690,8 @@
 ///   "length": int
 ///   "startLine": int
 ///   "startColumn": int
+///   "codeOffset": optional int
+///   "codeLength": optional int
 /// }
 ///
 /// Clients may not extend, implement or mix-in this class.
@@ -3706,6 +3708,10 @@
 
   int _startColumn;
 
+  int _codeOffset;
+
+  int _codeLength;
+
   /// The kind of the element.
   ElementKind get kind => _kind;
 
@@ -3726,54 +3732,73 @@
     _fileIndex = value;
   }
 
-  /// The offset of the region to which the user can navigate.
+  /// The offset of the name of the target to which the user can navigate.
   int get offset => _offset;
 
-  /// The offset of the region to which the user can navigate.
+  /// The offset of the name of the target to which the user can navigate.
   set offset(int value) {
     assert(value != null);
     _offset = value;
   }
 
-  /// The length of the region to which the user can navigate.
+  /// The length of the name of the target to which the user can navigate.
   int get length => _length;
 
-  /// The length of the region to which the user can navigate.
+  /// The length of the name of the target to which the user can navigate.
   set length(int value) {
     assert(value != null);
     _length = value;
   }
 
   /// The one-based index of the line containing the first character of the
-  /// region.
+  /// name of the target.
   int get startLine => _startLine;
 
   /// The one-based index of the line containing the first character of the
-  /// region.
+  /// name of the target.
   set startLine(int value) {
     assert(value != null);
     _startLine = value;
   }
 
   /// The one-based index of the column containing the first character of the
-  /// region.
+  /// name of the target.
   int get startColumn => _startColumn;
 
   /// The one-based index of the column containing the first character of the
-  /// region.
+  /// name of the target.
   set startColumn(int value) {
     assert(value != null);
     _startColumn = value;
   }
 
+  /// The offset of the target code to which the user can navigate.
+  int get codeOffset => _codeOffset;
+
+  /// The offset of the target code to which the user can navigate.
+  set codeOffset(int value) {
+    _codeOffset = value;
+  }
+
+  /// The length of the target code to which the user can navigate.
+  int get codeLength => _codeLength;
+
+  /// The length of the target code to which the user can navigate.
+  set codeLength(int value) {
+    _codeLength = value;
+  }
+
   NavigationTarget(ElementKind kind, int fileIndex, int offset, int length,
-      int startLine, int startColumn) {
+      int startLine, int startColumn,
+      {int codeOffset, int codeLength}) {
     this.kind = kind;
     this.fileIndex = fileIndex;
     this.offset = offset;
     this.length = length;
     this.startLine = startLine;
     this.startColumn = startColumn;
+    this.codeOffset = codeOffset;
+    this.codeLength = codeLength;
   }
 
   factory NavigationTarget.fromJson(
@@ -3820,8 +3845,19 @@
       } else {
         throw jsonDecoder.mismatch(jsonPath, 'startColumn');
       }
+      int codeOffset;
+      if (json.containsKey('codeOffset')) {
+        codeOffset =
+            jsonDecoder.decodeInt(jsonPath + '.codeOffset', json['codeOffset']);
+      }
+      int codeLength;
+      if (json.containsKey('codeLength')) {
+        codeLength =
+            jsonDecoder.decodeInt(jsonPath + '.codeLength', json['codeLength']);
+      }
       return NavigationTarget(
-          kind, fileIndex, offset, length, startLine, startColumn);
+          kind, fileIndex, offset, length, startLine, startColumn,
+          codeOffset: codeOffset, codeLength: codeLength);
     } else {
       throw jsonDecoder.mismatch(jsonPath, 'NavigationTarget', json);
     }
@@ -3836,6 +3872,12 @@
     result['length'] = length;
     result['startLine'] = startLine;
     result['startColumn'] = startColumn;
+    if (codeOffset != null) {
+      result['codeOffset'] = codeOffset;
+    }
+    if (codeLength != null) {
+      result['codeLength'] = codeLength;
+    }
     return result;
   }
 
@@ -3850,7 +3892,9 @@
           offset == other.offset &&
           length == other.length &&
           startLine == other.startLine &&
-          startColumn == other.startColumn;
+          startColumn == other.startColumn &&
+          codeOffset == other.codeOffset &&
+          codeLength == other.codeLength;
     }
     return false;
   }
@@ -3864,6 +3908,8 @@
     hash = JenkinsSmiHash.combine(hash, length.hashCode);
     hash = JenkinsSmiHash.combine(hash, startLine.hashCode);
     hash = JenkinsSmiHash.combine(hash, startColumn.hashCode);
+    hash = JenkinsSmiHash.combine(hash, codeOffset.hashCode);
+    hash = JenkinsSmiHash.combine(hash, codeLength.hashCode);
     return JenkinsSmiHash.finish(hash);
   }
 }
diff --git a/pkg/analyzer_plugin/lib/src/utilities/navigation/navigation.dart b/pkg/analyzer_plugin/lib/src/utilities/navigation/navigation.dart
index cb9dadd..c660f85 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/navigation/navigation.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/navigation/navigation.dart
@@ -33,32 +33,46 @@
 
 /// A concrete implementation of [NavigationCollector].
 class NavigationCollectorImpl implements NavigationCollector {
+  /// Whether the collector is collecting target code locations. Computers can
+  /// skip computing these if this is false.
+  @override
+  final bool collectCodeLocations;
+
   /// A list of navigation regions.
   final List<NavigationRegion> regions = <NavigationRegion>[];
+
   final Map<SourceRange, List<int>> regionMap = <SourceRange, List<int>>{};
 
   /// All the unique targets referenced by [regions].
   final List<NavigationTarget> targets = <NavigationTarget>[];
+
   final Map<Pair<ElementKind, Location>, int> targetMap =
       <Pair<ElementKind, Location>, int>{};
 
   /// All the unique files referenced by [targets].
   final List<String> files = <String>[];
+
   final Map<String, int> fileMap = <String, int>{};
 
+  NavigationCollectorImpl({this.collectCodeLocations = false});
+
   @override
   void addRange(
-      SourceRange range, ElementKind targetKind, Location targetLocation) {
-    addRegion(range.offset, range.length, targetKind, targetLocation);
+      SourceRange range, ElementKind targetKind, Location targetLocation,
+      {Location targetCodeLocation}) {
+    addRegion(range.offset, range.length, targetKind, targetLocation,
+        targetCodeLocation: targetCodeLocation);
   }
 
   @override
   void addRegion(
-      int offset, int length, ElementKind targetKind, Location targetLocation) {
+      int offset, int length, ElementKind targetKind, Location targetLocation,
+      {Location targetCodeLocation}) {
     var range = SourceRange(offset, length);
     // add new target
     var targets = regionMap.putIfAbsent(range, () => <int>[]);
-    var targetIndex = _addTarget(targetKind, targetLocation);
+    var targetIndex =
+        _addTarget(targetKind, targetLocation, targetCodeLocation);
     targets.add(targetIndex);
   }
 
@@ -82,7 +96,7 @@
     return index;
   }
 
-  int _addTarget(ElementKind kind, Location location) {
+  int _addTarget(ElementKind kind, Location location, Location codeLocation) {
     var pair = Pair<ElementKind, Location>(kind, location);
     var index = targetMap[pair];
     if (index == null) {
@@ -90,7 +104,9 @@
       var fileIndex = _addFile(file);
       index = targets.length;
       var target = NavigationTarget(kind, fileIndex, location.offset,
-          location.length, location.startLine, location.startColumn);
+          location.length, location.startLine, location.startColumn,
+          codeOffset: collectCodeLocations ? codeLocation?.offset : null,
+          codeLength: collectCodeLocations ? codeLocation?.length : null);
       targets.add(target);
       targetMap[pair] = index;
     }
diff --git a/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart b/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart
index f1b94e7..f7347d2 100644
--- a/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart
+++ b/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart
@@ -186,12 +186,13 @@
       plugin.AnalysisErrorType(type.name);
 
   /// Create a location based on an the given [element].
-  plugin.Location locationFromElement(analyzer.Element element) {
+  plugin.Location locationFromElement(analyzer.Element element,
+      {int offset, int length}) {
     if (element == null || element.source == null) {
       return null;
     }
-    var offset = element.nameOffset;
-    var length = element.nameLength;
+    offset ??= element.nameOffset;
+    length ??= element.nameLength;
     if (element is analyzer.CompilationUnitElement ||
         (element is analyzer.LibraryElement && offset < 0)) {
       offset = 0;
diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation.dart
index cd93264..74d0821 100644
--- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation.dart
+++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation.dart
@@ -26,15 +26,21 @@
 ///
 /// Clients may not extend, implement or mix-in this class.
 abstract class NavigationCollector {
+  /// Whether the collector is collecting target code locations. Computers can
+  /// skip computing these if this is false.
+  bool get collectCodeLocations;
+
   /// Record a new navigation region corresponding to the given [range] that
-  /// should navigate to the given [targetLocation].
+  /// should navigate to the given [targetNameLocation].
   void addRange(
-      SourceRange range, ElementKind targetKind, Location targetLocation);
+      SourceRange range, ElementKind targetKind, Location targetLocation,
+      {Location targetCodeLocation});
 
   /// Record a new navigation region with the given [offset] and [length] that
-  /// should navigate to the given [targetLocation].
-  void addRegion(
-      int offset, int length, ElementKind targetKind, Location targetLocation);
+  /// should navigate to the given [targetNameLocation].
+  void addRegion(int offset, int length, ElementKind targetKind,
+      Location targetNameLocation,
+      {Location targetCodeLocation});
 }
 
 /// An object used to produce navigation regions.
diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
index e09c6fb..57ee4dc 100644
--- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
@@ -62,7 +62,13 @@
     if (location == null) {
       return;
     }
-    collector.addRegion(offset, length, kind, location);
+
+    var codeLocation = collector.collectCodeLocations
+        ? _getCodeLocation(element, location, converter)
+        : null;
+
+    collector.addRegion(offset, length, kind, location,
+        targetCodeLocation: codeLocation);
   }
 
   void _addRegion_nodeStart_nodeEnd(AstNode a, AstNode b, Element element) {
@@ -85,6 +91,51 @@
     var length = token.length;
     _addRegion(offset, length, element);
   }
+
+  /// Get the location of the code (excluding leading doc comments) for this element.
+  protocol.Location _getCodeLocation(Element element,
+      protocol.Location location, AnalyzerConverter converter) {
+    var codeElement = element;
+    // For synthetic getters created for fields, we need to access the associated
+    // variable to get the codeOffset/codeLength.
+    if (codeElement.isSynthetic && codeElement is PropertyAccessorElementImpl) {
+      final variable = (codeElement as PropertyAccessorElementImpl).variable;
+      if (variable is ElementImpl) {
+        codeElement = variable as ElementImpl;
+      }
+    }
+
+    // Read the main codeOffset from the element. This may include doc comments
+    // but will give the correct end position.
+    int codeOffset, codeLength;
+    if (codeElement is ElementImpl) {
+      codeOffset = codeElement.codeOffset;
+      codeLength = codeElement.codeLength;
+    }
+
+    // Read the declaration so we can get the offset after the doc comments.
+    final declaration = codeElement.session
+        .getParsedLibrary(location.file)
+        .getElementDeclaration(codeElement);
+    var node = declaration?.node;
+    if (node is VariableDeclaration) {
+      node = node.parent;
+    }
+    if (node is AnnotatedNode) {
+      var offsetAfterDocs = node.firstTokenAfterCommentAndMetadata.offset;
+
+      // Reduce the length by the difference between the end of docs and the start.
+      codeLength -= (offsetAfterDocs - codeOffset);
+      codeOffset = offsetAfterDocs;
+    }
+
+    if (codeOffset == null || codeLength == null) {
+      return null;
+    }
+
+    return converter.locationFromElement(element,
+        offset: codeOffset, length: codeLength);
+  }
 }
 
 class _DartNavigationComputerVisitor extends RecursiveAstVisitor<void> {
diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
index 2ca8d47..53cebf0 100644
--- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
+++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
@@ -604,6 +604,8 @@
 ///   "length": int
 ///   "startLine": int
 ///   "startColumn": int
+///   "codeOffset": optional int
+///   "codeLength": optional int
 /// }
 final Matcher isNavigationTarget =
     LazyMatcher(() => MatchesJsonObject('NavigationTarget', {
@@ -613,6 +615,9 @@
           'length': isInt,
           'startLine': isInt,
           'startColumn': isInt
+        }, optionalFields: {
+          'codeOffset': isInt,
+          'codeLength': isInt
         }));
 
 /// Occurrences
diff --git a/pkg/analyzer_plugin/tool/spec/common_types_spec.html b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
index df9067d..c49dce7 100644
--- a/pkg/analyzer_plugin/tool/spec/common_types_spec.html
+++ b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
@@ -6,7 +6,7 @@
 </head>
 <body>
 <h1>Common Types</h1>
-<version>1.1.0</version>
+<version>1.2.0</version>
 <p>
   This document contains a specification of the types that are common between
   the analysis server wire protocol and the analysis server plugin wire
@@ -1102,27 +1102,39 @@
       <field name="offset">
         <ref>int</ref>
         <p>
-          The offset of the region to which the user can navigate.
+          The offset of the name of the target to which the user can navigate.
         </p>
       </field>
       <field name="length">
         <ref>int</ref>
         <p>
-          The length of the region to which the user can navigate.
+          The length of the name of the target to which the user can navigate.
         </p>
       </field>
       <field name="startLine">
         <ref>int</ref>
         <p>
           The one-based index of the line containing the first character of the
-          region.
+          name of the target.
         </p>
       </field>
       <field name="startColumn">
         <ref>int</ref>
         <p>
           The one-based index of the column containing the first character of
-          the region.
+          the name of the target.
+        </p>
+      </field>
+      <field name="codeOffset" optional="true">
+        <ref>int</ref>
+        <p>
+          The offset of the target code to which the user can navigate.
+        </p>
+      </field>
+      <field name="codeLength" optional="true">
+        <ref>int</ref>
+        <p>
+          The length of the target code to which the user can navigate.
         </p>
       </field>
     </object>
diff --git a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
index 05b2e5f..13e52db 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
@@ -62,7 +62,7 @@
   bool isLocalVariableWithoutDeclaredType(PromotableElement variable) {
     return variable is LocalVariableElement &&
         variable.hasImplicitType &&
-        variable.initializer == null;
+        !variable.hasInitializer;
   }
 
   @override
diff --git a/pkg/nnbd_migration/pubspec.yaml b/pkg/nnbd_migration/pubspec.yaml
index cbb34cd..e642a00 100644
--- a/pkg/nnbd_migration/pubspec.yaml
+++ b/pkg/nnbd_migration/pubspec.yaml
@@ -6,7 +6,7 @@
   sdk: '>=2.6.0 <3.0.0'
 dependencies:
   _fe_analyzer_shared: ^4.0.0
-  analyzer: ^0.39.12
+  analyzer: ^0.40.1
   analyzer_plugin: ^0.2.4
   args: ^1.4.4
   charcode: ^1.1.2
diff --git a/runtime/bin/file_system_watcher.h b/runtime/bin/file_system_watcher.h
index 36c23d5..c9a54e7 100644
--- a/runtime/bin/file_system_watcher.h
+++ b/runtime/bin/file_system_watcher.h
@@ -24,7 +24,7 @@
     kModifyContent = 1 << 1,
     kDelete = 1 << 2,
     kMove = 1 << 3,
-    kModefyAttribute = 1 << 4,
+    kModifyAttribute = 1 << 4,
     kDeleteSelf = 1 << 5,
     kIsDir = 1 << 6
   };
diff --git a/runtime/bin/file_system_watcher_android.cc b/runtime/bin/file_system_watcher_android.cc
index 0bef08c..d403351 100644
--- a/runtime/bin/file_system_watcher_android.cc
+++ b/runtime/bin/file_system_watcher_android.cc
@@ -81,7 +81,7 @@
     mask |= FileSystemWatcher::kModifyContent;
   }
   if ((e->mask & IN_ATTRIB) != 0) {
-    mask |= FileSystemWatcher::kModefyAttribute;
+    mask |= FileSystemWatcher::kModifyAttribute;
   }
   if ((e->mask & IN_CREATE) != 0) {
     mask |= FileSystemWatcher::kCreate;
diff --git a/runtime/bin/file_system_watcher_linux.cc b/runtime/bin/file_system_watcher_linux.cc
index f16b87c..daa2d26 100644
--- a/runtime/bin/file_system_watcher_linux.cc
+++ b/runtime/bin/file_system_watcher_linux.cc
@@ -80,7 +80,7 @@
     mask |= FileSystemWatcher::kModifyContent;
   }
   if ((e->mask & IN_ATTRIB) != 0) {
-    mask |= FileSystemWatcher::kModefyAttribute;
+    mask |= FileSystemWatcher::kModifyAttribute;
   }
   if ((e->mask & IN_CREATE) != 0) {
     mask |= FileSystemWatcher::kCreate;
diff --git a/runtime/bin/file_system_watcher_macos.cc b/runtime/bin/file_system_watcher_macos.cc
index 770ed06..b73c0d13 100644
--- a/runtime/bin/file_system_watcher_macos.cc
+++ b/runtime/bin/file_system_watcher_macos.cc
@@ -358,7 +358,7 @@
       mask |= kModifyContent;
     }
     if ((flags & kFSEventStreamEventFlagItemXattrMod) != 0) {
-      mask |= kModefyAttribute;
+      mask |= kModifyAttribute;
     }
     if ((flags & kFSEventStreamEventFlagItemCreated) != 0) {
       mask |= kCreate;
diff --git a/sdk/lib/io/file_system_entity.dart b/sdk/lib/io/file_system_entity.dart
index 6bba998..0e12414 100644
--- a/sdk/lib/io/file_system_entity.dart
+++ b/sdk/lib/io/file_system_entity.dart
@@ -596,7 +596,7 @@
   /// the path of [entity], if it has a drive letter (starts with `[a-zA-z]:`),
   /// or `-1` if it has no drive letter.
   static int _windowsDriveLetter(String path) {
-    if (!path.startsWith(':', 1)) return -1;
+    if (path.isEmpty || !path.startsWith(':', 1)) return -1;
     var first = path.codeUnitAt(0) & ~0x20;
     if (first >= 0x41 && first <= 0x5b) return first;
     return -1;
@@ -640,7 +640,8 @@
       if (entityDrive != _windowsDriveLetter(current)) {
         // Need to resolve relative to current directory of the drive.
         // Windows remembers the last CWD of each drive.
-        // We currently don't have that information, so we assume the root of that drive.
+        // We currently don't have that information,
+        // so we assume the root of that drive.
         return '${path[0]}:\\$path';
       }
 
diff --git a/tests/standalone/io/file_test.dart b/tests/standalone/io/file_test.dart
index 38fd51b..d78e512 100644
--- a/tests/standalone/io/file_test.dart
+++ b/tests/standalone/io/file_test.dart
@@ -1615,6 +1615,9 @@
 
     Expect.equals("content", absFile.readAsStringSync());
 
+    // An empty file path should be accepted.
+    File('').absolute;
+
     if (Platform.isWindows &&
         tempDirectory.path.startsWith(RegExp(r"^[a-zA-Z]:"))) {
       var driveRelativeFile = File(absFile.path.substring(2));
diff --git a/tests/standalone/standalone_kernel.status b/tests/standalone/standalone_kernel.status
index 5eca181..7376264 100644
--- a/tests/standalone/standalone_kernel.status
+++ b/tests/standalone/standalone_kernel.status
@@ -8,6 +8,9 @@
 fragmentation_typed_data_test: Pass, Slow # GC heavy
 io/process_sync_test: Pass, Slow # Spawns synchronously subprocesses in sequence.
 
+[ $builder_tag == bytecode_interpreter ]
+io/http_big_header_test: SkipSlow # Timeout
+
 [ $compiler == dartkb ]
 no_lazy_dispatchers_test: SkipByDesign # KBC interpreter doesn't support --no_lazy_dispatchers
 
diff --git a/tests/standalone_2/io/file_test.dart b/tests/standalone_2/io/file_test.dart
index 0d73ef9..37967e9 100644
--- a/tests/standalone_2/io/file_test.dart
+++ b/tests/standalone_2/io/file_test.dart
@@ -1614,6 +1614,9 @@
 
     Expect.equals("content", absFile.readAsStringSync());
 
+    // An empty file path should be accepted.
+    File('').absolute;
+
     if (Platform.isWindows &&
         tempDirectory.path.startsWith(RegExp(r"^[a-zA-Z]:"))) {
       var driveRelativeFile = File(absFile.path.substring(2));
diff --git a/tests/standalone_2/standalone_2_kernel.status b/tests/standalone_2/standalone_2_kernel.status
index 5eca181..7376264 100644
--- a/tests/standalone_2/standalone_2_kernel.status
+++ b/tests/standalone_2/standalone_2_kernel.status
@@ -8,6 +8,9 @@
 fragmentation_typed_data_test: Pass, Slow # GC heavy
 io/process_sync_test: Pass, Slow # Spawns synchronously subprocesses in sequence.
 
+[ $builder_tag == bytecode_interpreter ]
+io/http_big_header_test: SkipSlow # Timeout
+
 [ $compiler == dartkb ]
 no_lazy_dispatchers_test: SkipByDesign # KBC interpreter doesn't support --no_lazy_dispatchers
 
diff --git a/tools/VERSION b/tools/VERSION
index 69a8be3..ba3ae6a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 10
 PATCH 0
-PRERELEASE 90
+PRERELEASE 91
 PRERELEASE_PATCH 0
\ No newline at end of file