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