request: {
"id": String
"method": "server.openUrlRequest"
diff --git a/pkg/analysis_server/doc/implementation/code_completion.md b/pkg/analysis_server/doc/implementation/code_completion.md
new file mode 100644
index 000000000000..cff913533104
--- /dev/null
+++ b/pkg/analysis_server/doc/implementation/code_completion.md
@@ -0,0 +1,53 @@
+# Code completion
+
+This document describes how code completion is implemented and how to fix bugs
+and extend it.
+
+## Basic operation
+
+Code completion begins with the receipt of either a `completion.getSuggestions2`
+request (from the legacy protocol) or a `textDocument/completion` request (from
+LSP). When the request is received the appropriate handler is invoked. The
+handler will compute a list of completion suggestions and will then translate
+the suggestions into the form required by the protocol.
+
+Code completion is supported in `.dart` files as well as in the `pubspec.yaml`,
+`analysis_options.yaml`, and `fix_data.yaml` files.
+
+Dart completion suggestions are computed using the `DartCompletionManager` by
+invoking either the method `computeSuggestions` (for the legacy protocol) or
+`computeFinalizedCandidateSuggestions`. (The legacy protocol will be changed to
+use `computeFinalizedCandidateSuggestions` in the near future.)
+
+The completion manager computes suggestions in two "passes".
+
+- The `InScopeCompletionPass` computes suggestions for every appropriate element
+ whose name is visible in the name scope surrounding the completion location.
+
+- The `NotImportedCompletionPass` computes suggestions for elements that are not
+ yet visible in the name scope surrounding the completion location. This pass
+ is skipped if there isn't time left in the `budget` or if there are already
+ enough suggestions that the not-yet-imported elements wouldn't be sent anyway.
+
+## Maintaining and improving
+
+The easiest way to fix bugs or add support for new features is to start by
+writing a test in the directory `test/services/completion/dart/location`. These
+tests are grouped based on the location in the grammar at which completion is
+being requested.
+
+When you have a test that's failing, add a breakpoint to
+`InScopeCompletionPass.computeSuggestions`. When the debugger stops at the
+breakpoint, hover over `_completionNode` to see what kind of node will be
+visited. Add a breakpoint in the corresponding visit method and resume
+execution. (If the visit method if it doesn't already exist, then add it and
+restart the debugger).
+
+## New language features
+
+If a new language feature is being introduced that adds new syntax, then code
+completion support will need to be updated. If the changes are limited to
+updating an existing node then you should be able to use the method above to
+update the corresponding visit method. If the changes required the addition of
+some new subclasses of `AstNode`, then you'll likely need to add a new visit
+method for the added nodes.
diff --git a/pkg/analysis_server/doc/implementation/overview.md b/pkg/analysis_server/doc/implementation/overview.md
index f3a9dd5b9537..851a901122ce 100644
--- a/pkg/analysis_server/doc/implementation/overview.md
+++ b/pkg/analysis_server/doc/implementation/overview.md
@@ -3,31 +3,39 @@
This section of the documentation discusses how the analysis server works and
how to enhance the various features provided by the server.
+## Design
+
+The design of the analysis server beings with [a description of what the server
+does on startup](startup.md)
+
+## Features
+
To learn how the server implements support for a specific feature, read one of
the following feature-specific documents:
-- Call Hierarchy
-- Closing Labels
-- Code Completion
-- Code Folding
+
+- Call hierarchy
+- Closing labels
+- [Code completion](code_completion.md)
+- Code folding
- documentSymbol
-- Flutter Outline
+- Flutter outline
- Hovers
-- Implemented Markers
+- Implemented markers
- [Navigation](navigation.md)
- Occurrences
-- Organize Imports
+- Organize imports
- Outline
-- Overrides Markers
-- [Quick Assists](quick_assist.md)
-- [Quick Fixes](quick_fix.md)
+- Overrides markers
+- [Quick assists](quick_assist.md)
+- [Quick fixes](quick_fix.md)
- Refactorings - legacy
- Refactorings - self describing
-- Search - Find References
-- Search - Member Declarations
-- Search - Member References
-- Search - Top-level Declarations
+- Search - Find references
+- Search - Member declarations
+- Search - Member references
+- Search - Top-level declarations
- selectionRange
-- Semantic Highlights
+- [Semantic highlights](semantic_highlighting.md)
- signatureHelp
-- Sort Members
-- Type Hierarchy
+- Sort members
+- Type hierarchy
diff --git a/pkg/analysis_server/doc/introduction.md b/pkg/analysis_server/doc/introduction.md
index f01f81621035..9a776cecce03 100644
--- a/pkg/analysis_server/doc/introduction.md
+++ b/pkg/analysis_server/doc/introduction.md
@@ -1,8 +1,12 @@
# Introduction
-The `analysis_server` package implements the analysis server, which supports
-both the IDE experience and several command-line tools. This directory contains
-documentation related to the`analysis_server` package.
+The `analysis_server` package implements the Dart Analysis Server (sometimes
+referred to as DAS). The analysis server supports both the IDE experience and
+command-line tools such as `dart analyze` and `dart fix`.
+
+This directory contains documentation related to the `analysis_server` package.
+
+## Organization
The documentation is divided into the following sections:
@@ -10,5 +14,8 @@ The documentation is divided into the following sections:
implementation of the server. It is intended to help you understand how the
server works and how to enhance the various features provided by the server.
-- [process](process/overview.md), which describes the process that should be
- followed when working on the analysis server code base.
\ No newline at end of file
+- [process](process/overview.md), which describes the processes that should be
+ followed when working on the analysis server code base.
+
+- [tutorial](tutorial/instrumentation.md), which provides end-user information
+ about the analysis server.
diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart
index 09438d53f604..686521972db4 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.38.0';
+const String PROTOCOL_VERSION = '1.39.0';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
@@ -335,6 +335,8 @@ const String SERVER_REQUEST_OPEN_URL_REQUEST = 'server.openUrlRequest';
const String SERVER_REQUEST_OPEN_URL_REQUEST_URL = 'url';
const String SERVER_REQUEST_SET_CLIENT_CAPABILITIES =
'server.setClientCapabilities';
+const String SERVER_REQUEST_SET_CLIENT_CAPABILITIES_LSP_CAPABILITIES =
+ 'lspCapabilities';
const String SERVER_REQUEST_SET_CLIENT_CAPABILITIES_REQUESTS = 'requests';
const String SERVER_REQUEST_SET_CLIENT_CAPABILITIES_SUPPORTS_URIS =
'supportsUris';
diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart
index e131670f77c6..6945ca960e01 100644
--- a/pkg/analysis_server/lib/protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart
@@ -19093,6 +19093,7 @@ enum ServerService {
/// {
/// "requests": List
/// "supportsUris": optional bool
+/// "lspCapabilities": optional object
/// }
///
/// Clients may not extend, implement or mix-in this class.
@@ -19125,7 +19126,21 @@ class ServerSetClientCapabilitiesParams implements RequestParams {
/// capability.
bool? supportsUris;
- ServerSetClientCapabilitiesParams(this.requests, {this.supportsUris});
+ /// LSP capabilities of the client as defined by the Language Server Protocol
+ /// specification.
+ ///
+ /// If custom LSP capabilities are to be used, the setClientCapabilities
+ /// request should be called before any LSP requests are made to the server.
+ ///
+ /// If LSP capabilities are not provided or no setClientCapabilities request
+ /// is made, a very basic set of capabilities will be assumed.
+ Object? lspCapabilities;
+
+ ServerSetClientCapabilitiesParams(
+ this.requests, {
+ this.supportsUris,
+ this.lspCapabilities,
+ });
factory ServerSetClientCapabilitiesParams.fromJson(
JsonDecoder jsonDecoder,
@@ -19152,9 +19167,14 @@ class ServerSetClientCapabilitiesParams implements RequestParams {
json['supportsUris'],
);
}
+ Object? lspCapabilities;
+ if (json.containsKey('lspCapabilities')) {
+ lspCapabilities = json['lspCapabilities'] as Object;
+ }
return ServerSetClientCapabilitiesParams(
requests,
supportsUris: supportsUris,
+ lspCapabilities: lspCapabilities,
);
} else {
throw jsonDecoder.mismatch(
@@ -19187,6 +19207,10 @@ class ServerSetClientCapabilitiesParams implements RequestParams {
if (supportsUris != null) {
result['supportsUris'] = supportsUris;
}
+ var lspCapabilities = this.lspCapabilities;
+ if (lspCapabilities != null) {
+ result['lspCapabilities'] = lspCapabilities;
+ }
return result;
}
@@ -19213,13 +19237,15 @@ class ServerSetClientCapabilitiesParams implements RequestParams {
other.requests,
(String a, String b) => a == b,
) &&
- supportsUris == other.supportsUris;
+ supportsUris == other.supportsUris &&
+ lspCapabilities == other.lspCapabilities;
}
return false;
}
@override
- int get hashCode => Object.hash(Object.hashAll(requests), supportsUris);
+ int get hashCode =>
+ Object.hash(Object.hashAll(requests), supportsUris, lspCapabilities);
}
/// server.setClientCapabilities result
diff --git a/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart b/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart
index 4a991a717fce..063415104101 100644
--- a/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart
+++ b/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart
@@ -5,7 +5,6 @@
import 'package:analysis_server/src/protocol_server.dart' as protocol;
import 'package:analysis_server/src/services/search/search_engine.dart';
import 'package:analyzer/dart/element/element2.dart';
-import 'package:analyzer/src/utilities/extensions/element.dart';
class ImplementedComputer {
final SearchEngine searchEngine;
diff --git a/pkg/analysis_server/lib/src/handler/legacy/server_set_client_capabilities.dart b/pkg/analysis_server/lib/src/handler/legacy/server_set_client_capabilities.dart
index 83895edcced0..471a8a6ddb08 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/server_set_client_capabilities.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/server_set_client_capabilities.dart
@@ -29,6 +29,9 @@ class ServerSetClientCapabilitiesHandler extends LegacyHandler {
} on RequestFailure catch (exception) {
sendResponse(exception.response);
return;
+ } on RequestError catch (error) {
+ sendResponse(Response(request.id, error: error));
+ return;
}
sendResult(ServerSetClientCapabilitiesResult());
}
diff --git a/pkg/analysis_server/lib/src/legacy_analysis_server.dart b/pkg/analysis_server/lib/src/legacy_analysis_server.dart
index a2a397f807f0..475186542005 100644
--- a/pkg/analysis_server/lib/src/legacy_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/legacy_analysis_server.dart
@@ -111,6 +111,7 @@ import 'package:analyzer_plugin/src/utilities/client_uri_converter.dart';
import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
import 'package:analyzer_plugin/src/utilities/navigation/navigation_dart.dart';
import 'package:http/http.dart' as http;
+import 'package:language_server_protocol/json_parsing.dart' as lsp;
import 'package:meta/meta.dart';
import 'package:telemetry/crash_reporting.dart';
import 'package:watcher/watcher.dart';
@@ -292,8 +293,8 @@ class LegacyAnalysisServer extends AnalysisServer {
ServerSetClientCapabilitiesParams _clientCapabilities =
ServerSetClientCapabilitiesParams([]);
- @override
- final editorClientCapabilities = lsp.fixedBasicLspClientCapabilities;
+ /// See [editorClientCapabilities].
+ var _editorClientCapabilities = lsp.fixedBasicLspClientCapabilities;
@override
final lsp.LspClientConfiguration lspClientConfiguration;
@@ -441,8 +442,29 @@ class LegacyAnalysisServer extends AnalysisServer {
} else {
uriConverter = ClientUriConverter.noop(resourceProvider.pathContext);
}
+
+ if (capabilities.lspCapabilities
+ case Map lspCapabilities) {
+ // First validate the capabilities so we can get a better message if it's
+ // invalid.
+ var reporter = lsp.LspJsonReporter();
+ if (!lsp.ClientCapabilities.canParse(lspCapabilities, reporter)) {
+ throw RequestError(
+ RequestErrorCode.INVALID_PARAMETER,
+ "The 'lspCapabilities' parameter was invalid: ${reporter.errors.join(', ')}",
+ );
+ }
+
+ _editorClientCapabilities = lsp.LspClientCapabilities(
+ lsp.ClientCapabilities.fromJson(lspCapabilities.cast()),
+ );
+ }
}
+ @override
+ lsp.LspClientCapabilities get editorClientCapabilities =>
+ _editorClientCapabilities;
+
/// The [Future] that completes when analysis is complete.
Future get onAnalysisComplete {
if (isAnalysisComplete()) {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
index db25375189f4..740f61fa66e2 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
@@ -15,7 +15,6 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
-import 'package:analyzer/src/utilities/extensions/element.dart';
typedef StaticOptions = Either2;
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 11c411a6d52b..478783c3e33d 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -2421,6 +2421,16 @@ LintCode.unnecessary_final_without_type:
status: hasFix
LintCode.unnecessary_getters_setters:
status: hasFix
+LintCode.unnecessary_ignore:
+ status: needsFix
+ notes: |-
+ Remove the ignore comment (or one code in the comment).
+LintCode.unnecessary_ignore_file:
+ status: needsFix
+LintCode.unnecessary_ignore_name:
+ status: needsFix
+LintCode.unnecessary_ignore_name_file:
+ status: needsFix
LintCode.unnecessary_lambdas:
status: hasFix
LintCode.unnecessary_late:
@@ -3751,10 +3761,6 @@ WarningCode.UNNECESSARY_CAST:
status: hasFix
WarningCode.UNNECESSARY_FINAL:
status: hasFix
-WarningCode.UNNECESSARY_IGNORE:
- status: needsFix
- notes: |-
- Remove the ignore comment (or one code in the comment).
WarningCode.UNNECESSARY_NAN_COMPARISON_FALSE:
status: hasFix
notes: |-
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_fragment_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_fragment_parser.dart
index cfc452485788..a15c765da9e4 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_fragment_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_fragment_parser.dart
@@ -11,7 +11,6 @@ import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/utilities/extensions/string.dart';
// Several "report" functions intentionally return a `Null`-typed value.
-// ignore_for_file: prefer_void_to_null
/// A parser for the textual representation of a code fragment.
class CodeFragmentParser {
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index 0b6dc567035c..536b684b3086 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -27,7 +27,6 @@ import 'package:collection/collection.dart';
import 'package:yaml/yaml.dart';
// Several "report" functions intentionally return a `Null`-typed value.
-// ignore_for_file: prefer_void_to_null
/// Information used to report errors when translating a node.
class ErrorContext {
diff --git a/pkg/analysis_server/lib/src/utilities/mocks.dart b/pkg/analysis_server/lib/src/utilities/mocks.dart
index 1a1a5daebfe2..7de4355e2924 100644
--- a/pkg/analysis_server/lib/src/utilities/mocks.dart
+++ b/pkg/analysis_server/lib/src/utilities/mocks.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
+import 'dart:convert';
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/src/channel/channel.dart';
@@ -15,12 +16,21 @@ import 'package:watcher/watcher.dart';
/// A mock [ServerCommunicationChannel] for testing [AnalysisServer].
class MockServerChannel implements ServerCommunicationChannel {
+ /// A controller for the stream of requests and responses from the client to
+ /// the server.
StreamController requestController =
StreamController();
- StreamController responseController =
- StreamController.broadcast();
+
+ /// A controller for the stream of requests and responses from the server to
+ /// the client.
+ StreamController responseController =
+ StreamController.broadcast();
+
+ /// A controller for the stream of notifications from the server to the
+ /// client.
StreamController notificationController =
StreamController.broadcast(sync: true);
+
Completer? errorCompleter;
List responsesReceived = [];
@@ -41,6 +51,11 @@ class MockServerChannel implements ServerCommunicationChannel {
@override
Stream get requests => requestController.stream;
+ /// Return the broadcast stream of server-to-client requests.
+ Stream get serverToClientRequests {
+ return responseController.stream.where((r) => r is Request).cast();
+ }
+
@override
void close() {
_closed = true;
@@ -71,6 +86,7 @@ class MockServerChannel implements ServerCommunicationChannel {
@override
void sendRequest(Request request) {
serverRequestsSent.add(request);
+ responseController.add(request);
}
@override
@@ -89,13 +105,30 @@ class MockServerChannel implements ServerCommunicationChannel {
/// with the [request] has been received.
///
/// The value of the future will be the received response.
- Future simulateRequestFromClient(Request request) {
+ Future simulateRequestFromClient(Request request) async {
if (_closed) {
throw Exception('simulateRequestFromClient after connection closed');
}
+
+ // Round-trip via JSON to ensure all types are fully serialized as they
+ // would be in a real setup.
+ request =
+ Request.fromJson(
+ jsonDecode(jsonEncode(request)) as Map,
+ )!;
+
// Wrap send request in future to simulate WebSocket.
- Future(() => requestController.add(request));
- return waitForResponse(request);
+ unawaited(Future(() => requestController.add(request)));
+ var response = await waitForResponse(request);
+
+ // Round-trip via JSON to ensure all types are fully serialized as they
+ // would be in a real setup.
+ response =
+ Response.fromJson(
+ jsonDecode(jsonEncode(response)) as Map,
+ )!;
+
+ return response;
}
/// Send the given [response] to the server as if it had been sent from the
@@ -117,9 +150,10 @@ class MockServerChannel implements ServerCommunicationChannel {
/// has already been sent to the server.
Future waitForResponse(Request request) {
var id = request.id;
- return responseController.stream.firstWhere(
- (response) => response.id == id,
- );
+ return responseController.stream
+ .where((r) => r is Response)
+ .cast()
+ .firstWhere((response) => response.id == id);
}
}
diff --git a/pkg/analysis_server/test/analysis_server_base.dart b/pkg/analysis_server/test/analysis_server_base.dart
index 88f848197d6c..e035286586ce 100644
--- a/pkg/analysis_server/test/analysis_server_base.dart
+++ b/pkg/analysis_server/test/analysis_server_base.dart
@@ -224,7 +224,9 @@ abstract class ContextResolutionTest with ResourceProviderMixin {
class PubPackageAnalysisServerTest extends ContextResolutionTest
with MockPackagesMixin, ConfigurationFilesMixin, TestMacros {
// TODO(scheglov): Consider turning it back into a getter.
- late String testFilePath = '$testPackageLibPath/test.dart';
+ late String testFilePath = resourceProvider.convertPath(
+ '$testPackageLibPath/test.dart',
+ );
/// Return a list of the experiments that are to be enabled for tests in this
/// class, an empty list if there are no experiments that should be enabled.
diff --git a/pkg/analysis_server/test/domain_server_test.dart b/pkg/analysis_server/test/domain_server_test.dart
index aecd1f39802e..b2698214c8a8 100644
--- a/pkg/analysis_server/test/domain_server_test.dart
+++ b/pkg/analysis_server/test/domain_server_test.dart
@@ -4,6 +4,7 @@
import 'dart:async';
+import 'package:analysis_server/lsp_protocol/protocol.dart' as lsp;
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/protocol/protocol_generated.dart'
@@ -100,6 +101,59 @@ class ServerDomainTest extends PubPackageAnalysisServerTest {
await responseFuture;
}
+ Future test_setClientCapabilities_lspCapabilities() async {
+ // Test an arbitrary set of capabilities.
+ var capabilities = lsp.ClientCapabilities(
+ textDocument: lsp.TextDocumentClientCapabilities(
+ hover: lsp.HoverClientCapabilities(
+ contentFormat: [lsp.MarkupKind.PlainText],
+ ),
+ ),
+ workspace: lsp.WorkspaceClientCapabilities(
+ applyEdit: true,
+ workspaceEdit: lsp.WorkspaceEditClientCapabilities(
+ documentChanges: true,
+ resourceOperations: [lsp.ResourceOperationKind.Create],
+ ),
+ ),
+ );
+
+ var request = ServerSetClientCapabilitiesParams(
+ [],
+ lspCapabilities: capabilities.toJson(),
+ ).toRequest('1', clientUriConverter: server.uriConverter);
+
+ await handleSuccessfulRequest(request);
+ var effectiveCapabilities = server.editorClientCapabilities;
+ expect(
+ effectiveCapabilities.hoverContentFormats,
+ equals([lsp.MarkupKind.PlainText]),
+ );
+ expect(effectiveCapabilities.applyEdit, isTrue);
+ expect(effectiveCapabilities.documentChanges, isTrue);
+ expect(effectiveCapabilities.createResourceOperations, isTrue);
+ }
+
+ Future test_setClientCapabilities_lspCapabilities_invalid() async {
+ var request = ServerSetClientCapabilitiesParams(
+ [],
+ lspCapabilities: {
+ 'textDocument': 1, // Not valid
+ },
+ ).toRequest('1', clientUriConverter: server.uriConverter);
+
+ var response = await handleRequest(request);
+ expect(
+ response,
+ isResponseFailure('1', RequestErrorCode.INVALID_PARAMETER),
+ );
+ expect(
+ response.error!.message,
+ "The 'lspCapabilities' parameter was invalid:"
+ ' textDocument must be of type TextDocumentClientCapabilities',
+ );
+ }
+
Future test_setClientCapabilities_requests() async {
var requestId = -1;
diff --git a/pkg/analysis_server/test/integration/lsp/abstract_lsp_over_legacy.dart b/pkg/analysis_server/test/integration/lsp/abstract_lsp_over_legacy.dart
index b40bfffee1ab..64cce03a22b8 100644
--- a/pkg/analysis_server/test/integration/lsp/abstract_lsp_over_legacy.dart
+++ b/pkg/analysis_server/test/integration/lsp/abstract_lsp_over_legacy.dart
@@ -25,6 +25,10 @@ abstract class AbstractLspOverLegacyTest
),
);
+ @override
+ Stream get requestsFromServer =>
+ throw 'Reverse-requests not currently supported for LSP-over-Legacy integration tests';
+
/// The URI for the macro-generated content for [testFileUri].
Uri get testFileMacroUri =>
Uri.file(testFile).replace(scheme: macroClientUriScheme);
@@ -65,4 +69,9 @@ abstract class AbstractLspOverLegacyTest
return fromJson(lspResponse.result as R);
}
}
+
+ @override
+ void sendResponseToServer(ResponseMessage response) {
+ throw 'Reverse-requests not currently supported for LSP-over-Legacy integration tests';
+ }
}
diff --git a/pkg/analysis_server/test/integration/support/integration_test_methods.dart b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
index bb7730b0fbc3..6c214d41565c 100644
--- a/pkg/analysis_server/test/integration/support/integration_test_methods.dart
+++ b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
@@ -132,13 +132,27 @@ abstract class IntegrationTest {
///
/// LSP notifications are automatically enabled when the client sets this
/// capability.
+ ///
+ /// lspCapabilities: object (optional)
+ ///
+ /// LSP capabilities of the client as defined by the Language Server
+ /// Protocol specification.
+ ///
+ /// If custom LSP capabilities are to be used, the setClientCapabilities
+ /// request should be called before any LSP requests are made to the
+ /// server.
+ ///
+ /// If LSP capabilities are not provided or no setClientCapabilities
+ /// request is made, a very basic set of capabilities will be assumed.
Future sendServerSetClientCapabilities(
List requests, {
bool? supportsUris,
+ Object? lspCapabilities,
}) async {
var params = ServerSetClientCapabilitiesParams(
requests,
supportsUris: supportsUris,
+ lspCapabilities: lspCapabilities,
).toJson(clientUriConverter: uriConverter);
var result = await server.send('server.setClientCapabilities', params);
outOfTestExpect(result, isNull);
@@ -2989,6 +3003,11 @@ abstract class IntegrationTest {
/// Call an LSP handler. Message can be requests or notifications.
///
+ /// This request can be called in either direction, either by the client to
+ /// the server, or by the server to the client. The server will only call the
+ /// client if the client has indicated it supports the associated LSP request
+ /// via `lspCapabilities` in the `setClientCapabilities` request.
+ ///
/// Parameters
///
/// lspMessage: object
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index ff9ae9926dcb..feffff2bedec 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -3487,12 +3487,13 @@ final Matcher isServerOpenUrlRequestResult = isNull;
/// {
/// "requests": List
/// "supportsUris": optional bool
+/// "lspCapabilities": optional object
/// }
final Matcher isServerSetClientCapabilitiesParams = LazyMatcher(
() => MatchesJsonObject(
'server.setClientCapabilities params',
{'requests': isListOf(isString)},
- optionalFields: {'supportsUris': isBool},
+ optionalFields: {'supportsUris': isBool, 'lspCapabilities': isObject},
),
);
diff --git a/pkg/analysis_server/test/lsp/editable_arguments_test.dart b/pkg/analysis_server/test/lsp/editable_arguments_test.dart
index 844f0a8c539e..5aa0c093103d 100644
--- a/pkg/analysis_server/test/lsp/editable_arguments_test.dart
+++ b/pkg/analysis_server/test/lsp/editable_arguments_test.dart
@@ -2,12 +2,9 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:analysis_server/lsp_protocol/protocol.dart';
-import 'package:analyzer/src/test_utilities/test_code_format.dart';
-import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../utils/test_code_extensions.dart';
+import '../shared/shared_editable_arguments_tests.dart';
import 'server_abstract.dart';
void main() {
@@ -17,1265 +14,12 @@ void main() {
}
@reflectiveTest
-class EditableArgumentsTest extends AbstractLspAnalysisServerTest {
- late TestCode code;
-
- /// Initializes the server with [content] and fetches editable arguments.
- Future getEditableArgumentsFor(
- String content, {
- bool open = true,
- }) async {
- code = TestCode.parse('''
-import 'package:flutter/widgets.dart';
-
-$content
-''');
- newFile(mainFilePath, code.code);
- await initialize();
- if (open) {
- await openFile(mainFileUri, code.code);
- }
- await initialAnalysis;
- return await getEditableArguments(mainFileUri, code.position.position);
- }
-
- Matcher hasArg(Matcher matcher) {
- return hasArgs(contains(matcher));
- }
-
- Matcher hasArgNamed(String argumentName) {
- return hasArg(isArg(argumentName));
- }
-
- Matcher hasArgs(Matcher matcher) {
- return isA().having(
- (arguments) => arguments.arguments,
- 'arguments',
- matcher,
- );
- }
-
- Matcher isArg(
- String name, {
- Object? type = anything,
- Object? value = anything,
- Object? displayValue = anything,
- Object? hasArgument = anything,
- Object? isDefault = anything,
- Object? isRequired = anything,
- Object? isNullable = anything,
- Object? isEditable = anything,
- Object? notEditableReason = anything,
- Object? options = anything,
- }) {
- return isA()
- .having((arg) => arg.name, 'name', name)
- .having((arg) => arg.type, 'type', type)
- .having((arg) => arg.value, 'value', value)
- .having((arg) => arg.displayValue, 'displayValue', displayValue)
- .having((arg) => arg.hasArgument, 'hasArgument', hasArgument)
- .having((arg) => arg.isDefault, 'isDefault', isDefault)
- .having((arg) => arg.isRequired, 'isRequired', isRequired)
- .having((arg) => arg.isNullable, 'isNullable', isNullable)
- .having((arg) => arg.isEditable, 'isEditable', isEditable)
- .having(
- (arg) => arg.notEditableReason,
- 'notEditableReason',
- notEditableReason,
- )
- .having((arg) => arg.options, 'options', options)
- // Some extra checks that should be true for all.
- .having(
- (arg) =>
- arg.value == null ||
- arg.value?.toString() != arg.displayValue?.toString(),
- 'different value and displayValues',
- isTrue,
- )
- .having(
- (arg) => (arg.notEditableReason == null) == arg.isEditable,
- 'notEditableReason must be supplied if isEditable=false',
- isTrue,
- )
- .having(
- (arg) => arg.value == null || arg.isEditable,
- 'isEditable must be true if there is a value',
- isTrue,
- )
- .having(
- (arg) =>
- arg.type == 'enum'
- ? (arg.options?.isNotEmpty ?? false)
- : arg.options == null,
- 'enum types must have options / non-enums must not have options',
- isTrue,
- );
- }
-
+class EditableArgumentsTest extends SharedAbstractLspAnalysisServerTest
+ with SharedEditableArgumentsTests {
@override
void setUp() {
super.setUp();
writeTestPackageConfig(flutter: true);
}
-
- test_hasArgument() async {
- failTestOnErrorDiagnostic = false;
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget(
- int? aPositionalSupplied,
- int? aPositionalNotSupplied, {
- int? aNamedSupplied,
- int? aNamedNotSupplied,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(1, aNamedSupplied: 1);
-}
-''');
- expect(
- result,
- hasArgs(
- unorderedEquals([
- isArg('aPositionalSupplied', hasArgument: true),
- isArg('aPositionalNotSupplied', hasArgument: false),
- isArg('aNamedSupplied', hasArgument: true),
- isArg('aNamedNotSupplied', hasArgument: false),
- ]),
- ),
- );
- }
-
- test_isEditable_false_positional_optional() async {
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget([int? a, int? b, int? c]);
-
- @override
- Widget build(BuildContext context) => MyW^idget(1);
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('a', isEditable: true),
- isArg('b', isEditable: true),
- isArg(
- 'c',
- // c is not editable because it is not guaranteed that we can insert
- // a default value for b (it could be a private value or require
- // imports).
- isEditable: false,
- notEditableReason:
- "A value for the 3rd parameter can't be added until a value "
- 'for all preceding positional parameters have been added.',
- ),
- ]),
- ),
- );
- }
-
- test_isEditable_false_positional_required1() async {
- failTestOnErrorDiagnostic = false;
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget(int a, int b);
-
- @override
- Widget build(BuildContext context) => MyW^idget();
-}
-''');
- expect(
- result,
- hasArg(
- // b is not editable because there are missing required previous
- // arguments (a).
- isArg(
- 'b',
- isEditable: false,
- notEditableReason:
- "A value for the 2nd parameter can't be added until a value "
- 'for all preceding positional parameters have been added.',
- ),
- ),
- );
- }
-
- test_isEditable_false_positional_required2() async {
- failTestOnErrorDiagnostic = false;
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget(int a, int b, int c);
-
- @override
- Widget build(BuildContext context) => MyW^idget(1);
-}
-''');
- expect(
- result,
- hasArg(
- // c is not editable because there are missing required previous
- // arguments (b).
- isArg(
- 'c',
- isEditable: false,
- notEditableReason:
- "A value for the 3rd parameter can't be added until a value "
- 'for all preceding positional parameters have been added.',
- ),
- ),
- );
- }
-
- test_isEditable_false_string_adjacent() async {
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget(String s);
-
- @override
- Widget build(BuildContext context) => MyW^idget('a' 'b');
-}
-''');
- expect(
- result,
- hasArg(
- isArg(
- 's',
- type: 'string',
- value: isNull,
- displayValue: 'ab',
- isDefault: false,
- isEditable: false,
- notEditableReason: "Adjacent strings can't be edited",
- ),
- ),
- );
- }
-
- test_isEditable_false_string_interpolated() async {
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget(String s);
-
- @override
- Widget build(BuildContext context) => MyW^idget('${context.runtimeType}');
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg(
- 's',
- type: 'string',
- value: isNull,
- displayValue: r"'${context.runtimeType}'",
- isEditable: false,
- notEditableReason: "Interpolated strings can't be edited",
- ),
- ]),
- ),
- );
- }
-
- test_isEditable_false_string_withNewlines() async {
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget(String sEscaped, String sLiteral);
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- 'a\nb',
- """
-a
-b
-""",
- );
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg(
- 'sEscaped',
- type: 'string',
- value: isNull,
- displayValue: 'a\nb',
- isEditable: false,
- notEditableReason: "Strings containing newlines can't be edited",
- ),
- isArg(
- 'sLiteral',
- type: 'string',
- value: isNull,
- displayValue: 'a\nb\n',
- isEditable: false,
- notEditableReason: "Strings containing newlines can't be edited",
- ),
- ]),
- ),
- );
- }
-
- test_isEditable_true_named() async {
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget({int? a, int? b, int? c});
-
- @override
- Widget build(BuildContext context) => MyW^idget(a: 1);
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('a', isEditable: true),
- isArg('b', isEditable: true),
- isArg('c', isEditable: true),
- ]),
- ),
- );
- }
-
- test_isEditable_true_positional_required() async {
- failTestOnErrorDiagnostic = false;
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget(int a, int b);
-
- @override
- Widget build(BuildContext context) => MyW^idget(1);
-}
-''');
- expect(
- result,
- hasArg(
- isArg(
- 'b',
- // b is editable because it's the next argument and we don't need
- // to add anything additional.
- isEditable: true,
- ),
- ),
- );
- }
-
- test_isEditable_true_string_dollar_escaped() async {
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget(String s);
-
- @override
- Widget build(BuildContext context) => MyW^idget('\${1}');
-}
-''');
- expect(
- result,
- hasArg(
- isArg(
- 's',
- type: 'string',
- value: r'${1}',
- displayValue: isNull,
- isEditable: true,
- ),
- ),
- );
- }
-
- test_isEditable_true_string_dollar_raw() async {
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget(String s);
-
- @override
- Widget build(BuildContext context) => MyW^idget(r'${1}');
-}
-''');
- expect(
- result,
- hasArg(
- isArg(
- 's',
- type: 'string',
- value: r'${1}',
- displayValue: isNull,
- isEditable: true,
- ),
- ),
- );
- }
-
- test_isEditable_true_string_tripleQuoted_withoutNewlines() async {
- var result = await getEditableArgumentsFor(r'''
-class MyWidget extends StatelessWidget {
- const MyWidget(String s);
-
- @override
- Widget build(BuildContext context) => MyW^idget("""string_value""");
-}
-''');
- expect(
- result,
- hasArg(
- isArg(
- 's',
- type: 'string',
- value: 'string_value',
- displayValue: isNull,
- isEditable: true,
- ),
- ),
- );
- }
-
- test_isNullable() async {
- failTestOnErrorDiagnostic = false;
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget(
- int aPositional,
- int? aPositionalNullable, {
- int? aNamed,
- required int aRequiredNamed,
- required int? aRequiredNamedNullable
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget();
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('aPositional', isNullable: false),
- isArg('aPositionalNullable', isNullable: true),
- isArg('aNamed', isNullable: true),
- isArg('aRequiredNamed', isNullable: false),
- isArg('aRequiredNamedNullable', isNullable: true),
- ]),
- ),
- );
- }
-
- test_isRequired() async {
- failTestOnErrorDiagnostic = false;
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget(
- int aPositional,
- int? aPositionalNullable, {
- int? aNamed,
- required int aRequiredNamed,
- required int? aRequiredNamedNullable
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget();
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('aPositional', isRequired: true),
- isArg('aPositionalNullable', isRequired: true),
- isArg('aNamed', isRequired: false),
- isArg('aRequiredNamed', isRequired: true),
- isArg('aRequiredNamedNullable', isRequired: true),
- ]),
- ),
- );
- }
-
- test_location_bad_extensionMethod_noWidgetFactory() async {
- var result = await getEditableArgumentsFor('''
-extension on MyWidget {
- Widget padded(String a1) => this;
-}
-
-class MyWidget extends StatelessWidget {
- const MyWidget();
- const MyWidget.foo(String a1);
-
- @override
- Widget build(BuildContext context) => this.pad^ded('value1');
-}
-''');
- expect(result, isNull);
- }
-
- test_location_bad_functionInvocation() async {
- var result = await getEditableArgumentsFor('''
-MyWidget create(String a1) => throw '';
-
-class MyWidget extends StatelessWidget {
- @override
- Widget build(BuildContext context) => crea^te('value1');
-}
-''');
- expect(result, isNull);
- }
-
- test_location_bad_methodInvocation() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- @widgetFactory
- MyWidget create(String a1) => throw '';
-
- @override
- Widget build(BuildContext context) => crea^te('value1');
-}
-''');
- expect(result, isNull);
- }
-
- test_location_bad_unnamedConstructor_notWidget() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget {
- const MyWidget(String a1);
-
- @override
- MyWidget build(BuildContext context) => MyW^idget('value1');
-}
-''');
- expect(result, isNull);
- }
-
- test_location_good_argumentList_argumentName() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({required String a1 });
-
- @override
- Widget build(BuildContext context) => MyWidget(a^1: 'value1');
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_argumentList_literalValue() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({required String a1 });
-
- @override
- Widget build(BuildContext context) => MyWidget(a1: 'val^ue1');
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_argumentList_nestedInvocation() async {
- var result = await getEditableArgumentsFor('''
-String getString() => '';
-
-class MyWidget extends StatelessWidget {
- const MyWidget(String a1);
-
- @override
- Widget build(BuildContext context) => MyWidget(getS^tring());
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_argumentList_nestedInvocation_arguments() async {
- var result = await getEditableArgumentsFor('''
-String getString(String s) => s;
-
-class MyWidget extends StatelessWidget {
- const MyWidget(String a1);
-
- @override
- Widget build(BuildContext context) => MyWidget(getString('valu^e1'));
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_argumentList_parens_afterOpen() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({required String a1 });
-
- @override
- Widget build(BuildContext context) => MyWidget(^a1: 'value1');
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_argumentList_parens_beforeClose() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({required String a1 });
-
- @override
- Widget build(BuildContext context) => MyWidget(a1: 'value1'^);
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_argumentList_parens_beforeOpen() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({required String a1 });
-
- @override
- Widget build(BuildContext context) => MyWidget^(a1: 'value1');
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_extensionMethod_constructorTarget() async {
- var result = await getEditableArgumentsFor('''
-extension on MyWidget {
- @widgetFactory
- Widget padded(String a1) => this;
-}
-
-class MyWidget extends StatelessWidget {
- const MyWidget();
- const MyWidget.foo(String a1);
-
- @override
- Widget build(BuildContext context) => MyWidget().pad^ded('value1');
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_extensionMethod_thisTarget() async {
- var result = await getEditableArgumentsFor('''
-extension on MyWidget {
- @widgetFactory
- Widget padded(String a1) => this;
-}
-
-class MyWidget extends StatelessWidget {
- const MyWidget.foo(String a1);
-
- @override
- Widget build(BuildContext context) => this.pad^ded('value1');
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_extensionMethod_variableTarget() async {
- var result = await getEditableArgumentsFor('''
-extension on MyWidget {
- @widgetFactory
- Widget padded(String a1) => this;
-}
-
-class MyWidget extends StatelessWidget {
- const MyWidget();
- const MyWidget.foo(String a1);
-
- @override
- Widget build(BuildContext context) {
- MyWidget? foo;
- return foo!.pad^ded('value1');
- }
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_namedConstructor_className() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget.foo(String a1);
-
- @override
- Widget build(BuildContext context) => MyW^idget.foo('value1');
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_namedConstructor_constructorName() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget.foo(String a1);
-
- @override
- Widget build(BuildContext context) => MyWidget.f^oo('value1');
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- test_location_good_unnamedConstructor() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget(String a1);
-
- @override
- Widget build(BuildContext context) => MyW^idget('value1');
-}
-''');
- expect(result, hasArgNamed('a1'));
- }
-
- /// Arguments should be returned in the order of the parameters in the source
- /// code. This keeps things consistent across different instances of the same
- /// Widget class and prevents the order from changing as a user adds/removes
- /// arguments.
- ///
- /// If an editor wants to sort provided arguments first (and keep these stable
- /// across add/removes) it could still do so client-side, whereas if server
- /// orders them that way, the opposite (using source-order) is not possible.
- test_order() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({
- int c1 = 0,
- int c2 = 0,
- int a1 = 0,
- int a2 = 0,
- int b1 = 0,
- int b2 = 0,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(b1: 1, a1: 1, c1: 1);
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('c1'),
- isArg('c2'),
- isArg('a1'),
- isArg('a2'),
- isArg('b1'),
- isArg('b2'),
- ]),
- ),
- );
- }
-
- test_textDocument_closedFile() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget(String a1);
-
- @override
- Widget build(BuildContext context) => MyW^idget('value1');
-}
-''');
-
- // Verify initial content of 1.
- expect(
- result!.textDocument,
- isA()
- .having((td) => td.uri, 'uri', mainFileUri)
- .having((td) => td.version, 'version', 1),
- );
-
- // Close the file.
- await closeFile(mainFileUri);
-
- // Verify new results have null version.
- result = await getEditableArguments(mainFileUri, code.position.position);
- expect(
- result!.textDocument,
- isA()
- .having((td) => td.uri, 'uri', mainFileUri)
- .having((td) => td.version, 'version', isNull),
- );
- }
-
- test_textDocument_unopenedFile() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget(String a1);
-
- @override
- Widget build(BuildContext context) => MyW^idget('value1');
-}
-''', open: false);
-
- // Verify null version for unopened file.
- expect(
- result!.textDocument,
- isA()
- .having((td) => td.uri, 'uri', mainFileUri)
- .having((td) => td.version, 'version', null),
- );
- }
-
- test_textDocument_versions() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget(String a1);
-
- @override
- Widget build(BuildContext context) => MyW^idget('value1');
-}
-''');
-
- // Verify initial content of 1.
- expect(
- result!.textDocument,
- isA()
- .having((td) => td.uri, 'uri', mainFileUri)
- .having((td) => td.version, 'version', 1),
- );
-
- // Update the content to v5.
- await replaceFile(5, mainFileUri, '${code.code}\n// extra comment');
-
- // Verify new results have version 5.
- result = await getEditableArguments(mainFileUri, code.position.position);
- expect(
- result!.textDocument,
- isA()
- .having((td) => td.uri, 'uri', mainFileUri)
- .having((td) => td.version, 'version', 5),
- );
- }
-
- test_type_bool() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({
- bool supplied = true,
- bool suppliedAsDefault = true,
- bool notSupplied = true,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- supplied: false,
- suppliedAsDefault: true,
- );
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('supplied', type: 'bool', value: false, isDefault: false),
- isArg(
- 'suppliedAsDefault',
- type: 'bool',
- value: true,
- isDefault: true,
- ),
- isArg('notSupplied', type: 'bool', value: true, isDefault: true),
- ]),
- ),
- );
- }
-
- test_type_bool_nonLiterals() async {
- var result = await getEditableArgumentsFor('''
-var myVar = true;
-const myConst = true;
-class MyWidget extends StatelessWidget {
- const MyWidget({
- bool? aVar,
- bool? aConst,
- bool? aExpr,
- bool? aConstExpr,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- aVar: myVar,
- aConst: myConst,
- aExpr: DateTime.now().isBefore(DateTime.now()),
- aConstExpr: 1 == 2,
- );
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('aVar', type: 'bool', value: isNull, displayValue: 'myVar'),
- isArg('aConst', type: 'bool', value: true, displayValue: 'myConst'),
- isArg(
- 'aExpr',
- type: 'bool',
- value: isNull,
- displayValue: 'DateTime.now().isBefore(DateTime.now())',
- ),
- isArg(
- 'aConstExpr',
- type: 'bool',
- value: false,
- displayValue: '1 == 2',
- ),
- ]),
- ),
- );
- }
-
- test_type_double() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({
- double supplied = 1.0,
- double suppliedAsDefault = 1.0,
- double notSupplied = 1.0,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- supplied: 2.0,
- suppliedAsDefault: 1.0,
- );
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('supplied', type: 'double', value: 2.0, isDefault: false),
- isArg(
- 'suppliedAsDefault',
- type: 'double',
- value: 1.0,
- isDefault: true,
- ),
- isArg('notSupplied', type: 'double', value: 1.0, isDefault: true),
- ]),
- ),
- );
- }
-
- test_type_double_intLiterals() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({
- double supplied = 1,
- double suppliedAsDefault = 1,
- double notSupplied = 1,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- supplied: 2,
- suppliedAsDefault: 1,
- );
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('supplied', type: 'double', value: 2, isDefault: false),
- isArg('suppliedAsDefault', type: 'double', value: 1, isDefault: true),
- isArg('notSupplied', type: 'double', value: 1, isDefault: true),
- ]),
- ),
- );
- }
-
- test_type_double_nonLiterals() async {
- var result = await getEditableArgumentsFor('''
-var myVar = 1.0;
-const myConst = 1.0;
-class MyWidget extends StatelessWidget {
- const MyWidget({
- double? aVar,
- double? aConst,
- double? aExpr,
- double? aConstExpr,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- aVar: myVar,
- aConst: myConst,
- aExpr: DateTime.now().millisecondsSinceEpoch.toDouble(),
- aConstExpr: 1.0 + myConst,
- );
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('aVar', type: 'double', value: isNull, displayValue: 'myVar'),
- isArg('aConst', type: 'double', value: 1.0, displayValue: 'myConst'),
- isArg(
- 'aExpr',
- type: 'double',
- value: isNull,
- displayValue: 'DateTime.now().millisecondsSinceEpoch.toDouble()',
- ),
- isArg(
- 'aConstExpr',
- type: 'double',
- value: 2.0,
- displayValue: '1.0 + myConst',
- ),
- ]),
- ),
- );
- }
-
- test_type_enum() async {
- var result = await getEditableArgumentsFor('''
-enum E { one, two }
-class MyWidget extends StatelessWidget {
- const MyWidget({
- E supplied = E.one,
- E suppliedAsDefault = E.one,
- E notSupplied = E.one,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- supplied: E.two,
- suppliedAsDefault: E.one,
- );
-}
-''');
-
- var optionsMatcher = equals(['E.one', 'E.two']);
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg(
- 'supplied',
- type: 'enum',
- value: 'E.two',
- isDefault: false,
- options: optionsMatcher,
- ),
- isArg(
- 'suppliedAsDefault',
- type: 'enum',
- value: 'E.one',
- isDefault: true,
- options: optionsMatcher,
- ),
- isArg(
- 'notSupplied',
- type: 'enum',
- value: 'E.one',
- isDefault: true,
- options: optionsMatcher,
- ),
- ]),
- ),
- );
- }
-
- test_type_enum_nonLiterals() async {
- var result = await getEditableArgumentsFor('''
-enum E { one, two }
-var myVar = E.one;
-const myConst = E.one;
-class MyWidget extends StatelessWidget {
- const MyWidget({
- E? aVar,
- E? aConst,
- E? aExpr,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- aVar: myVar,
- aConst: myConst,
- aExpr: E.values.first,
- );
-}
-''');
-
- var optionsMatcher = equals(['E.one', 'E.two']);
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg(
- 'aVar',
- type: 'enum',
- value: isNull,
- displayValue: 'myVar',
- options: optionsMatcher,
- ),
- isArg(
- 'aConst',
- type: 'enum',
- value: 'E.one',
- displayValue: 'myConst',
- options: optionsMatcher,
- ),
- isArg(
- 'aExpr',
- type: 'enum',
- value: isNull,
- displayValue: 'E.values.first',
- options: optionsMatcher,
- ),
- ]),
- ),
- );
- }
-
- test_type_int() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({
- int supplied = 1,
- int suppliedAsDefault = 1,
- int notSupplied = 1,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- supplied: 2,
- suppliedAsDefault: 1,
- );
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('supplied', type: 'int', value: 2, isDefault: false),
- isArg('suppliedAsDefault', type: 'int', value: 1, isDefault: true),
- isArg('notSupplied', type: 'int', value: 1, isDefault: true),
- ]),
- ),
- );
- }
-
- test_type_int_nonLiterals() async {
- var result = await getEditableArgumentsFor('''
-var myVar = 1;
-const myConst = 1;
-class MyWidget extends StatelessWidget {
- const MyWidget({
- int? aVar,
- int? aConst,
- int? aExpr,
- int? aConstExpr,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- aVar: myVar,
- aConst: myConst,
- aExpr: DateTime.now().millisecondsSinceEpoch,
- aConstExpr: 1 + myConst,
- );
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('aVar', type: 'int', value: isNull, displayValue: 'myVar'),
- isArg('aConst', type: 'int', value: 1, displayValue: 'myConst'),
- isArg(
- 'aExpr',
- type: 'int',
- value: isNull,
- displayValue: 'DateTime.now().millisecondsSinceEpoch',
- ),
- isArg(
- 'aConstExpr',
- type: 'int',
- value: 2,
- displayValue: '1 + myConst',
- ),
- ]),
- ),
- );
- }
-
- test_type_string() async {
- var result = await getEditableArgumentsFor('''
-class MyWidget extends StatelessWidget {
- const MyWidget({
- String supplied = 'a',
- String suppliedAsDefault = 'a',
- String notSupplied = 'a',
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- supplied: 'b',
- suppliedAsDefault: 'a',
- );
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('supplied', type: 'string', value: 'b', isDefault: false),
- isArg(
- 'suppliedAsDefault',
- type: 'string',
- value: 'a',
- isDefault: true,
- ),
- isArg('notSupplied', type: 'string', value: 'a', isDefault: true),
- ]),
- ),
- );
- }
-
- test_type_string_nonLiterals() async {
- var result = await getEditableArgumentsFor('''
-var myVar = 'a';
-const myConst = 'a';
-class MyWidget extends StatelessWidget {
- const MyWidget({
- String? aVar,
- String? aConst,
- String? aExpr,
- String? aConstExpr,
- });
-
- @override
- Widget build(BuildContext context) => MyW^idget(
- aVar: myVar,
- aConst: myConst,
- aExpr: DateTime.now().toString(),
- aConstExpr: 'a' + 'b',
- );
-}
-''');
- expect(
- result,
- hasArgs(
- orderedEquals([
- isArg('aVar', type: 'string', value: isNull, displayValue: 'myVar'),
- isArg('aConst', type: 'string', value: 'a', displayValue: 'myConst'),
- isArg(
- 'aExpr',
- type: 'string',
- value: isNull,
- displayValue: 'DateTime.now().toString()',
- ),
- isArg(
- 'aConstExpr',
- type: 'string',
- value: 'ab',
- displayValue: "'a' + 'b'",
- ),
- ]),
- ),
- );
- }
}
diff --git a/pkg/analysis_server/test/lsp/flutter_outline_test.dart b/pkg/analysis_server/test/lsp/flutter_outline_test.dart
index 105c18ee2ad5..3e076a06ff07 100644
--- a/pkg/analysis_server/test/lsp/flutter_outline_test.dart
+++ b/pkg/analysis_server/test/lsp/flutter_outline_test.dart
@@ -30,7 +30,6 @@ class FlutterOutlineNonFlutterProjectTest
// Wait up to 1sec to ensure no error/log notifications were sent back.
var didTimeout = false;
var outlineNotification = waitForFlutterOutline(mainFileUri)
- // ignore: unnecessary_cast
.then((outline) => outline as FlutterOutline?)
.timeout(
const Duration(seconds: 1),
diff --git a/pkg/analysis_server/test/lsp/request_helpers_mixin.dart b/pkg/analysis_server/test/lsp/request_helpers_mixin.dart
index 093736c750f7..442816e9029c 100644
--- a/pkg/analysis_server/test/lsp/request_helpers_mixin.dart
+++ b/pkg/analysis_server/test/lsp/request_helpers_mixin.dart
@@ -5,6 +5,7 @@
import 'dart:async';
import 'package:analysis_server/lsp_protocol/protocol.dart';
+import 'package:analysis_server/src/lsp/client_capabilities.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
@@ -14,7 +15,7 @@ import 'package:collection/collection.dart';
import 'package:language_server_protocol/json_parsing.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart' as test show expect;
-import 'package:test/test.dart' hide expect;
+import 'package:test/test.dart';
import 'change_verifier.dart';
@@ -169,6 +170,8 @@ mixin LspRequestHelpersMixin {
Stream get notificationsFromServer;
+ Stream get requestsFromServer;
+
Future?> callHierarchyIncoming(
CallHierarchyItem item,
) {
@@ -225,6 +228,29 @@ mixin LspRequestHelpersMixin {
void expect(Object? actual, Matcher matcher, {String? reason}) =>
test.expect(actual, matcher, reason: reason);
+ /// Expects a [method] request from the server after executing [f].
+ Future expectRequest(
+ Method method,
+ FutureOr Function() f, {
+ Duration timeout = const Duration(seconds: 5),
+ }) async {
+ var firstRequest = requestsFromServer.firstWhere((n) => n.method == method);
+ await f();
+
+ var requestFromServer = await firstRequest.timeout(
+ timeout,
+ onTimeout:
+ () =>
+ throw TimeoutException(
+ 'Did not receive the expected $method request from the server in the timeout period',
+ timeout,
+ ),
+ );
+
+ expect(requestFromServer, isNotNull);
+ return requestFromServer;
+ }
+
Future expectSuccessfulResponseTo(
RequestMessage request,
T Function(R) fromJson,
@@ -777,6 +803,71 @@ mixin LspRequestHelpersMixin {
);
}
+ /// Executes [f] then waits for a request of type [method] from the server which
+ /// is passed to [handler] to process, then waits for (and returns) the
+ /// response to the original request.
+ ///
+ /// This is used for testing things like code actions, where the client initiates
+ /// a request but the server does not respond to it until it's sent its own
+ /// request to the client and it received a response.
+ ///
+ /// Client Server
+ /// 1. |- Req: textDocument/codeAction ->
+ /// 1. <- Resp: textDocument/codeAction -|
+ ///
+ /// 2. |- Req: workspace/executeCommand ->
+ /// 3. <- Req: textDocument/applyEdits -|
+ /// 3. |- Resp: textDocument/applyEdits ->
+ /// 2. <- Resp: workspace/executeCommand -|
+ ///
+ /// Request 2 from the client is not responded to until the server has its own
+ /// response to the request it sends (3).
+ Future handleExpectedRequest(
+ Method method,
+ R Function(Map) fromJson,
+ Future Function() f, {
+ required FutureOr Function(R) handler,
+ Duration timeout = const Duration(seconds: 5),
+ }) async {
+ late Future outboundRequest;
+ Object? outboundRequestError;
+
+ // Run [f] and wait for the incoming request from the server.
+ var incomingRequest = await expectRequest(method, () {
+ // Don't return/await the response yet, as this may not complete until
+ // after we have handled the request that comes from the server.
+ outboundRequest = f();
+
+ // Because we don't await this future until "later", if it throws the
+ // error is treated as unhandled and will fail the test even if expected.
+ // Instead, capture the error and suppress it. But if we time out (in
+ // which case we will never return outboundRequest), then we'll raise this
+ // error.
+ outboundRequest.then(
+ (_) {},
+ onError: (e) {
+ outboundRequestError = e;
+ return null;
+ },
+ );
+ }, timeout: timeout).catchError((Object timeoutException) {
+ // We timed out waiting for the request from the server. Probably this is
+ // because our outbound request for some reason, so if we have an error
+ // for that, then throw it. Otherwise, propogate the timeout.
+ throw outboundRequestError ?? timeoutException;
+ }, test: (e) => e is TimeoutException);
+
+ // Handle the request from the server and send the response back.
+ var clientsResponse = await handler(
+ fromJson(incomingRequest.params as Map),
+ );
+ respondTo(incomingRequest, clientsResponse);
+
+ // Return a future that completes when the response to the original request
+ // (from [f]) returns.
+ return outboundRequest;
+ }
+
RequestMessage makeRequest(Method method, ToJsonable? params) {
var id = Either2.t1(_id++);
return RequestMessage(
@@ -851,6 +942,22 @@ mixin LspRequestHelpersMixin {
return expectSuccessfulResponseTo(request, CompletionItem.fromJson);
}
+ /// Sends [responseParams] to the server as a successful response to
+ /// a server-initiated [request].
+ void respondTo(RequestMessage request, T responseParams) {
+ sendResponseToServer(
+ ResponseMessage(
+ id: request.id,
+ result: responseParams,
+ jsonrpc: jsonRpcVersion,
+ ),
+ );
+ }
+
+ /// Sends a ResponseMessage to the server, completing a reverse
+ /// (server-to-client) request.
+ void sendResponseToServer(ResponseMessage response);
+
Future?> typeHierarchySubtypes(
TypeHierarchyItem item,
) {
@@ -1079,15 +1186,64 @@ mixin LspRequestHelpersMixin {
/// Extends [LspEditHelpersMixin] with methods for accessing file state and
/// information about the project to build paths.
mixin LspVerifyEditHelpersMixin on LspEditHelpersMixin {
+ LspClientCapabilities get editorClientCapabilities;
+
path.Context get pathContext;
String get projectFolderPath;
ClientUriConverter get uriConverter;
+ /// Executes a function which is expected to call back to the client to apply
+ /// a [WorkspaceEdit].
+ ///
+ /// Returns a [LspChangeVerifier] that can be used to verify changes.
+ Future executeForEdits(
+ Future Function() function, {
+ ApplyWorkspaceEditResult? applyEditResult,
+ }) async {
+ ApplyWorkspaceEditParams? editParams;
+
+ var commandResponse = await handleExpectedRequest<
+ Object?,
+ ApplyWorkspaceEditParams,
+ ApplyWorkspaceEditResult
+ >(
+ Method.workspace_applyEdit,
+ ApplyWorkspaceEditParams.fromJson,
+ function,
+ handler: (edit) {
+ // When the server sends the edit back, just keep a copy and say we
+ // applied successfully (it'll be verified by the caller).
+ editParams = edit;
+ return applyEditResult ?? ApplyWorkspaceEditResult(applied: true);
+ },
+ );
+ // Successful edits return an empty success() response.
+ expect(commandResponse, isNull);
+
+ // Ensure the edit came back, and using the expected change type.
+ expect(editParams, isNotNull);
+ var edit = editParams!.edit;
+
+ var expectDocumentChanges = editorClientCapabilities.documentChanges;
+ expect(edit.documentChanges, expectDocumentChanges ? isNotNull : isNull);
+ expect(edit.changes, expectDocumentChanges ? isNull : isNotNull);
+
+ return LspChangeVerifier(this, edit);
+ }
+
/// A function to get the current contents of a file to apply edits.
String? getCurrentFileContent(Uri uri);
+ Future handleExpectedRequest(
+ Method method,
+ R Function(Map) fromJson,
+ Future Function() f, {
+ required FutureOr Function(R) handler,
+ Duration timeout = const Duration(seconds: 5),
+ });
+
/// Formats a path relative to the project root always using forward slashes.
///
/// This is used in the text format for comparing edits.
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index ca6b503458a4..1b67adaa77e6 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -33,6 +33,7 @@ import 'package:unified_analytics/unified_analytics.dart';
import '../mocks.dart';
import '../mocks_lsp.dart';
+import '../shared/shared_test_interface.dart';
import '../support/configuration_files.dart';
import '../test_macros.dart';
import 'change_verifier.dart';
@@ -68,6 +69,10 @@ abstract class AbstractLspAnalysisServerTest
DartFixPromptManager? get dartFixPromptManager => null;
+ @override
+ LspClientCapabilities get editorClientCapabilities =>
+ server.editorClientCapabilities!;
+
String get mainFileAugmentationPath => fromUri(mainFileAugmentationUri);
/// The path that is not in [projectFolderPath], contains external packages.
@@ -143,45 +148,6 @@ abstract class AbstractLspAnalysisServerTest
);
}
- /// Executes a function which is expected to call back to the client to apply
- /// a [WorkspaceEdit].
- ///
- /// Returns a [LspChangeVerifier] that can be used to verify changes.
- Future executeForEdits(
- Future Function() function,
- ) async {
- ApplyWorkspaceEditParams? editParams;
-
- var commandResponse = await handleExpectedRequest<
- Object?,
- ApplyWorkspaceEditParams,
- ApplyWorkspaceEditResult
- >(
- Method.workspace_applyEdit,
- ApplyWorkspaceEditParams.fromJson,
- function,
- handler: (edit) {
- // When the server sends the edit back, just keep a copy and say we
- // applied successfully (it'll be verified by the caller).
- editParams = edit;
- return ApplyWorkspaceEditResult(applied: true);
- },
- );
- // Successful edits return an empty success() response.
- expect(commandResponse, isNull);
-
- // Ensure the edit came back, and using the expected change type.
- expect(editParams, isNotNull);
- var edit = editParams!.edit;
-
- var expectDocumentChanges =
- workspaceCapabilities.workspaceEdit?.documentChanges ?? false;
- expect(edit.documentChanges, expectDocumentChanges ? isNotNull : isNull);
- expect(edit.changes, expectDocumentChanges ? isNull : isNotNull);
-
- return LspChangeVerifier(this, edit);
- }
-
void expectContextBuilds() => expect(
server.contextBuilds - _previousContextBuilds,
greaterThan(0),
@@ -283,6 +249,10 @@ abstract class AbstractLspAnalysisServerTest
_previousContextBuilds = server.contextBuilds;
}
+ Future sendLspRequestToClient(Method method, Object params) {
+ return server.sendRequest(method, params);
+ }
+
@override
Future sendNotificationToServer(
NotificationMessage notification,
@@ -917,9 +887,19 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
/// server.
bool failTestOnErrorDiagnostic = true;
+ /// A completer for [initialAnalysis].
+ final Completer _initialAnalysisCompleter = Completer();
+
+ /// A completer for [currentAnalysis].
+ Completer _currentAnalysisCompleter = Completer()..complete();
+
/// [analysisOptionsPath] as a 'file:///' [Uri].
Uri get analysisOptionsUri => pathContext.toUri(analysisOptionsPath);
+ /// A [Future] that completes when the current analysis completes (or is
+ /// already completed if no analysis is in progress).
+ Future get currentAnalysis => _currentAnalysisCompleter.future;
+
/// A stream of [NotificationMessage]s from the server that may be errors.
Stream get errorNotificationsFromServer {
return notificationsFromServer.where(_isErrorNotification);
@@ -930,8 +910,7 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
serverCapabilities.experimental as Map? ?? {};
/// A [Future] that completes with the first analysis after initialization.
- Future get initialAnalysis =>
- initialized ? Future.value() : waitForAnalysisComplete();
+ Future get initialAnalysis => _initialAnalysisCompleter.future;
bool get initialized => _clientCapabilities != null;
@@ -988,6 +967,7 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
Uri get pubspecFileUri => pathContext.toUri(pubspecFilePath);
/// A stream of [RequestMessage]s from the server.
+ @override
Stream get requestsFromServer {
return serverToClient
.where((m) => m is RequestMessage)
@@ -1125,29 +1105,6 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
return notificationFromServer.params as T;
}
- /// Expects a [method] request from the server after executing [f].
- Future expectRequest(
- Method method,
- FutureOr Function() f, {
- Duration timeout = const Duration(seconds: 5),
- }) async {
- var firstRequest = requestsFromServer.firstWhere((n) => n.method == method);
- await f();
-
- var requestFromServer = await firstRequest.timeout(
- timeout,
- onTimeout:
- () =>
- throw TimeoutException(
- 'Did not receive the expected $method request from the server in the timeout period',
- timeout,
- ),
- );
-
- expect(requestFromServer, isNotNull);
- return requestFromServer;
- }
-
/// Gets the current contents of a file.
///
/// This is used to apply edits when the server sends workspace/applyEdit. It
@@ -1155,71 +1112,6 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
/// would be an overlay (if the file is open) or the underlying file.
String? getCurrentFileContent(Uri uri);
- /// Executes [f] then waits for a request of type [method] from the server which
- /// is passed to [handler] to process, then waits for (and returns) the
- /// response to the original request.
- ///
- /// This is used for testing things like code actions, where the client initiates
- /// a request but the server does not respond to it until it's sent its own
- /// request to the client and it received a response.
- ///
- /// Client Server
- /// 1. |- Req: textDocument/codeAction ->
- /// 1. <- Resp: textDocument/codeAction -|
- ///
- /// 2. |- Req: workspace/executeCommand ->
- /// 3. <- Req: textDocument/applyEdits -|
- /// 3. |- Resp: textDocument/applyEdits ->
- /// 2. <- Resp: workspace/executeCommand -|
- ///
- /// Request 2 from the client is not responded to until the server has its own
- /// response to the request it sends (3).
- Future handleExpectedRequest(
- Method method,
- R Function(Map) fromJson,
- Future Function() f, {
- required FutureOr Function(R) handler,
- Duration timeout = const Duration(seconds: 5),
- }) async {
- late Future outboundRequest;
- Object? outboundRequestError;
-
- // Run [f] and wait for the incoming request from the server.
- var incomingRequest = await expectRequest(method, () {
- // Don't return/await the response yet, as this may not complete until
- // after we have handled the request that comes from the server.
- outboundRequest = f();
-
- // Because we don't await this future until "later", if it throws the
- // error is treated as unhandled and will fail the test even if expected.
- // Instead, capture the error and suppress it. But if we time out (in
- // which case we will never return outboundRequest), then we'll raise this
- // error.
- outboundRequest.then(
- (_) {},
- onError: (e) {
- outboundRequestError = e;
- return null;
- },
- );
- }, timeout: timeout).catchError((Object timeoutException) {
- // We timed out waiting for the request from the server. Probably this is
- // because our outbound request for some reason, so if we have an error
- // for that, then throw it. Otherwise, propogate the timeout.
- throw outboundRequestError ?? timeoutException;
- }, test: (e) => e is TimeoutException);
-
- // Handle the request from the server and send the response back.
- var clientsResponse = await handler(
- fromJson(incomingRequest.params as Map),
- );
- respondTo(incomingRequest, clientsResponse);
-
- // Return a future that completes when the response to the original request
- // (from [f]) returns.
- return outboundRequest;
- }
-
/// A helper that initializes the server with common values, since the server
/// will reject any other requests until it is initialized.
/// Capabilities are overridden by providing JSON to avoid having to construct
@@ -1274,6 +1166,16 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
notificationsFromServer.listen((notification) async {
if (notification.method == Method.progress) {
await _handleProgress(notification);
+ } else if (notification.method == CustomMethods.analyzerStatus) {
+ var params = AnalyzerStatusParams.fromJson(
+ notification.params as Map,
+ );
+
+ if (params.isAnalyzing) {
+ _handleAnalysisBegin();
+ } else {
+ _handleAnalysisEnd();
+ }
}
});
@@ -1541,18 +1443,6 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
]);
}
- /// Sends [responseParams] to the server as a successful response to
- /// a server-initiated [request].
- void respondTo(RequestMessage request, T responseParams) {
- sendResponseToServer(
- ResponseMessage(
- id: request.id,
- result: responseParams,
- jsonrpc: jsonRpcVersion,
- ),
- );
- }
-
Future sendDidChangeConfiguration() {
var request = makeRequest(
Method.workspace_didChangeConfiguration,
@@ -1570,11 +1460,8 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
Future sendRequestToServer(RequestMessage request);
- void sendResponseToServer(ResponseMessage response);
-
// This is the signature expected for LSP.
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#:~:text=Response%3A-,result%3A%20null,-error%3A%20code%20and
- // ignore: prefer_void_to_null
Future sendShutdown() {
var request = makeRequest(Method.shutdown, null);
return expectSuccessfulResponseTo(request, (result) => result as Null);
@@ -1729,6 +1616,19 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
return outlineParams.outline;
}
+ void _handleAnalysisBegin() async {
+ assert(_currentAnalysisCompleter.isCompleted);
+ _currentAnalysisCompleter = Completer();
+ }
+
+ void _handleAnalysisEnd() async {
+ if (!_initialAnalysisCompleter.isCompleted) {
+ _initialAnalysisCompleter.complete();
+ }
+ assert(!_currentAnalysisCompleter.isCompleted);
+ _currentAnalysisCompleter.complete();
+ }
+
Future _handleProgress(NotificationMessage request) async {
var params = ProgressParams.fromJson(
request.params as Map,
@@ -1744,6 +1644,15 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
if (WorkDoneProgressEnd.canParse(params.value, nullLspJsonReporter)) {
validProgressTokens.remove(params.token);
}
+
+ if (params.token == analyzingProgressToken) {
+ if (WorkDoneProgressBegin.canParse(params.value, nullLspJsonReporter)) {
+ _handleAnalysisBegin();
+ }
+ if (WorkDoneProgressEnd.canParse(params.value, nullLspJsonReporter)) {
+ _handleAnalysisEnd();
+ }
+ }
}
Future _handleWorkDoneProgressCreate(RequestMessage request) async {
@@ -1778,3 +1687,25 @@ mixin LspAnalysisServerTestMixin on LspRequestHelpersMixin, LspEditHelpersMixin
}
}
}
+
+/// An [AbstractLspAnalysisServerTest] that provides an implementation of
+/// [SharedTestInterface] to allow tests to be shared between server/test kinds.
+abstract class SharedAbstractLspAnalysisServerTest
+ extends AbstractLspAnalysisServerTest
+ implements SharedTestInterface {
+ @override
+ String get testFilePath => join(projectFolderPath, 'lib', 'test.dart');
+
+ @override
+ Uri get testFileUri => toUri(testFilePath);
+
+ @override
+ void createFile(String path, String content) {
+ newFile(path, content);
+ }
+
+ @override
+ Future initializeServer() async {
+ await initialize();
+ }
+}
diff --git a/pkg/analysis_server/test/lsp/test_all.dart b/pkg/analysis_server/test/lsp/test_all.dart
index cfe75dcc3583..06180ebfb923 100644
--- a/pkg/analysis_server/test/lsp/test_all.dart
+++ b/pkg/analysis_server/test/lsp/test_all.dart
@@ -62,6 +62,7 @@ import 'temporary_overlay_operation_test.dart' as temporary_overlay_operation;
import 'type_definition_test.dart' as type_definition;
import 'type_hierarchy_test.dart' as type_hierarchy;
import 'will_rename_files_test.dart' as will_rename_files;
+import 'workspace_apply_edit_test.dart' as workspace_apply_edit;
import 'workspace_symbols_test.dart' as workspace_symbols;
void main() {
@@ -123,6 +124,7 @@ void main() {
type_definition.main();
type_hierarchy.main();
will_rename_files.main();
+ workspace_apply_edit.main();
workspace_symbols.main();
}, name: 'lsp');
}
diff --git a/pkg/analysis_server/test/lsp/workspace_apply_edit_test.dart b/pkg/analysis_server/test/lsp/workspace_apply_edit_test.dart
new file mode 100644
index 000000000000..2d6165247a92
--- /dev/null
+++ b/pkg/analysis_server/test/lsp/workspace_apply_edit_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../shared/shared_workspace_apply_edit_tests.dart';
+import 'server_abstract.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(WorkspaceApplyEditTest);
+ });
+}
+
+@reflectiveTest
+class WorkspaceApplyEditTest extends SharedAbstractLspAnalysisServerTest
+ with
+ // Tests are defined in SharedWorkspaceApplyEditTests because they
+ // are shared and run for both LSP and Legacy servers.
+ SharedWorkspaceApplyEditTests {
+ @override
+ Future initializeServer() async {
+ await initialize();
+ await currentAnalysis;
+ }
+
+ @override
+ Future setUp() async {
+ super.setUp();
+ setApplyEditSupport();
+ setFileCreateSupport();
+ setDocumentChangesSupport();
+ }
+}
diff --git a/pkg/analysis_server/test/lsp_over_legacy/abstract_lsp_over_legacy.dart b/pkg/analysis_server/test/lsp_over_legacy/abstract_lsp_over_legacy.dart
index 2697da54c248..95a225a067fd 100644
--- a/pkg/analysis_server/test/lsp_over_legacy/abstract_lsp_over_legacy.dart
+++ b/pkg/analysis_server/test/lsp_over_legacy/abstract_lsp_over_legacy.dart
@@ -7,6 +7,7 @@ import 'dart:convert';
import 'package:analysis_server/lsp_protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
+import 'package:analysis_server/src/lsp/client_capabilities.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/protocol/protocol_internal.dart';
import 'package:analysis_server/src/protocol_server.dart';
@@ -21,7 +22,9 @@ import 'package:test/test.dart';
import '../analysis_server_base.dart';
import '../lsp/change_verifier.dart';
import '../lsp/request_helpers_mixin.dart';
+import '../lsp/server_abstract.dart';
import '../services/completion/dart/text_expectations.dart';
+import '../shared/shared_test_interface.dart';
class EventsCollector {
final ContextResolutionTest test;
@@ -145,10 +148,10 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
with
LspRequestHelpersMixin,
LspEditHelpersMixin,
- LspVerifyEditHelpersMixin {
- /// The next ID to use for the LSP request that is wrapped inside
- /// a legacy `lsp.handle` request.
- var _nextLspRequestId = 0;
+ LspVerifyEditHelpersMixin,
+ ClientCapabilitiesHelperMixin {
+ /// The next ID to use a request to the server.
+ var _nextRequestId = 0;
/// The last ID that was used for a legacy request.
late String lastSentLegacyRequestId;
@@ -157,6 +160,10 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
final StreamController _notificationsFromServer =
StreamController.broadcast();
+ @override
+ LspClientCapabilities get editorClientCapabilities =>
+ server.editorClientCapabilities;
+
/// A stream of [NotificationMessage]s from the server.
@override
Stream get notificationsFromServer =>
@@ -168,6 +175,16 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
@override
String get projectFolderPath => convertPath(testPackageRootPath);
+ /// A stream of [RequestMessage]s from the server.
+ ///
+ /// Only LSP message requests (`lsp.handle`) from the server are included
+ /// here.
+ @override
+ Stream get requestsFromServer => serverChannel
+ .serverToClientRequests
+ .where((request) => request.method == LSP_REQUEST_HANDLE)
+ .map((request) => RequestMessage.fromJson(request.params));
+
/// The URI for the macro-generated content for [testFileUri].
Uri get testFileMacroUri =>
toUri(convertPath(testFilePath)).replace(scheme: macroClientUriScheme);
@@ -182,7 +199,7 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
AnalysisUpdateContentParams({
convertPath(filePath): AddContentOverlay(content),
}).toRequest(
- '${_nextLspRequestId++}',
+ '${_nextRequestId++}',
clientUriConverter: server.uriConverter,
),
);
@@ -216,7 +233,7 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
/// Creates a legacy request with an auto-assigned ID.
Request createLegacyRequest(RequestParams params) {
return params.toRequest(
- '${_nextLspRequestId++}',
+ '${_nextRequestId++}',
clientUriConverter: server.uriConverter,
);
}
@@ -226,11 +243,7 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
RequestMessage message,
T Function(R) fromJson,
) async {
- // Round-trip request via JSON because this doesn't happen automatically
- // when we're bypassing the streams (running in-process) and we want to
- // validate everything.
- var messageJson =
- jsonDecode(jsonEncode(message.toJson())) as Map;
+ var messageJson = message.toJson();
var legacyRequest = createLegacyRequest(LspHandleParams(messageJson));
var legacyResponse = await handleSuccessfulRequest(legacyRequest);
@@ -239,24 +252,17 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
clientUriConverter: server.uriConverter,
);
- // Round-trip response via JSON because this doesn't happen automatically
- // when we're bypassing the streams (running in-process) and we want to
- // validate everything.
- var lspResponseJson =
- jsonDecode(jsonEncode(legacyResult.lspResponse))
- as Map;
+ var lspResponseJson = legacyResult.lspResponse as Map;
// Unwrap the LSP response.
var lspResponse = ResponseMessage.fromJson(lspResponseJson);
var error = lspResponse.error;
if (error != null) {
throw error;
- } else if (T == Null) {
+ } else {
return lspResponse.result == null
? null as T
- : throw 'Expected Null response but got ${lspResponse.result}';
- } else {
- return fromJson(lspResponse.result as R);
+ : fromJson(lspResponse.result as R);
}
}
@@ -304,6 +310,48 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
}
}
+ Future removeOverlay(String filePath) {
+ return handleSuccessfulRequest(
+ AnalysisUpdateContentParams({
+ convertPath(filePath): RemoveContentOverlay(),
+ }).toRequest(
+ '${_nextRequestId++}',
+ clientUriConverter: server.uriConverter,
+ ),
+ );
+ }
+
+ /// Send the configured LSP client capabilities to the server in a
+ /// `server.setClientCapabilities` request.
+ Future sendClientCapabilities() async {
+ var clientCapabilities = ClientCapabilities(
+ workspace: workspaceCapabilities,
+ textDocument: textDocumentCapabilities,
+ window: windowCapabilities,
+ experimental: experimentalCapabilities,
+ );
+ var request = ServerSetClientCapabilitiesParams(
+ [],
+ lspCapabilities: clientCapabilities,
+ ).toRequest('${_nextRequestId++}', clientUriConverter: server.uriConverter);
+
+ await handleSuccessfulRequest(request);
+ }
+
+ @override
+ void sendResponseToServer(ResponseMessage response) {
+ serverChannel.simulateResponseFromClient(
+ Response(
+ // Convert the LSP int-or-string ID to always a string for legacy.
+ response.id!.map((i) => i.toString(), (s) => s),
+ // A client-provided response to an LSP reverse-request is always
+ // a full LSP result payload as the "result". The legacy request should
+ // always succeed and any errors handled as LSP error responses within.
+ result: response.toJson(),
+ ),
+ );
+ }
+
@override
Future setUp() async {
super.setUp();
@@ -315,7 +363,7 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
AnalysisUpdateContentParams({
convertPath(filePath): ChangeContentOverlay([edit]),
}).toRequest(
- '${_nextLspRequestId++}',
+ '${_nextRequestId++}',
clientUriConverter: server.uriConverter,
),
);
@@ -330,3 +378,66 @@ abstract class LspOverLegacyTest extends PubPackageAnalysisServerTest
verifier.verifyFiles(expected);
}
}
+
+/// A [LspOverLegacyTest] that provides an implementation of
+/// [SharedTestInterface] to allow tests to be shared between server/test kinds.
+abstract class SharedLspOverLegacyTest extends LspOverLegacyTest
+ implements SharedTestInterface {
+ // TODO(dantup): Support this for LSP-over-Legacy shared tests.
+ var failTestOnErrorDiagnostic = false;
+
+ @override
+ Future get currentAnalysis => waitForTasksFinished();
+
+ @override
+ Future closeFile(Uri uri) async {
+ await removeOverlay(fromUri(uri));
+ }
+
+ @override
+ void createFile(String path, String content) {
+ newFile(path, content);
+ }
+
+ @override
+ Future openFile(Uri uri, String content, {int version = 1}) async {
+ await addOverlay(fromUri(uri), content);
+ }
+
+ @override
+ Future replaceFile(int newVersion, Uri uri, String content) async {
+ // For legacy, we can use addOverlay to replace the whole file.
+ await addOverlay(fromUri(uri), content);
+ }
+
+ /// Wraps an LSP request up and sends it from the server to the client.
+ Future sendLspRequestToClient(
+ Method method,
+ Object params,
+ ) async {
+ var id = server.nextServerRequestId++;
+ // Round-trip through JSON to ensure everything becomes basic types and we
+ // don't have instances of classes like `Either2<>` in the JSON.
+ var lspRequest =
+ jsonDecode(
+ jsonEncode(
+ RequestMessage(
+ id: Either2.t1(id),
+ jsonrpc: jsonRpcVersion,
+ method: method,
+ params: params,
+ ),
+ ),
+ )
+ as Map;
+ var legacyResponse = await server.sendRequest(
+ Request(id.toString(), LSP_REQUEST_HANDLE, lspRequest),
+ );
+
+ // Round-trip through JSON to ensure everything becomes basic types and we
+ // don't have instances of classes like `Either2<>` in the JSON.
+ var lspResponse =
+ jsonDecode(jsonEncode(legacyResponse.result)) as Map;
+ return ResponseMessage.fromJson(lspResponse);
+ }
+}
diff --git a/pkg/analysis_server/test/lsp_over_legacy/editable_arguments_test.dart b/pkg/analysis_server/test/lsp_over_legacy/editable_arguments_test.dart
new file mode 100644
index 000000000000..5e56fffbce26
--- /dev/null
+++ b/pkg/analysis_server/test/lsp_over_legacy/editable_arguments_test.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../shared/shared_editable_arguments_tests.dart';
+import 'abstract_lsp_over_legacy.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(EditableArgumentsTest);
+ });
+}
+
+@reflectiveTest
+class EditableArgumentsTest extends SharedLspOverLegacyTest
+ with
+ // Tests are defined in SharedEditableArgumentsTests because they
+ // are shared and run for both LSP and Legacy servers.
+ SharedEditableArgumentsTests {
+ @override
+ Future initializeServer() async {
+ await waitForTasksFinished();
+ }
+
+ @override
+ Future setUp() async {
+ await super.setUp();
+
+ writeTestPackageConfig(flutter: true);
+ }
+
+ @override
+ @FailingTest(reason: 'Document versions not currently supported for legacy')
+ test_textDocument_closedFile() {
+ // TODO(dantup): Implement support for version numbers in the legacy
+ // protocol.
+ return super.test_textDocument_closedFile();
+ }
+
+ @override
+ @FailingTest(reason: 'Document versions not currently supported for legacy')
+ test_textDocument_versions() {
+ // TODO(dantup): Implement support for version numbers in the legacy
+ // protocol.
+ return super.test_textDocument_versions();
+ }
+}
diff --git a/pkg/analysis_server/test/lsp_over_legacy/test_all.dart b/pkg/analysis_server/test/lsp_over_legacy/test_all.dart
index 488b610d88ac..afc8547e3e10 100644
--- a/pkg/analysis_server/test/lsp_over_legacy/test_all.dart
+++ b/pkg/analysis_server/test/lsp_over_legacy/test_all.dart
@@ -10,6 +10,7 @@ import 'dart_text_document_content_provider_test.dart'
import 'document_color_test.dart' as document_color;
import 'document_highlights_test.dart' as document_highlights;
import 'document_symbols_test.dart' as document_symbols;
+import 'editable_arguments_test.dart' as editable_arguments;
import 'format_test.dart' as format;
import 'hover_test.dart' as hover;
import 'implementation_test.dart' as implementation;
@@ -17,6 +18,7 @@ import 'signature_help_test.dart' as signature_help;
import 'type_definition_test.dart' as type_definition;
import 'type_hierarchy_test.dart' as type_hierarchy;
import 'will_rename_files_test.dart' as will_rename_files;
+import 'workspace_apply_edit_test.dart' as workspace_apply_edit;
import 'workspace_symbols_test.dart' as workspace_symbols;
void main() {
@@ -26,6 +28,7 @@ void main() {
document_color.main;
document_highlights.main();
document_symbols.main();
+ editable_arguments.main();
format.main();
hover.main();
implementation.main();
@@ -33,6 +36,7 @@ void main() {
type_definition.main();
type_hierarchy.main();
will_rename_files.main();
+ workspace_apply_edit.main();
workspace_symbols.main();
}, name: 'lsp_over_legacy');
}
diff --git a/pkg/analysis_server/test/lsp_over_legacy/workspace_apply_edit_test.dart b/pkg/analysis_server/test/lsp_over_legacy/workspace_apply_edit_test.dart
new file mode 100644
index 000000000000..45495f5f5f7c
--- /dev/null
+++ b/pkg/analysis_server/test/lsp_over_legacy/workspace_apply_edit_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../shared/shared_workspace_apply_edit_tests.dart';
+import 'abstract_lsp_over_legacy.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(WorkspaceApplyEditTest);
+ });
+}
+
+@reflectiveTest
+class WorkspaceApplyEditTest extends SharedLspOverLegacyTest
+ with
+ // Tests are defined in SharedWorkspaceApplyEditTests because they
+ // are shared and run for both LSP and Legacy servers.
+ SharedWorkspaceApplyEditTests {
+ @override
+ Future initializeServer() async {
+ await waitForTasksFinished();
+ await sendClientCapabilities();
+ }
+
+ @override
+ Future setUp() async {
+ await super.setUp();
+ setApplyEditSupport();
+ setFileCreateSupport();
+ setDocumentChangesSupport();
+ }
+}
diff --git a/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart b/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart
new file mode 100644
index 000000000000..dfa990ae933a
--- /dev/null
+++ b/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart
@@ -0,0 +1,1270 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/lsp_protocol/protocol.dart';
+import 'package:analyzer/src/test_utilities/test_code_format.dart';
+import 'package:test/test.dart';
+
+import '../lsp/request_helpers_mixin.dart';
+import '../utils/test_code_extensions.dart';
+import 'shared_test_interface.dart';
+
+/// Shared editable arguments tests that are used by both LSP + Legacy server
+/// tests.
+mixin SharedEditableArgumentsTests
+ on SharedTestInterface, LspRequestHelpersMixin {
+ late TestCode code;
+
+ /// Initializes the server with [content] and fetches editable arguments.
+ Future getEditableArgumentsFor(
+ String content, {
+ bool open = true,
+ }) async {
+ code = TestCode.parse('''
+import 'package:flutter/widgets.dart';
+
+$content
+''');
+ createFile(testFilePath, code.code);
+ await initializeServer();
+ if (open) {
+ await openFile(testFileUri, code.code);
+ }
+ await currentAnalysis;
+ return await getEditableArguments(testFileUri, code.position.position);
+ }
+
+ Matcher hasArg(Matcher matcher) {
+ return hasArgs(contains(matcher));
+ }
+
+ Matcher hasArgNamed(String argumentName) {
+ return hasArg(isArg(argumentName));
+ }
+
+ Matcher hasArgs(Matcher matcher) {
+ return isA().having(
+ (arguments) => arguments.arguments,
+ 'arguments',
+ matcher,
+ );
+ }
+
+ Matcher isArg(
+ String name, {
+ Object? type = anything,
+ Object? value = anything,
+ Object? displayValue = anything,
+ Object? hasArgument = anything,
+ Object? isDefault = anything,
+ Object? isRequired = anything,
+ Object? isNullable = anything,
+ Object? isEditable = anything,
+ Object? notEditableReason = anything,
+ Object? options = anything,
+ }) {
+ return isA()
+ .having((arg) => arg.name, 'name', name)
+ .having((arg) => arg.type, 'type', type)
+ .having((arg) => arg.value, 'value', value)
+ .having((arg) => arg.displayValue, 'displayValue', displayValue)
+ .having((arg) => arg.hasArgument, 'hasArgument', hasArgument)
+ .having((arg) => arg.isDefault, 'isDefault', isDefault)
+ .having((arg) => arg.isRequired, 'isRequired', isRequired)
+ .having((arg) => arg.isNullable, 'isNullable', isNullable)
+ .having((arg) => arg.isEditable, 'isEditable', isEditable)
+ .having(
+ (arg) => arg.notEditableReason,
+ 'notEditableReason',
+ notEditableReason,
+ )
+ .having((arg) => arg.options, 'options', options)
+ // Some extra checks that should be true for all.
+ .having(
+ (arg) =>
+ arg.value == null ||
+ arg.value?.toString() != arg.displayValue?.toString(),
+ 'different value and displayValues',
+ isTrue,
+ )
+ .having(
+ (arg) => (arg.notEditableReason == null) == arg.isEditable,
+ 'notEditableReason must be supplied if isEditable=false',
+ isTrue,
+ )
+ .having(
+ (arg) => arg.value == null || arg.isEditable,
+ 'isEditable must be true if there is a value',
+ isTrue,
+ )
+ .having(
+ (arg) =>
+ arg.type == 'enum'
+ ? (arg.options?.isNotEmpty ?? false)
+ : arg.options == null,
+ 'enum types must have options / non-enums must not have options',
+ isTrue,
+ );
+ }
+
+ test_hasArgument() async {
+ failTestOnErrorDiagnostic = false;
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget(
+ int? aPositionalSupplied,
+ int? aPositionalNotSupplied, {
+ int? aNamedSupplied,
+ int? aNamedNotSupplied,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(1, aNamedSupplied: 1);
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ unorderedEquals([
+ isArg('aPositionalSupplied', hasArgument: true),
+ isArg('aPositionalNotSupplied', hasArgument: false),
+ isArg('aNamedSupplied', hasArgument: true),
+ isArg('aNamedNotSupplied', hasArgument: false),
+ ]),
+ ),
+ );
+ }
+
+ test_isEditable_false_positional_optional() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget([int? a, int? b, int? c]);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(1);
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('a', isEditable: true),
+ isArg('b', isEditable: true),
+ isArg(
+ 'c',
+ // c is not editable because it is not guaranteed that we can insert
+ // a default value for b (it could be a private value or require
+ // imports).
+ isEditable: false,
+ notEditableReason:
+ "A value for the 3rd parameter can't be added until a value "
+ 'for all preceding positional parameters have been added.',
+ ),
+ ]),
+ ),
+ );
+ }
+
+ test_isEditable_false_positional_required1() async {
+ failTestOnErrorDiagnostic = false;
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget(int a, int b);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget();
+}
+''');
+ expect(
+ result,
+ hasArg(
+ // b is not editable because there are missing required previous
+ // arguments (a).
+ isArg(
+ 'b',
+ isEditable: false,
+ notEditableReason:
+ "A value for the 2nd parameter can't be added until a value "
+ 'for all preceding positional parameters have been added.',
+ ),
+ ),
+ );
+ }
+
+ test_isEditable_false_positional_required2() async {
+ failTestOnErrorDiagnostic = false;
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget(int a, int b, int c);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(1);
+}
+''');
+ expect(
+ result,
+ hasArg(
+ // c is not editable because there are missing required previous
+ // arguments (b).
+ isArg(
+ 'c',
+ isEditable: false,
+ notEditableReason:
+ "A value for the 3rd parameter can't be added until a value "
+ 'for all preceding positional parameters have been added.',
+ ),
+ ),
+ );
+ }
+
+ test_isEditable_false_string_adjacent() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget(String s);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget('a' 'b');
+}
+''');
+ expect(
+ result,
+ hasArg(
+ isArg(
+ 's',
+ type: 'string',
+ value: isNull,
+ displayValue: 'ab',
+ isDefault: false,
+ isEditable: false,
+ notEditableReason: "Adjacent strings can't be edited",
+ ),
+ ),
+ );
+ }
+
+ test_isEditable_false_string_interpolated() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget(String s);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget('${context.runtimeType}');
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg(
+ 's',
+ type: 'string',
+ value: isNull,
+ displayValue: r"'${context.runtimeType}'",
+ isEditable: false,
+ notEditableReason: "Interpolated strings can't be edited",
+ ),
+ ]),
+ ),
+ );
+ }
+
+ test_isEditable_false_string_withNewlines() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget(String sEscaped, String sLiteral);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ 'a\nb',
+ """
+a
+b
+""",
+ );
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg(
+ 'sEscaped',
+ type: 'string',
+ value: isNull,
+ displayValue: 'a\nb',
+ isEditable: false,
+ notEditableReason: "Strings containing newlines can't be edited",
+ ),
+ isArg(
+ 'sLiteral',
+ type: 'string',
+ value: isNull,
+ displayValue: 'a\nb\n',
+ isEditable: false,
+ notEditableReason: "Strings containing newlines can't be edited",
+ ),
+ ]),
+ ),
+ );
+ }
+
+ test_isEditable_true_named() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget({int? a, int? b, int? c});
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(a: 1);
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('a', isEditable: true),
+ isArg('b', isEditable: true),
+ isArg('c', isEditable: true),
+ ]),
+ ),
+ );
+ }
+
+ test_isEditable_true_positional_required() async {
+ failTestOnErrorDiagnostic = false;
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget(int a, int b);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(1);
+}
+''');
+ expect(
+ result,
+ hasArg(
+ isArg(
+ 'b',
+ // b is editable because it's the next argument and we don't need
+ // to add anything additional.
+ isEditable: true,
+ ),
+ ),
+ );
+ }
+
+ test_isEditable_true_string_dollar_escaped() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget(String s);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget('\${1}');
+}
+''');
+ expect(
+ result,
+ hasArg(
+ isArg(
+ 's',
+ type: 'string',
+ value: r'${1}',
+ displayValue: isNull,
+ isEditable: true,
+ ),
+ ),
+ );
+ }
+
+ test_isEditable_true_string_dollar_raw() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget(String s);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(r'${1}');
+}
+''');
+ expect(
+ result,
+ hasArg(
+ isArg(
+ 's',
+ type: 'string',
+ value: r'${1}',
+ displayValue: isNull,
+ isEditable: true,
+ ),
+ ),
+ );
+ }
+
+ test_isEditable_true_string_tripleQuoted_withoutNewlines() async {
+ var result = await getEditableArgumentsFor(r'''
+class MyWidget extends StatelessWidget {
+ const MyWidget(String s);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget("""string_value""");
+}
+''');
+ expect(
+ result,
+ hasArg(
+ isArg(
+ 's',
+ type: 'string',
+ value: 'string_value',
+ displayValue: isNull,
+ isEditable: true,
+ ),
+ ),
+ );
+ }
+
+ test_isNullable() async {
+ failTestOnErrorDiagnostic = false;
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget(
+ int aPositional,
+ int? aPositionalNullable, {
+ int? aNamed,
+ required int aRequiredNamed,
+ required int? aRequiredNamedNullable
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget();
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('aPositional', isNullable: false),
+ isArg('aPositionalNullable', isNullable: true),
+ isArg('aNamed', isNullable: true),
+ isArg('aRequiredNamed', isNullable: false),
+ isArg('aRequiredNamedNullable', isNullable: true),
+ ]),
+ ),
+ );
+ }
+
+ test_isRequired() async {
+ failTestOnErrorDiagnostic = false;
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget(
+ int aPositional,
+ int? aPositionalNullable, {
+ int? aNamed,
+ required int aRequiredNamed,
+ required int? aRequiredNamedNullable
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget();
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('aPositional', isRequired: true),
+ isArg('aPositionalNullable', isRequired: true),
+ isArg('aNamed', isRequired: false),
+ isArg('aRequiredNamed', isRequired: true),
+ isArg('aRequiredNamedNullable', isRequired: true),
+ ]),
+ ),
+ );
+ }
+
+ test_location_bad_extensionMethod_noWidgetFactory() async {
+ var result = await getEditableArgumentsFor('''
+extension on MyWidget {
+ Widget padded(String a1) => this;
+}
+
+class MyWidget extends StatelessWidget {
+ const MyWidget();
+ const MyWidget.foo(String a1);
+
+ @override
+ Widget build(BuildContext context) => this.pad^ded('value1');
+}
+''');
+ expect(result, isNull);
+ }
+
+ test_location_bad_functionInvocation() async {
+ var result = await getEditableArgumentsFor('''
+MyWidget create(String a1) => throw '';
+
+class MyWidget extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) => crea^te('value1');
+}
+''');
+ expect(result, isNull);
+ }
+
+ test_location_bad_methodInvocation() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ @widgetFactory
+ MyWidget create(String a1) => throw '';
+
+ @override
+ Widget build(BuildContext context) => crea^te('value1');
+}
+''');
+ expect(result, isNull);
+ }
+
+ test_location_bad_unnamedConstructor_notWidget() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget {
+ const MyWidget(String a1);
+
+ @override
+ MyWidget build(BuildContext context) => MyW^idget('value1');
+}
+''');
+ expect(result, isNull);
+ }
+
+ test_location_good_argumentList_argumentName() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({required String a1 });
+
+ @override
+ Widget build(BuildContext context) => MyWidget(a^1: 'value1');
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_argumentList_literalValue() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({required String a1 });
+
+ @override
+ Widget build(BuildContext context) => MyWidget(a1: 'val^ue1');
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_argumentList_nestedInvocation() async {
+ var result = await getEditableArgumentsFor('''
+String getString() => '';
+
+class MyWidget extends StatelessWidget {
+ const MyWidget(String a1);
+
+ @override
+ Widget build(BuildContext context) => MyWidget(getS^tring());
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_argumentList_nestedInvocation_arguments() async {
+ var result = await getEditableArgumentsFor('''
+String getString(String s) => s;
+
+class MyWidget extends StatelessWidget {
+ const MyWidget(String a1);
+
+ @override
+ Widget build(BuildContext context) => MyWidget(getString('valu^e1'));
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_argumentList_parens_afterOpen() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({required String a1 });
+
+ @override
+ Widget build(BuildContext context) => MyWidget(^a1: 'value1');
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_argumentList_parens_beforeClose() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({required String a1 });
+
+ @override
+ Widget build(BuildContext context) => MyWidget(a1: 'value1'^);
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_argumentList_parens_beforeOpen() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({required String a1 });
+
+ @override
+ Widget build(BuildContext context) => MyWidget^(a1: 'value1');
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_extensionMethod_constructorTarget() async {
+ var result = await getEditableArgumentsFor('''
+extension on MyWidget {
+ @widgetFactory
+ Widget padded(String a1) => this;
+}
+
+class MyWidget extends StatelessWidget {
+ const MyWidget();
+ const MyWidget.foo(String a1);
+
+ @override
+ Widget build(BuildContext context) => MyWidget().pad^ded('value1');
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_extensionMethod_thisTarget() async {
+ var result = await getEditableArgumentsFor('''
+extension on MyWidget {
+ @widgetFactory
+ Widget padded(String a1) => this;
+}
+
+class MyWidget extends StatelessWidget {
+ const MyWidget.foo(String a1);
+
+ @override
+ Widget build(BuildContext context) => this.pad^ded('value1');
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_extensionMethod_variableTarget() async {
+ var result = await getEditableArgumentsFor('''
+extension on MyWidget {
+ @widgetFactory
+ Widget padded(String a1) => this;
+}
+
+class MyWidget extends StatelessWidget {
+ const MyWidget();
+ const MyWidget.foo(String a1);
+
+ @override
+ Widget build(BuildContext context) {
+ MyWidget? foo;
+ return foo!.pad^ded('value1');
+ }
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_namedConstructor_className() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget.foo(String a1);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget.foo('value1');
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_namedConstructor_constructorName() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget.foo(String a1);
+
+ @override
+ Widget build(BuildContext context) => MyWidget.f^oo('value1');
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ test_location_good_unnamedConstructor() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget(String a1);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget('value1');
+}
+''');
+ expect(result, hasArgNamed('a1'));
+ }
+
+ /// Arguments should be returned in the order of the parameters in the source
+ /// code. This keeps things consistent across different instances of the same
+ /// Widget class and prevents the order from changing as a user adds/removes
+ /// arguments.
+ ///
+ /// If an editor wants to sort provided arguments first (and keep these stable
+ /// across add/removes) it could still do so client-side, whereas if server
+ /// orders them that way, the opposite (using source-order) is not possible.
+ test_order() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ int c1 = 0,
+ int c2 = 0,
+ int a1 = 0,
+ int a2 = 0,
+ int b1 = 0,
+ int b2 = 0,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(b1: 1, a1: 1, c1: 1);
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('c1'),
+ isArg('c2'),
+ isArg('a1'),
+ isArg('a2'),
+ isArg('b1'),
+ isArg('b2'),
+ ]),
+ ),
+ );
+ }
+
+ test_textDocument_closedFile() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget(String a1);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget('value1');
+}
+''');
+
+ // Verify initial content of 1.
+ expect(
+ result!.textDocument,
+ isA()
+ .having((td) => td.uri, 'uri', testFileUri)
+ .having((td) => td.version, 'version', 1),
+ );
+
+ // Close the file.
+ await closeFile(testFileUri);
+
+ // Verify new results have null version.
+ result = await getEditableArguments(testFileUri, code.position.position);
+ expect(
+ result!.textDocument,
+ isA()
+ .having((td) => td.uri, 'uri', testFileUri)
+ .having((td) => td.version, 'version', isNull),
+ );
+ }
+
+ test_textDocument_unopenedFile() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget(String a1);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget('value1');
+}
+''', open: false);
+
+ // Verify null version for unopened file.
+ expect(
+ result!.textDocument,
+ isA()
+ .having((td) => td.uri, 'uri', testFileUri)
+ .having((td) => td.version, 'version', null),
+ );
+ }
+
+ test_textDocument_versions() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget(String a1);
+
+ @override
+ Widget build(BuildContext context) => MyW^idget('value1');
+}
+''');
+
+ // Verify initial content of 1.
+ expect(
+ result!.textDocument,
+ isA()
+ .having((td) => td.uri, 'uri', testFileUri)
+ .having((td) => td.version, 'version', 1),
+ );
+
+ // Update the content to v5.
+ await replaceFile(5, testFileUri, '${code.code}\n// extra comment');
+
+ // Verify new results have version 5.
+ result = await getEditableArguments(testFileUri, code.position.position);
+ expect(
+ result!.textDocument,
+ isA()
+ .having((td) => td.uri, 'uri', testFileUri)
+ .having((td) => td.version, 'version', 5),
+ );
+ }
+
+ test_type_bool() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ bool supplied = true,
+ bool suppliedAsDefault = true,
+ bool notSupplied = true,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ supplied: false,
+ suppliedAsDefault: true,
+ );
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('supplied', type: 'bool', value: false, isDefault: false),
+ isArg(
+ 'suppliedAsDefault',
+ type: 'bool',
+ value: true,
+ isDefault: true,
+ ),
+ isArg('notSupplied', type: 'bool', value: true, isDefault: true),
+ ]),
+ ),
+ );
+ }
+
+ test_type_bool_nonLiterals() async {
+ var result = await getEditableArgumentsFor('''
+var myVar = true;
+const myConst = true;
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ bool? aVar,
+ bool? aConst,
+ bool? aExpr,
+ bool? aConstExpr,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ aVar: myVar,
+ aConst: myConst,
+ aExpr: DateTime.now().isBefore(DateTime.now()),
+ aConstExpr: 1 == 2,
+ );
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('aVar', type: 'bool', value: isNull, displayValue: 'myVar'),
+ isArg('aConst', type: 'bool', value: true, displayValue: 'myConst'),
+ isArg(
+ 'aExpr',
+ type: 'bool',
+ value: isNull,
+ displayValue: 'DateTime.now().isBefore(DateTime.now())',
+ ),
+ isArg(
+ 'aConstExpr',
+ type: 'bool',
+ value: false,
+ displayValue: '1 == 2',
+ ),
+ ]),
+ ),
+ );
+ }
+
+ test_type_double() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ double supplied = 1.0,
+ double suppliedAsDefault = 1.0,
+ double notSupplied = 1.0,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ supplied: 2.0,
+ suppliedAsDefault: 1.0,
+ );
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('supplied', type: 'double', value: 2.0, isDefault: false),
+ isArg(
+ 'suppliedAsDefault',
+ type: 'double',
+ value: 1.0,
+ isDefault: true,
+ ),
+ isArg('notSupplied', type: 'double', value: 1.0, isDefault: true),
+ ]),
+ ),
+ );
+ }
+
+ test_type_double_intLiterals() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ double supplied = 1,
+ double suppliedAsDefault = 1,
+ double notSupplied = 1,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ supplied: 2,
+ suppliedAsDefault: 1,
+ );
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('supplied', type: 'double', value: 2, isDefault: false),
+ isArg('suppliedAsDefault', type: 'double', value: 1, isDefault: true),
+ isArg('notSupplied', type: 'double', value: 1, isDefault: true),
+ ]),
+ ),
+ );
+ }
+
+ test_type_double_nonLiterals() async {
+ var result = await getEditableArgumentsFor('''
+var myVar = 1.0;
+const myConst = 1.0;
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ double? aVar,
+ double? aConst,
+ double? aExpr,
+ double? aConstExpr,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ aVar: myVar,
+ aConst: myConst,
+ aExpr: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ aConstExpr: 1.0 + myConst,
+ );
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('aVar', type: 'double', value: isNull, displayValue: 'myVar'),
+ isArg('aConst', type: 'double', value: 1.0, displayValue: 'myConst'),
+ isArg(
+ 'aExpr',
+ type: 'double',
+ value: isNull,
+ displayValue: 'DateTime.now().millisecondsSinceEpoch.toDouble()',
+ ),
+ isArg(
+ 'aConstExpr',
+ type: 'double',
+ value: 2.0,
+ displayValue: '1.0 + myConst',
+ ),
+ ]),
+ ),
+ );
+ }
+
+ test_type_enum() async {
+ var result = await getEditableArgumentsFor('''
+enum E { one, two }
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ E supplied = E.one,
+ E suppliedAsDefault = E.one,
+ E notSupplied = E.one,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ supplied: E.two,
+ suppliedAsDefault: E.one,
+ );
+}
+''');
+
+ var optionsMatcher = equals(['E.one', 'E.two']);
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg(
+ 'supplied',
+ type: 'enum',
+ value: 'E.two',
+ isDefault: false,
+ options: optionsMatcher,
+ ),
+ isArg(
+ 'suppliedAsDefault',
+ type: 'enum',
+ value: 'E.one',
+ isDefault: true,
+ options: optionsMatcher,
+ ),
+ isArg(
+ 'notSupplied',
+ type: 'enum',
+ value: 'E.one',
+ isDefault: true,
+ options: optionsMatcher,
+ ),
+ ]),
+ ),
+ );
+ }
+
+ test_type_enum_nonLiterals() async {
+ var result = await getEditableArgumentsFor('''
+enum E { one, two }
+var myVar = E.one;
+const myConst = E.one;
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ E? aVar,
+ E? aConst,
+ E? aExpr,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ aVar: myVar,
+ aConst: myConst,
+ aExpr: E.values.first,
+ );
+}
+''');
+
+ var optionsMatcher = equals(['E.one', 'E.two']);
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg(
+ 'aVar',
+ type: 'enum',
+ value: isNull,
+ displayValue: 'myVar',
+ options: optionsMatcher,
+ ),
+ isArg(
+ 'aConst',
+ type: 'enum',
+ value: 'E.one',
+ displayValue: 'myConst',
+ options: optionsMatcher,
+ ),
+ isArg(
+ 'aExpr',
+ type: 'enum',
+ value: isNull,
+ displayValue: 'E.values.first',
+ options: optionsMatcher,
+ ),
+ ]),
+ ),
+ );
+ }
+
+ test_type_int() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ int supplied = 1,
+ int suppliedAsDefault = 1,
+ int notSupplied = 1,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ supplied: 2,
+ suppliedAsDefault: 1,
+ );
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('supplied', type: 'int', value: 2, isDefault: false),
+ isArg('suppliedAsDefault', type: 'int', value: 1, isDefault: true),
+ isArg('notSupplied', type: 'int', value: 1, isDefault: true),
+ ]),
+ ),
+ );
+ }
+
+ test_type_int_nonLiterals() async {
+ var result = await getEditableArgumentsFor('''
+var myVar = 1;
+const myConst = 1;
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ int? aVar,
+ int? aConst,
+ int? aExpr,
+ int? aConstExpr,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ aVar: myVar,
+ aConst: myConst,
+ aExpr: DateTime.now().millisecondsSinceEpoch,
+ aConstExpr: 1 + myConst,
+ );
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('aVar', type: 'int', value: isNull, displayValue: 'myVar'),
+ isArg('aConst', type: 'int', value: 1, displayValue: 'myConst'),
+ isArg(
+ 'aExpr',
+ type: 'int',
+ value: isNull,
+ displayValue: 'DateTime.now().millisecondsSinceEpoch',
+ ),
+ isArg(
+ 'aConstExpr',
+ type: 'int',
+ value: 2,
+ displayValue: '1 + myConst',
+ ),
+ ]),
+ ),
+ );
+ }
+
+ test_type_string() async {
+ var result = await getEditableArgumentsFor('''
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ String supplied = 'a',
+ String suppliedAsDefault = 'a',
+ String notSupplied = 'a',
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ supplied: 'b',
+ suppliedAsDefault: 'a',
+ );
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('supplied', type: 'string', value: 'b', isDefault: false),
+ isArg(
+ 'suppliedAsDefault',
+ type: 'string',
+ value: 'a',
+ isDefault: true,
+ ),
+ isArg('notSupplied', type: 'string', value: 'a', isDefault: true),
+ ]),
+ ),
+ );
+ }
+
+ test_type_string_nonLiterals() async {
+ var result = await getEditableArgumentsFor('''
+var myVar = 'a';
+const myConst = 'a';
+class MyWidget extends StatelessWidget {
+ const MyWidget({
+ String? aVar,
+ String? aConst,
+ String? aExpr,
+ String? aConstExpr,
+ });
+
+ @override
+ Widget build(BuildContext context) => MyW^idget(
+ aVar: myVar,
+ aConst: myConst,
+ aExpr: DateTime.now().toString(),
+ aConstExpr: 'a' + 'b',
+ );
+}
+''');
+ expect(
+ result,
+ hasArgs(
+ orderedEquals([
+ isArg('aVar', type: 'string', value: isNull, displayValue: 'myVar'),
+ isArg('aConst', type: 'string', value: 'a', displayValue: 'myConst'),
+ isArg(
+ 'aExpr',
+ type: 'string',
+ value: isNull,
+ displayValue: 'DateTime.now().toString()',
+ ),
+ isArg(
+ 'aConstExpr',
+ type: 'string',
+ value: 'ab',
+ displayValue: "'a' + 'b'",
+ ),
+ ]),
+ ),
+ );
+ }
+}
diff --git a/pkg/analysis_server/test/shared/shared_test_interface.dart b/pkg/analysis_server/test/shared/shared_test_interface.dart
new file mode 100644
index 000000000000..47f3fb884aa7
--- /dev/null
+++ b/pkg/analysis_server/test/shared/shared_test_interface.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// A common interface that can be implemented by a set of base classes to
+/// allow tests to be written to run in different configurations
+/// (LSP and LSP-over-Legacy, and in-process and out-of-process).
+///
+/// Implementations should use the appropriate APIs for the given server, for
+/// example a test running against LSP will send a `textDocument/didOpen`
+/// notification from [openFile] whereas when using the legacy server (even for
+/// LSP-over-Legacy tests) will send an `analysis.updateContent` request.
+abstract interface class SharedTestInterface {
+ /// A future that completes when the current analysis completes.
+ ///
+ /// If there is no analysis in progress, completes immediately.
+ Future get currentAnalysis;
+
+ /// Sets whether the test should fail if error diagnostics are generated.
+ ///
+ /// This is used to avoid accidentally including invalid code in tests but can
+ /// be overridden for tests that are deliberately testing invalid code.
+ set failTestOnErrorDiagnostic(bool value);
+
+ /// Gets the full normalized file path of a file named "test.dart" in the test
+ /// project.
+ String get testFilePath;
+
+ /// Gets a file:/// URI for [testFilePath];
+ Uri get testFileUri => Uri.file(testFilePath);
+
+ /// Tells the server that file with [uri] has been closed and any overlay
+ /// should be removed.
+ Future closeFile(Uri uri);
+
+ /// Creates a file at [filePath] with the given [content].
+ void createFile(String filePath, String content);
+
+ /// Performs standard initialization of the server, including starting
+ /// the server (an external process for integration tests) and sending any
+ /// initialization/analysis roots, and waiting for initial analysis to
+ /// complete.
+ Future initializeServer();
+
+ /// Tells the server that the file with [uri] has been opened and has the
+ /// given [content].
+ Future openFile(Uri uri, String content, {int version = 1});
+
+ /// Tells the server that the file with [uri] has had it's content replaced
+ /// with [content].
+ Future replaceFile(int newVersion, Uri uri, String content);
+}
diff --git a/pkg/analysis_server/test/shared/shared_workspace_apply_edit_tests.dart b/pkg/analysis_server/test/shared/shared_workspace_apply_edit_tests.dart
new file mode 100644
index 000000000000..3cb3a19afb04
--- /dev/null
+++ b/pkg/analysis_server/test/shared/shared_workspace_apply_edit_tests.dart
@@ -0,0 +1,162 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/lsp_protocol/protocol.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analysis_server/src/lsp/source_edits.dart';
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/test_utilities/test_code_format.dart';
+import 'package:test/test.dart';
+
+import '../lsp/change_verifier.dart';
+import '../lsp/request_helpers_mixin.dart';
+
+/// Shared `workspace/applyEdit` tests that are used by both LSP and legacy
+/// server tests.
+mixin SharedWorkspaceApplyEditTests
+ on LspRequestHelpersMixin, LspVerifyEditHelpersMixin {
+ /// Overridden by test subclasses to provide the path of a file for testing.
+ String get testFilePath;
+
+ /// The URI for [testFilePath].
+ Uri get testFileUri => Uri.file(testFilePath);
+
+ /// Overridden by test subclasses to create a new file.
+ void createFile(String path, String content);
+
+ /// Overridden by test subclasses to initialize the server.
+ Future initializeServer();
+
+ /// Overridden by test subclasses to send LSP requests from the server to
+ /// the client.
+ Future sendLspRequestToClient(Method method, Object params);
+
+ test_applyEdit_existingFile() async {
+ var code = TestCode.parse('''
+void f() {
+ print('--/*[0*/replace/*0]*/--');
+ // --/*[1*/delete/*1]*/--
+ // --^--
+}
+''');
+
+ var expectedResult = r'''
+>>>>>>>>>> lib/test.dart
+void f() {
+ print('--replacedtext--');
+ // ----
+ // --insertedtext--
+}
+''';
+
+ createFile(testFilePath, code.code);
+
+ await initializeServer();
+
+ var fileEdits = [
+ FileEditInformation(
+ newFile: false,
+ OptionalVersionedTextDocumentIdentifier(uri: testFileUri, version: 5),
+ LineInfo.fromContent(code.code),
+ [
+ // replace
+ SourceEdit(
+ code.ranges[0].sourceRange.offset,
+ code.ranges[0].sourceRange.length,
+ 'replacedtext',
+ ),
+ // delete
+ SourceEdit(
+ code.ranges[1].sourceRange.offset,
+ code.ranges[1].sourceRange.length,
+ '',
+ ),
+ // insert
+ SourceEdit(code.position.offset, 0, 'insertedtext'),
+ ],
+ ),
+ ];
+
+ var (verifier, applyEditResult) = await _sendApplyEdits(
+ toWorkspaceEdit(editorClientCapabilities, fileEdits),
+ );
+
+ expect(applyEditResult.applied, isTrue);
+ verifier.verifyFiles(expectedResult);
+ }
+
+ test_applyEdit_newFile() async {
+ await initializeServer();
+
+ var fileEdits = [
+ FileEditInformation(
+ newFile: true,
+ OptionalVersionedTextDocumentIdentifier(uri: testFileUri),
+ LineInfo.fromContent(''),
+ [SourceEdit(0, 0, 'inserted')],
+ ),
+ ];
+
+ var (verifier, applyEditResult) = await _sendApplyEdits(
+ toWorkspaceEdit(editorClientCapabilities, fileEdits),
+ );
+
+ expect(applyEditResult.applied, isTrue);
+ verifier.verifyFiles('''
+>>>>>>>>>> lib/test.dart created
+inserted<<<<<<<<<<
+''');
+ }
+
+ test_bad_failedToApply() async {
+ await initializeServer();
+
+ var fileEdits = [
+ FileEditInformation(
+ newFile: true,
+ OptionalVersionedTextDocumentIdentifier(uri: testFileUri),
+ LineInfo.fromContent(''),
+ [SourceEdit(0, 0, 'inserted')],
+ ),
+ ];
+
+ var (verifier, applyEditResult) = await _sendApplyEdits(
+ toWorkspaceEdit(editorClientCapabilities, fileEdits),
+ // Have the client return that it failed to apply.
+ applyEditResult: ApplyWorkspaceEditResult(
+ applied: false,
+ failureReason: 'x',
+ ),
+ );
+
+ // Ensure the server correctly parsed the clients result.
+ expect(applyEditResult.applied, isFalse);
+ expect(applyEditResult.failureReason, 'x');
+ }
+
+ /// Triggers a `workspace/applyEdit` request from the server, collects the
+ /// edits from the client, and returns a [LspChangeVerifier] along with the
+ /// [ApplyWorkspaceEditResult] the server got back.
+ Future<(LspChangeVerifier, ApplyWorkspaceEditResult)> _sendApplyEdits(
+ WorkspaceEdit workspaceEdit, {
+ ApplyWorkspaceEditResult? applyEditResult,
+ }) async {
+ var applyEditResultToSend = applyEditResult;
+ ApplyWorkspaceEditResult? receivedApplyEditResult;
+
+ var verifier = await executeForEdits(() async {
+ var result = await sendLspRequestToClient(
+ Method.workspace_applyEdit,
+ ApplyWorkspaceEditParams(edit: workspaceEdit),
+ );
+ receivedApplyEditResult = ApplyWorkspaceEditResult.fromJson(
+ result.result as Map,
+ );
+ return null;
+ }, applyEditResult: applyEditResultToSend);
+
+ return (verifier, receivedApplyEditResult!);
+ }
+}
diff --git a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
index 110d47eebd4c..f9f13647c4ce 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
@@ -288,7 +288,7 @@ void main() {
expect(reporter.errors.first, equals('params.kind must not be null'));
});
- test('canParse records fields of the wrong type', () {
+ test('canParse records fields of the wrong type (non-spec types)', () {
var reporter = LspJsonReporter('params');
expect(RenameFileOptions.canParse({'overwrite': 1}, reporter), isFalse);
expect(reporter.errors, hasLength(1));
@@ -298,6 +298,20 @@ void main() {
);
});
+ test('canParse records fields of the wrong type (spec types)', () {
+ var reporter = LspJsonReporter('params');
+ expect(
+ ClientCapabilities.canParse({'textDocument': 1}, reporter),
+ isFalse,
+ );
+ expect(
+ reporter.errors.single,
+ equals(
+ 'params.textDocument must be of type TextDocumentClientCapabilities',
+ ),
+ );
+ });
+
test('canParse records nested undefined fields', () {
var reporter = LspJsonReporter('params');
expect(
diff --git a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
index 1b45c0d502ee..befdd2ab1bff 100644
--- a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
+++ b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
@@ -514,8 +514,15 @@ void _writeCanParseType(
buffer
..write(') {')
- ..indent()
- ..writeIndentedln('reporter.reportError($quote$failureMessage$quote);')
+ ..indent();
+ if (!_isSpecType(type)) {
+ // Only report an error for non-spec types, as spec types will have reported
+ // their own error in the nested canParse() call.
+ buffer.writeIndentedln(
+ 'reporter.reportError($quote$failureMessage$quote);',
+ );
+ }
+ buffer
..writeIndentedln('return false;')
..outdent()
..writeIndentedln('}')
diff --git a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
index 8e3d1c4c92c9..fb9a9ce657ab 100644
--- a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
+++ b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
@@ -876,6 +876,11 @@ public interface AnalysisServer {
*
* Call an LSP handler. Message can be requests or notifications.
*
+ * This request can be called in either direction, either by the client to the server, or by the
+ * server to the client. The server will only call the client if the client has indicated it
+ * supports the associated LSP request via lspCapabilities
in the
+ * setClientCapabilities
request.
+ *
* @param lspMessage The LSP RequestMessage.
*/
public void lsp_handle(Object lspMessage, HandleConsumer consumer);
@@ -1041,8 +1046,13 @@ public interface AnalysisServer {
* can fetch the file contents for URIs with custom schemes (and receive modification
* events) through the LSP protocol (see the "lsp" domain). LSP notifications are
* automatically enabled when the client sets this capability.
+ * @param lspCapabilities LSP capabilities of the client as defined by the Language Server Protocol
+ * specification. If custom LSP capabilities are to be used, the setClientCapabilities
+ * request should be called before any LSP requests are made to the server. If LSP
+ * capabilities are not provided or no setClientCapabilities request is made, a very basic
+ * set of capabilities will be assumed.
*/
- public void server_setClientCapabilities(List requests, boolean supportsUris);
+ public void server_setClientCapabilities(List requests, boolean supportsUris, Object lspCapabilities);
/**
* {@code server.setSubscriptions}
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index 6d11b0caf7d7..d89c3d279c2c 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -7,7 +7,7 @@
Analysis Server API Specification
Version
- 1.38.0
+ 1.39.0
This document contains a specification of the API provided by the
@@ -142,6 +142,10 @@
Enumerations
ignoring the item or treating it with some default/fallback handling.
Changelog
+1.39.0
+
+ Added a new lspCapabilities field to the setClientCapabilities parameters to allow clients to provide their LSP capabilities. These capabilities can indicate that a client can support lsp.handle requests in the server-to-client direction.
+
1.38.0
Deprecated the analytics.enable request.
@@ -427,6 +431,18 @@ Options
LSP notifications are automatically enabled when the client sets this capability.
+
+ [object]
+
+ LSP capabilities of the client as defined by the Language Server Protocol specification.
+
+
+ If custom LSP capabilities are to be used, the setClientCapabilities request should be called before any LSP requests are made to the server.
+
+
+ If LSP capabilities are not provided or no setClientCapabilities request is made, a very basic set of capabilities will be assumed.
+
+
@@ -3436,6 +3452,9 @@ Options
Call an LSP handler. Message can be requests or notifications.
+
+ This request can be called in either direction, either by the client to the server, or by the server to the client. The server will only call the client if the client has indicated it supports the associated LSP request via lspCapabilities in the setClientCapabilities request.
+
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 09438d53f604..686521972db4 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.38.0';
+const String PROTOCOL_VERSION = '1.39.0';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
@@ -335,6 +335,8 @@ const String SERVER_REQUEST_OPEN_URL_REQUEST = 'server.openUrlRequest';
const String SERVER_REQUEST_OPEN_URL_REQUEST_URL = 'url';
const String SERVER_REQUEST_SET_CLIENT_CAPABILITIES =
'server.setClientCapabilities';
+const String SERVER_REQUEST_SET_CLIENT_CAPABILITIES_LSP_CAPABILITIES =
+ 'lspCapabilities';
const String SERVER_REQUEST_SET_CLIENT_CAPABILITIES_REQUESTS = 'requests';
const String SERVER_REQUEST_SET_CLIENT_CAPABILITIES_SUPPORTS_URIS =
'supportsUris';
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
index 3eb9742df69b..0fb6a270db1c 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
@@ -14859,6 +14859,7 @@ enum ServerService {
/// {
/// "requests": List
/// "supportsUris": optional bool
+/// "lspCapabilities": optional object
/// }
///
/// Clients may not extend, implement or mix-in this class.
@@ -14891,7 +14892,18 @@ class ServerSetClientCapabilitiesParams implements RequestParams {
/// capability.
bool? supportsUris;
- ServerSetClientCapabilitiesParams(this.requests, {this.supportsUris});
+ /// LSP capabilities of the client as defined by the Language Server Protocol
+ /// specification.
+ ///
+ /// If custom LSP capabilities are to be used, the setClientCapabilities
+ /// request should be called before any LSP requests are made to the server.
+ ///
+ /// If LSP capabilities are not provided or no setClientCapabilities request
+ /// is made, a very basic set of capabilities will be assumed.
+ Object? lspCapabilities;
+
+ ServerSetClientCapabilitiesParams(this.requests,
+ {this.supportsUris, this.lspCapabilities});
factory ServerSetClientCapabilitiesParams.fromJson(
JsonDecoder jsonDecoder, String jsonPath, Object? json) {
@@ -14909,8 +14921,12 @@ class ServerSetClientCapabilitiesParams implements RequestParams {
supportsUris = jsonDecoder.decodeBool(
'$jsonPath.supportsUris', json['supportsUris']);
}
+ Object? lspCapabilities;
+ if (json.containsKey('lspCapabilities')) {
+ lspCapabilities = json['lspCapabilities'] as Object;
+ }
return ServerSetClientCapabilitiesParams(requests,
- supportsUris: supportsUris);
+ supportsUris: supportsUris, lspCapabilities: lspCapabilities);
} else {
throw jsonDecoder.mismatch(
jsonPath, 'server.setClientCapabilities params', json);
@@ -14930,6 +14946,10 @@ class ServerSetClientCapabilitiesParams implements RequestParams {
if (supportsUris != null) {
result['supportsUris'] = supportsUris;
}
+ var lspCapabilities = this.lspCapabilities;
+ if (lspCapabilities != null) {
+ result['lspCapabilities'] = lspCapabilities;
+ }
return result;
}
@@ -14946,7 +14966,8 @@ class ServerSetClientCapabilitiesParams implements RequestParams {
if (other is ServerSetClientCapabilitiesParams) {
return listEqual(
requests, other.requests, (String a, String b) => a == b) &&
- supportsUris == other.supportsUris;
+ supportsUris == other.supportsUris &&
+ lspCapabilities == other.lspCapabilities;
}
return false;
}
@@ -14955,6 +14976,7 @@ class ServerSetClientCapabilitiesParams implements RequestParams {
int get hashCode => Object.hash(
Object.hashAll(requests),
supportsUris,
+ lspCapabilities,
);
}
diff --git a/pkg/analysis_server_plugin/test/src/plugin_server_error_test.dart b/pkg/analysis_server_plugin/test/src/plugin_server_error_test.dart
index bb165b081d5f..d3d9d8831575 100644
--- a/pkg/analysis_server_plugin/test/src/plugin_server_error_test.dart
+++ b/pkg/analysis_server_plugin/test/src/plugin_server_error_test.dart
@@ -232,7 +232,6 @@ class _ThrowsAsyncErrorVisitor extends SimpleAstVisitor {
void visitBooleanLiteral(BooleanLiteral node) {
// Raise an async error that can only be caught by an error zone's `onError`
// handler.
- // ignore: unawaited_futures
Future.error(StateError('A message.'));
}
}
diff --git a/pkg/analyzer/lib/dart/element/element2.dart b/pkg/analyzer/lib/dart/element/element2.dart
index 55ec4f875078..3f36d780a1bf 100644
--- a/pkg/analyzer/lib/dart/element/element2.dart
+++ b/pkg/analyzer/lib/dart/element/element2.dart
@@ -111,6 +111,9 @@ abstract class Annotatable {
abstract class BindPatternVariableElement2 implements PatternVariableElement2 {
@override
BindPatternVariableFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The portion of a [BindPatternVariableElement2] contributed by a single
@@ -138,6 +141,9 @@ abstract class ClassElement2 implements InterfaceElement2 {
@override
ClassFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the class or its superclass declares a non-final instance field.
bool get hasNonFinalField;
@@ -256,7 +262,8 @@ abstract class ClassFragment implements InterfaceFragment {
/// type.
///
/// Clients may not extend, implement or mix-in this class.
-abstract class ConstructorElement2 implements ExecutableElement2 {
+abstract class ConstructorElement2
+ implements ExecutableElement2, HasSinceSdkVersion {
@override
ConstructorElement2 get baseElement;
@@ -266,6 +273,9 @@ abstract class ConstructorElement2 implements ExecutableElement2 {
@override
ConstructorFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the constructor is a const constructor.
bool get isConst;
@@ -405,6 +415,9 @@ abstract class Element2 {
/// invocations of [Fragment.nextFragment].
Fragment get firstFragment;
+ /// The fragments this element consists of.
+ List get fragments;
+
/// The unique integer identifier of this element.
int get id;
@@ -607,6 +620,9 @@ abstract class EnumElement2 implements InterfaceElement2 {
@override
EnumFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The portion of an [EnumElement2] contributed by a single declaration.
@@ -637,6 +653,9 @@ abstract class ExecutableElement2 implements FunctionTypedElement2 {
@override
ExecutableFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the executable element did not have an explicit return type
/// specified for it in the original source.
bool get hasImplicitReturnType;
@@ -711,6 +730,9 @@ abstract class ExtensionElement2 implements InstanceElement2 {
@override
ExtensionFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The portion of an [ExtensionElement2] contributed by a single
@@ -735,6 +757,9 @@ abstract class ExtensionTypeElement2 implements InterfaceElement2 {
@override
ExtensionTypeFragment get firstFragment;
+ @override
+ List get fragments;
+
/// The primary constructor of this extension.
ConstructorElement2 get primaryConstructor2;
@@ -780,6 +805,9 @@ abstract class FieldElement2 implements PropertyInducingElement2 {
@override
FieldFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the field is abstract.
///
/// Executable fields are abstract if they are declared with the `abstract`
@@ -810,6 +838,9 @@ abstract class FieldFormalParameterElement2 implements FormalParameterElement {
@override
FieldFormalParameterFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The portion of a [FieldFormalParameterElement2] contributed by a single
@@ -845,7 +876,11 @@ abstract class FieldFragment implements PropertyInducingFragment {
///
/// Clients may not extend, implement or mix-in this class.
abstract class FormalParameterElement
- implements PromotableElement2, Annotatable, LocalElement2 {
+ implements
+ PromotableElement2,
+ Annotatable,
+ HasSinceSdkVersion,
+ LocalElement2 {
@override
FormalParameterElement get baseElement;
@@ -863,6 +898,9 @@ abstract class FormalParameterElement
/// formal parameter.
List get formalParameters;
+ @override
+ List get fragments;
+
/// Whether the parameter has a default value.
bool get hasDefaultValue;
@@ -1046,6 +1084,9 @@ abstract class FunctionTypedElement2 implements TypeParameterizedElement2 {
/// The formal parameters defined by this element.
List get formalParameters;
+ @override
+ List get fragments;
+
/// The return type defined by this element.
DartType get returnType;
@@ -1076,6 +1117,9 @@ abstract class FunctionTypedFragment implements TypeParameterizedFragment {
abstract class GenericFunctionTypeElement2 implements FunctionTypedElement2 {
@override
GenericFunctionTypeFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The portion of a [GenericFunctionTypeElement2] coming from a single
@@ -1112,6 +1156,9 @@ abstract class GetterElement implements PropertyAccessorElement2 {
@override
GetterFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The portion of a [GetterElement] contributed by a single declaration.
@@ -1140,11 +1187,35 @@ abstract class GetterFragment implements PropertyAccessorFragment {
// GetterElement get element;
}
+/// The interface that is implemented by elements that can have `@Since()`
+/// annotation.
+abstract class HasSinceSdkVersion {
+ /// The version where the associated SDK API was added.
+ ///
+ /// A `@Since()` annotation can be applied to a library declaration,
+ /// any public declaration in a library, or in a class, or to an optional
+ /// parameter, etc.
+ ///
+ /// The returned version is "effective", so that if a library is annotated
+ /// then all elements of the library inherit it; or if a class is annotated
+ /// then all members and constructors of the class inherit it.
+ ///
+ /// If multiple `@Since()` annotations apply to the same element, the latest
+ /// version takes precedence.
+ ///
+ /// Returns `null` if the element is not declared in the SDK, or doesn't have
+ /// a `@Since()` annotation applied to it.
+ Version? get sinceSdkVersion;
+}
+
/// An element whose instance members can refer to `this`.
///
/// Clients may not extend, implement or mix-in this class.
abstract class InstanceElement2
- implements TypeDefiningElement2, TypeParameterizedElement2 {
+ implements
+ TypeDefiningElement2,
+ TypeParameterizedElement2,
+ HasSinceSdkVersion {
@override
LibraryElement2 get enclosingElement2;
@@ -1154,6 +1225,9 @@ abstract class InstanceElement2
@override
InstanceFragment get firstFragment;
+ @override
+ List get fragments;
+
/// The getters declared in this element.
List get getters2;
@@ -1268,6 +1342,9 @@ abstract class InterfaceElement2 implements InstanceElement2 {
@override
InterfaceFragment get firstFragment;
+ @override
+ List get fragments;
+
/// The interfaces that are implemented by this class.
///
/// Note: Because the element model represents the state of the code,
@@ -1408,6 +1485,9 @@ abstract class JoinPatternVariableElement2 implements PatternVariableElement2 {
@override
JoinPatternVariableFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the [variables2] are consistent.
///
/// The variables are consistent if they are present in all branches, and have
@@ -1453,6 +1533,9 @@ abstract class LabelElement2 implements Element2 {
@override
LabelFragment get firstFragment;
+ @override
+ List get fragments;
+
@override
LibraryElement2 get library2;
}
@@ -1474,7 +1557,8 @@ abstract class LabelFragment implements Fragment {
/// A library.
///
/// Clients may not extend, implement or mix-in this class.
-abstract class LibraryElement2 implements Element2, Annotatable {
+abstract class LibraryElement2
+ implements Element2, Annotatable, HasSinceSdkVersion {
/// The classes defined in this library.
///
/// There is no guarantee of the order in which the classes will be returned.
@@ -1532,6 +1616,7 @@ abstract class LibraryElement2 implements Element2, Annotatable {
///
/// This includes the defining fragment, and fragments included using the
/// `part` directive.
+ @override
List get fragments;
/// The getters defined in this library.
@@ -1796,6 +1881,9 @@ abstract class LocalFunctionElement
implements ExecutableElement2, LocalElement2 {
@override
LocalFunctionFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The portion of a [LocalFunctionElement] contributed by a single
@@ -1834,6 +1922,9 @@ abstract class LocalVariableElement2
@override
LocalVariableFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the variable has an initializer at declaration.
bool get hasInitializer;
}
@@ -1958,23 +2049,6 @@ abstract class Metadata {
/// Whether the receiver has an annotation of the form `@widgetFactory`.
bool get hasWidgetFactory;
-
- /// The version where the associated SDK API was added.
- ///
- /// A `@Since()` annotation can be applied to a library declaration,
- /// any public declaration in a library, or in a class, or to an optional
- /// parameter, etc.
- ///
- /// The returned version is "effective", so that if a library is annotated
- /// then all elements of the library inherit it; or if a class is annotated
- /// then all members and constructors of the class inherit it.
- ///
- /// If multiple `@Since()` annotations apply to the same element, the latest
- /// version takes precedence.
- ///
- /// Returns `null` if the element is not declared in the SDK, or doesn't have
- /// a `@Since()` annotation applied to it.
- Version? get sinceSdkVersion;
}
/// A method.
@@ -1983,7 +2057,8 @@ abstract class Metadata {
/// method.
///
/// Clients may not extend, implement or mix-in this class.
-abstract class MethodElement2 implements ExecutableElement2 {
+abstract class MethodElement2
+ implements ExecutableElement2, HasSinceSdkVersion {
/// The name of the method that can be implemented by a class to allow its
/// instances to be invoked as if they were a function.
static final String CALL_METHOD_NAME = "call";
@@ -1998,6 +2073,9 @@ abstract class MethodElement2 implements ExecutableElement2 {
@override
MethodFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the method defines an operator.
///
/// The test might be based on the name of the executable element, in which
@@ -2029,6 +2107,9 @@ abstract class MixinElement2 implements InterfaceElement2 {
@override
MixinFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the mixin is a base mixin.
///
/// A mixin is a base mixin if it has an explicit `base` modifier.
@@ -2091,6 +2172,9 @@ abstract class MultiplyDefinedElement2 implements Element2 {
@override
MultiplyDefinedFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The fragment for a [MultiplyDefinedElement2].
@@ -2125,6 +2209,9 @@ abstract class PatternVariableElement2 implements LocalVariableElement2 {
@override
PatternVariableFragment get firstFragment;
+ @override
+ List get fragments;
+
/// The variable in which this variable joins with other pattern variables
/// with the same name, in a logical-or pattern, or shared case scope.
JoinPatternVariableElement2? get join2;
@@ -2161,6 +2248,9 @@ abstract class PrefixElement2 implements Element2 {
@override
PrefixFragment get firstFragment;
+ @override
+ List get fragments;
+
/// The imports that share this prefix.
List get imports;
@@ -2202,6 +2292,9 @@ abstract class PrefixFragment implements Fragment {
abstract class PromotableElement2 implements VariableElement2 {
@override
PromotableFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The portion of a [PromotableElement2] contributed by a single declaration.
@@ -2232,6 +2325,9 @@ abstract class PropertyAccessorElement2 implements ExecutableElement2 {
@override
PropertyAccessorFragment get firstFragment;
+ @override
+ List get fragments;
+
/// The field or top-level variable associated with this getter.
///
/// If this getter was explicitly defined (is not synthetic) then the variable
@@ -2277,10 +2373,13 @@ abstract class PropertyAccessorFragment implements ExecutableFragment {
///
/// Clients may not extend, implement or mix-in this class.
abstract class PropertyInducingElement2
- implements VariableElement2, Annotatable {
+ implements VariableElement2, Annotatable, HasSinceSdkVersion {
@override
PropertyInducingFragment get firstFragment;
+ @override
+ List get fragments;
+
/// The getter associated with this variable.
///
/// If this variable was explicitly defined (is not synthetic) then the
@@ -2375,6 +2474,9 @@ abstract class SetterElement implements PropertyAccessorElement2 {
@override
SetterFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The portion of a [SetterElement] contributed by a single declaration.
@@ -2412,6 +2514,9 @@ abstract class SuperFormalParameterElement2 implements FormalParameterElement {
@override
SuperFormalParameterFragment get firstFragment;
+ @override
+ List get fragments;
+
/// The associated super-constructor parameter, from the super-constructor
/// that is referenced by the implicit or explicit super-constructor
/// invocation.
@@ -2439,13 +2544,17 @@ abstract class SuperFormalParameterFragment implements FormalParameterFragment {
/// A top-level function.
///
/// Clients may not extend, implement or mix-in this class.
-abstract class TopLevelFunctionElement implements ExecutableElement2 {
+abstract class TopLevelFunctionElement
+ implements ExecutableElement2, HasSinceSdkVersion {
@override
TopLevelFunctionElement get baseElement;
@override
TopLevelFunctionFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the function represents `identical` from the `dart:core` library.
bool get isDartCoreIdentical;
@@ -2489,6 +2598,9 @@ abstract class TopLevelVariableElement2 implements PropertyInducingElement2 {
@override
TopLevelVariableFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the field was explicitly marked as being external.
bool get isExternal;
}
@@ -2512,7 +2624,10 @@ abstract class TopLevelVariableFragment implements PropertyInducingFragment {
///
/// Clients may not extend, implement or mix-in this class.
abstract class TypeAliasElement2
- implements TypeParameterizedElement2, TypeDefiningElement2 {
+ implements
+ TypeParameterizedElement2,
+ TypeDefiningElement2,
+ HasSinceSdkVersion {
/// If the aliased type has structure, return the corresponding element.
/// For example, it could be [GenericFunctionTypeElement].
///
@@ -2532,6 +2647,9 @@ abstract class TypeAliasElement2
@override
TypeAliasFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Returns the type resulting from instantiating this typedef with the given
/// [typeArguments] and [nullabilitySuffix].
///
@@ -2575,6 +2693,9 @@ abstract class TypeDefiningElement2 implements Element2, Annotatable {
@override
TypeDefiningFragment get firstFragment;
+
+ @override
+ List get fragments;
}
/// The portion of a [TypeDefiningElement2] contributed by a single declaration.
@@ -2608,6 +2729,9 @@ abstract class TypeParameterElement2 implements TypeDefiningElement2 {
@override
TypeParameterFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Returns the [TypeParameterType] with the given [nullabilitySuffix] for
/// this type parameter.
TypeParameterType instantiate({
@@ -2637,6 +2761,9 @@ abstract class TypeParameterizedElement2 implements Element2, Annotatable {
@override
TypeParameterizedFragment get firstFragment;
+ @override
+ List get fragments;
+
/// If the element defines a type, indicates whether the type may safely
/// appear without explicit type arguments as the bounds of a type parameter
/// declaration.
@@ -2684,6 +2811,9 @@ abstract class VariableElement2 implements Element2 {
@override
VariableFragment get firstFragment;
+ @override
+ List get fragments;
+
/// Whether the variable element did not have an explicit type specified
/// for it.
bool get hasImplicitType;
diff --git a/pkg/analyzer/lib/dart/element/type.dart b/pkg/analyzer/lib/dart/element/type.dart
index f7f1f51b6ea5..d1b1d13bc4f5 100644
--- a/pkg/analyzer/lib/dart/element/type.dart
+++ b/pkg/analyzer/lib/dart/element/type.dart
@@ -28,13 +28,14 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type_visitor.dart';
-import 'package:analyzer/src/dart/element/type.dart' show RecordTypeImpl;
+import 'package:analyzer/src/dart/element/type.dart'
+ show RecordTypeImpl, TypeImpl;
import 'package:meta/meta.dart';
/// The type associated with elements in the element model.
///
/// Clients may not extend, implement or mix-in this class.
-abstract class DartType implements SharedTypeStructure {
+abstract class DartType implements SharedTypeStructure {
/// If this type is an instantiation of a type alias, information about
/// the alias element, and the type arguments.
/// Otherwise return `null`.
@@ -208,6 +209,15 @@ abstract class DartType implements SharedTypeStructure {
@Deprecated('Only non-nullable by default mode is supported')
bool withNullability = true,
});
+
+ /// Determines whether this type is the same as [other].
+ ///
+ /// Deprecated: this getter is a part of the analyzer's private
+ /// implementation, and was exposed by accident (see
+ /// https://github.com/dart-lang/sdk/issues/59763). Please use `==` instead.
+ @override
+ @Deprecated('Use `==` instead')
+ bool isStructurallyEqualTo(covariant DartType other);
}
/// The type `dynamic` is a type which is a supertype of all other types, just
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 4ff1baa06a79..537185f91b17 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -99,7 +99,7 @@ import 'package:meta/meta.dart';
// TODO(scheglov): Clean up the list of implicitly analyzed files.
class AnalysisDriver {
/// The version of data format, should be incremented on every format change.
- static const int DATA_VERSION = 426;
+ static const int DATA_VERSION = 427;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
index 3c4b256845d4..7f78b8407ea2 100644
--- a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
@@ -12,7 +12,7 @@ part of 'experiments.dart';
/// The current version of the Dart language (or, for non-stable releases, the
/// version of the language currently in the process of being developed).
-const _currentVersion = '3.7.0';
+const _currentVersion = '3.8.0';
/// A map containing information about all known experimental flags.
final _knownFeatures = {
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index eeef7d7a8ade..331906c73202 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -9,6 +9,7 @@ import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/source/file_source.dart';
import 'package:analyzer/src/context/source.dart';
import 'package:analyzer/src/dart/analysis/analysis_options.dart';
import 'package:analyzer/src/dart/analysis/file_analysis.dart';
@@ -350,16 +351,22 @@ class LibraryAnalyzer {
_checkForInconsistentLanguageVersionOverride();
+ var validateUnnecessaryIgnores =
+ _analysisOptions.isLintEnabled('unnecessary_ignore');
+
// This must happen after all other diagnostics have been computed but
// before the list of diagnostics has been filtered.
- for (var fileAnalysis in _libraryFiles.values) {
+ for (var fileAnalysis in _libraryFiles.values
+ // Only validate non-generated files.
+ .whereNot((f) => f.file.source.isGenerated)) {
IgnoreValidator(
- fileAnalysis.errorReporter,
- fileAnalysis.errorListener.errors,
- fileAnalysis.ignoreInfo,
- fileAnalysis.unit.lineInfo,
- _analysisOptions.unignorableNames,
- ).reportErrors();
+ fileAnalysis.errorReporter,
+ fileAnalysis.errorListener.errors,
+ fileAnalysis.ignoreInfo,
+ fileAnalysis.unit.lineInfo,
+ _analysisOptions.unignorableNames,
+ validateUnnecessaryIgnores)
+ .reportErrors();
}
}
@@ -713,7 +720,7 @@ class LibraryAnalyzer {
} else if (state is LibraryImportWithFile && !state.importedFile.exists) {
var errorCode = state.isDocImport
? WarningCode.URI_DOES_NOT_EXIST_IN_DOC_IMPORT
- : isGeneratedSource(state.importedSource)
+ : state.importedSource.isGenerated
? CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED
: CompileTimeErrorCode.URI_DOES_NOT_EXIST;
errorReporter.atNode(
@@ -1148,3 +1155,7 @@ extension on file_state.DirectiveUri {
return DirectiveUriImpl();
}
}
+
+extension on FileSource {
+ bool get isGenerated => isGeneratedSource(this);
+}
diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart
index 2a415d5c7a8f..3157ea6a4dfa 100644
--- a/pkg/analyzer/lib/src/dart/ast/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// ignore_for_file: analyzer_use_new_elements
-
/// @docImport 'package:analyzer/src/lint/linter.dart';
/// @docImport 'package:analyzer/src/lint/linter_visitor.dart';
library;
@@ -2349,7 +2347,7 @@ class NodeReplacer extends ThrowingAstVisitor {
return true;
} else if (identical(node.defaultValue, _oldNode)) {
node.defaultValue = _newNode as ExpressionImpl;
- var parameterElement = node.declaredElement;
+ var parameterElement = node.declaredFragment;
if (parameterElement is DefaultParameterElementImpl) {
parameterElement.constantInitializer = _newNode;
} else if (parameterElement is DefaultFieldFormalParameterElementImpl) {
diff --git a/pkg/analyzer/lib/src/dart/element/class_hierarchy.dart b/pkg/analyzer/lib/src/dart/element/class_hierarchy.dart
index 6ceeb88fe9cf..ef38831b1564 100644
--- a/pkg/analyzer/lib/src/dart/element/class_hierarchy.dart
+++ b/pkg/analyzer/lib/src/dart/element/class_hierarchy.dart
@@ -76,7 +76,7 @@ class ClassHierarchy {
}
append(element.supertype);
- if (augmented is AugmentedMixinElement) {
+ if (augmented is MixinElementImpl2) {
for (var type in augmented.superclassConstraints) {
append(type);
}
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 4efd68c9edb8..16d0ae1d5fd2 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -127,12 +127,12 @@ class BindPatternVariableElementImpl extends PatternVariableElementImpl
super.element as BindPatternVariableElementImpl2;
@override
- BindPatternVariableFragment? get nextFragment =>
- super.nextFragment as BindPatternVariableFragment?;
+ BindPatternVariableElementImpl? get nextFragment =>
+ super.nextFragment as BindPatternVariableElementImpl?;
@override
- BindPatternVariableFragment? get previousFragment =>
- super.previousFragment as BindPatternVariableFragment?;
+ BindPatternVariableElementImpl? get previousFragment =>
+ super.previousFragment as BindPatternVariableElementImpl?;
}
class BindPatternVariableElementImpl2 extends PatternVariableElementImpl2
@@ -140,8 +140,18 @@ class BindPatternVariableElementImpl2 extends PatternVariableElementImpl2
BindPatternVariableElementImpl2(super._wrappedElement);
@override
- BindPatternVariableFragment get firstFragment =>
- super.firstFragment as BindPatternVariableFragment;
+ BindPatternVariableElementImpl get firstFragment =>
+ super.firstFragment as BindPatternVariableElementImpl;
+
+ @override
+ List get fragments {
+ return [
+ for (BindPatternVariableElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
/// This flag is set to `true` if this variable clashes with another
/// pattern variable with the same name within the same pattern.
@@ -451,7 +461,7 @@ class ClassElementImpl extends ClassOrMixinElementImpl
}
@override
- ClassFragment? get nextFragment => super.nextFragment as ClassFragment?;
+ ClassElementImpl? get nextFragment => super.nextFragment as ClassElementImpl?;
@override
ClassFragment? get previousFragment =>
@@ -653,6 +663,16 @@ class ClassElementImpl2 extends InterfaceElementImpl2
firstFragment.augmentedInternal = this;
}
+ @override
+ List get fragments {
+ return [
+ for (ClassElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get hasNonFinalField => firstFragment.hasNonFinalField;
@@ -1520,7 +1540,7 @@ class ConstructorElementImpl extends ExecutableElementImpl
}
@override
- ConstructorFragment? get nextFragment => augmentation;
+ ConstructorElementImpl? get nextFragment => augmentation;
@override
Element get nonSynthetic {
@@ -1541,15 +1561,15 @@ class ConstructorElementImpl extends ExecutableElementImpl
}
@override
- InterfaceType get returnType {
+ InterfaceTypeImpl get returnType {
var result = _returnType;
if (result != null) {
- return result as InterfaceType;
+ return result as InterfaceTypeImpl;
}
var augmentedDeclaration = enclosingElement3.augmented.firstFragment;
result = augmentedDeclaration.thisType;
- return _returnType = result as InterfaceType;
+ return _returnType = result as InterfaceTypeImpl;
}
@override
@@ -1612,7 +1632,8 @@ class ConstructorElementImpl2 extends ExecutableElementImpl2
FragmentedFunctionTypedElementMixin,
FragmentedTypeParameterizedElementMixin,
FragmentedAnnotatableElementMixin,
- FragmentedElementMixin
+ FragmentedElementMixin,
+ _HasSinceSdkVersionMixin
implements ConstructorElement2 {
@override
final String? name3;
@@ -1624,7 +1645,7 @@ class ConstructorElementImpl2 extends ExecutableElementImpl2
ConstructorElementImpl? fragment = firstFragment;
while (fragment != null) {
fragment.element = this;
- fragment = fragment.nextFragment as ConstructorElementImpl?;
+ fragment = fragment.nextFragment;
}
}
@@ -1646,6 +1667,16 @@ class ConstructorElementImpl2 extends ExecutableElementImpl2
InterfaceElementImpl2 get enclosingElement2 =>
firstFragment.enclosingElement3.element;
+ @override
+ List get fragments {
+ return [
+ for (ConstructorElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get isConst => firstFragment.isConst;
@@ -1661,6 +1692,11 @@ class ConstructorElementImpl2 extends ExecutableElementImpl2
@override
ElementKind get kind => ElementKind.CONSTRUCTOR;
+ @override
+ ConstructorElementImpl get lastFragment {
+ return super.lastFragment as ConstructorElementImpl;
+ }
+
@override
Element2 get nonSynthetic2 {
if (isSynthetic) {
@@ -1727,6 +1763,9 @@ mixin ConstructorElementMixin implements ConstructorElement {
bool get isGenerative {
return !isFactory;
}
+
+ @override
+ InterfaceTypeImpl get returnType;
}
/// A [TopLevelVariableElement] for a top-level 'const' variable that has an
@@ -2061,6 +2100,16 @@ class DynamicElementImpl2 extends TypeDefiningElementImpl2 {
@override
DynamicElementImpl get firstFragment => DynamicElementImpl.instance;
+ @override
+ List get fragments {
+ return [
+ for (DynamicElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get isSynthetic => true;
@@ -2072,7 +2121,7 @@ class DynamicElementImpl2 extends TypeDefiningElementImpl2 {
@override
Metadata get metadata2 {
- return MetadataImpl(0, const [], () => null);
+ return MetadataImpl(0, const []);
}
@override
@@ -2502,14 +2551,6 @@ abstract class ElementImpl implements Element, Element2 {
static const _metadataFlag_hasDeprecated = 1 << 1;
static const _metadataFlag_hasOverride = 1 << 2;
- /// Cached values for [sinceSdkVersion].
- ///
- /// Only very few elements have `@Since()` annotations, so instead of adding
- /// an instance field to [ElementImpl], we attach this information this way.
- /// We ask it only when [Modifier.HAS_SINCE_SDK_VERSION_VALUE] is `true`, so
- /// don't pay for a hash lookup when we know that the result is `null`.
- static final Expando _sinceSdkVersion = Expando();
-
static int _NEXT_ID = 0;
@override
@@ -2616,6 +2657,16 @@ abstract class ElementImpl implements Element, Element2 {
throw UnimplementedError('This is a fragment');
}
+ @override
+ List get fragments {
+ return [
+ for (Fragment? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get hasAlwaysThrows {
var metadata = this.metadata;
@@ -3005,8 +3056,7 @@ abstract class ElementImpl implements Element, Element2 {
_metadata = metadata;
}
- Metadata get metadata2 =>
- MetadataImpl(_getMetadataFlags(), metadata, () => sinceSdkVersion);
+ Metadata get metadata2 => MetadataImpl(_getMetadataFlags(), metadata);
@override
String? get name => _name;
@@ -3044,18 +3094,7 @@ abstract class ElementImpl implements Element, Element2 {
@override
Version? get sinceSdkVersion {
- if (!hasModifier(Modifier.HAS_SINCE_SDK_VERSION_COMPUTED)) {
- setModifier(Modifier.HAS_SINCE_SDK_VERSION_COMPUTED, true);
- var result = SinceSdkVersionComputer().compute(this);
- if (result != null) {
- _sinceSdkVersion[this] = result;
- setModifier(Modifier.HAS_SINCE_SDK_VERSION_VALUE, true);
- }
- }
- if (hasModifier(Modifier.HAS_SINCE_SDK_VERSION_VALUE)) {
- return _sinceSdkVersion[this];
- }
- return null;
+ return asElement2.ifTypeOrNull()?.sinceSdkVersion;
}
@override
@@ -3310,6 +3349,9 @@ abstract class ElementImpl2 implements Element2 {
@override
final int id = ElementImpl._NEXT_ID++;
+ /// The modifiers associated with this element.
+ EnumSet _modifiers = EnumSet.empty();
+
@override
Element2 get baseElement => this;
@@ -3323,6 +3365,16 @@ abstract class ElementImpl2 implements Element2 {
// TODO(augmentations): implement enclosingElement2
Element2? get enclosingElement2 => throw UnimplementedError();
+ @override
+ List get fragments {
+ return [
+ for (Fragment? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
/// Return an identifier that uniquely identifies this element among the
/// children of this element's parent.
String get identifier {
@@ -3396,6 +3448,9 @@ abstract class ElementImpl2 implements Element2 {
return "$shortName (${source?.fullName})";
}
+ /// Whether this element has the [modifier].
+ bool hasModifier(Modifier modifier) => _modifiers[modifier];
+
@override
bool isAccessibleIn2(LibraryElement2 library) {
var name3 = this.name3;
@@ -3405,6 +3460,11 @@ abstract class ElementImpl2 implements Element2 {
return true;
}
+ /// Update [modifier] of this element to [value].
+ void setModifier(Modifier modifier, bool value) {
+ _modifiers = _modifiers.updated(modifier, value);
+ }
+
@override
Element2? thisOrAncestorMatching2(bool Function(Element2 p1) predicate) {
Element2? element = this;
@@ -3590,10 +3650,11 @@ class EnumElementImpl extends InterfaceElementImpl
ElementKind get kind => ElementKind.ENUM;
@override
- EnumFragment? get nextFragment => super.nextFragment as EnumFragment?;
+ EnumElementImpl? get nextFragment => super.nextFragment as EnumElementImpl?;
@override
- EnumFragment? get previousFragment => super.previousFragment as EnumFragment?;
+ EnumElementImpl? get previousFragment =>
+ super.previousFragment as EnumElementImpl?;
ConstFieldElementImpl? get valuesField {
for (var field in fields) {
@@ -3639,6 +3700,16 @@ class EnumElementImpl2 extends InterfaceElementImpl2
List get constants2 =>
constants.map((e) => e.asElement2).toList();
+ @override
+ List get fragments {
+ return [
+ for (EnumElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
T? accept2(ElementVisitor2 visitor) {
return visitor.visitEnumElement(this);
@@ -3654,7 +3725,7 @@ abstract class ExecutableElementImpl extends _ExistingElementImpl
List _parameters = const [];
/// The inferred return type of this executable element.
- DartType? _returnType;
+ TypeImpl? _returnType;
/// The type of function defined by this executable element.
FunctionType? _type;
@@ -3792,13 +3863,15 @@ abstract class ExecutableElementImpl extends _ExistingElementImpl
}
@override
- DartType get returnType {
+ TypeImpl get returnType {
linkedData?.read(this);
return _returnType!;
}
set returnType(DartType returnType) {
- _returnType = returnType;
+ // TODO(paulberry): eliminate this cast by changing the setter parameter
+ // type to `TypeImpl`.
+ _returnType = returnType as TypeImpl;
// We do this because of return type inference. At the moment when we
// create a local function element we don't know yet its return type,
// because we have not done static type analysis yet.
@@ -3855,6 +3928,17 @@ abstract class ExecutableElementImpl2 extends FunctionTypedElementImpl2
return firstFragment.hasModifier(Modifier.INVOKES_SUPER_SELF);
}
+ ExecutableElementImpl get lastFragment {
+ var result = firstFragment as ExecutableElementImpl;
+ while (true) {
+ if (result.nextFragment case ExecutableElementImpl nextFragment) {
+ result = nextFragment;
+ } else {
+ return result;
+ }
+ }
+ }
+
@override
LibraryElement2 get library2 {
var firstFragment = this.firstFragment as ExecutableElementImpl;
@@ -3927,12 +4011,12 @@ class ExtensionElementImpl extends InstanceElementImpl
}
@override
- ExtensionFragment? get nextFragment =>
- super.nextFragment as ExtensionFragment?;
+ ExtensionElementImpl? get nextFragment =>
+ super.nextFragment as ExtensionElementImpl?;
@override
- ExtensionFragment? get previousFragment =>
- super.previousFragment as ExtensionFragment?;
+ ExtensionElementImpl? get previousFragment =>
+ super.previousFragment as ExtensionElementImpl?;
@override
DartType get thisType => extendedType;
@@ -3987,6 +4071,7 @@ class ExtensionElementImpl extends InstanceElementImpl
}
class ExtensionElementImpl2 extends InstanceElementImpl2
+ with _HasSinceSdkVersionMixin
implements AugmentedExtensionElement, ExtensionElement2 {
@override
final Reference reference;
@@ -4002,6 +4087,16 @@ class ExtensionElementImpl2 extends InstanceElementImpl2
firstFragment.augmentedInternal = this;
}
+ @override
+ List get fragments {
+ return [
+ for (ExtensionElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
DartType get thisType => extendedType;
@@ -4048,12 +4143,12 @@ class ExtensionTypeElementImpl extends InterfaceElementImpl
}
@override
- ExtensionTypeFragment? get nextFragment =>
- super.nextFragment as ExtensionTypeFragment?;
+ ExtensionTypeElementImpl? get nextFragment =>
+ super.nextFragment as ExtensionTypeElementImpl?;
@override
- ExtensionTypeFragment? get previousFragment =>
- super.previousFragment as ExtensionTypeFragment?;
+ ExtensionTypeElementImpl? get previousFragment =>
+ super.previousFragment as ExtensionTypeElementImpl?;
@override
ConstructorElementImpl get primaryConstructor {
@@ -4110,6 +4205,16 @@ class ExtensionTypeElementImpl2 extends InterfaceElementImpl2
firstFragment.augmentedInternal = this;
}
+ @override
+ List get fragments {
+ return [
+ for (ExtensionTypeElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
ConstructorElement2 get primaryConstructor2 => primaryConstructor.element;
@@ -4225,11 +4330,11 @@ class FieldElementImpl extends PropertyInducingElementImpl
}
@override
- FieldFragment? get nextFragment => super.nextFragment as FieldFragment?;
+ FieldElementImpl? get nextFragment => super.nextFragment as FieldElementImpl?;
@override
- FieldFragment? get previousFragment =>
- super.previousFragment as FieldFragment?;
+ FieldElementImpl? get previousFragment =>
+ super.previousFragment as FieldElementImpl?;
@override
T? accept(ElementVisitor visitor) => visitor.visitFieldElement(this);
@@ -4238,7 +4343,8 @@ class FieldElementImpl extends PropertyInducingElementImpl
class FieldElementImpl2 extends PropertyInducingElementImpl2
with
FragmentedAnnotatableElementMixin,
- FragmentedElementMixin
+ FragmentedElementMixin,
+ _HasSinceSdkVersionMixin
implements FieldElement2 {
@override
final FieldElementImpl firstFragment;
@@ -4247,7 +4353,7 @@ class FieldElementImpl2 extends PropertyInducingElementImpl2
FieldElementImpl? fragment = firstFragment;
while (fragment != null) {
fragment.element = this;
- fragment = fragment.nextFragment as FieldElementImpl?;
+ fragment = fragment.nextFragment;
}
}
@@ -4258,6 +4364,16 @@ class FieldElementImpl2 extends PropertyInducingElementImpl2
InstanceElement2 get enclosingElement2 =>
(firstFragment._enclosingElement3 as InstanceFragment).element;
+ @override
+ List get fragments {
+ return [
+ for (FieldElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
GetterElement? get getter2 => firstFragment.getter?.element as GetterElement?;
@@ -4349,12 +4465,12 @@ class FieldFormalParameterElementImpl extends ParameterElementImpl
bool get isInitializingFormal => true;
@override
- FieldFormalParameterFragment? get nextFragment =>
- super.nextFragment as FieldFormalParameterFragment?;
+ FieldFormalParameterElementImpl? get nextFragment =>
+ super.nextFragment as FieldFormalParameterElementImpl?;
@override
- FieldFormalParameterFragment? get previousFragment =>
- super.previousFragment as FieldFormalParameterFragment?;
+ FieldFormalParameterElementImpl? get previousFragment =>
+ super.previousFragment as FieldFormalParameterElementImpl?;
@override
T? accept(ElementVisitor visitor) =>
@@ -4377,8 +4493,18 @@ class FieldFormalParameterElementImpl2 extends FormalParameterElementImpl
};
@override
- FieldFormalParameterFragment get firstFragment =>
- super.firstFragment as FieldFormalParameterFragment;
+ FieldFormalParameterElementImpl get firstFragment =>
+ super.firstFragment as FieldFormalParameterElementImpl;
+
+ @override
+ List get fragments {
+ return [
+ for (FieldFormalParameterElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
}
class FormalParameterElementImpl extends PromotableElementImpl2
@@ -4386,15 +4512,15 @@ class FormalParameterElementImpl extends PromotableElementImpl2
FragmentedAnnotatableElementMixin,
FragmentedElementMixin,
FormalParameterElementMixin,
- _NonTopLevelVariableOrParameter
- implements FormalParameterElementOrMember {
+ _HasSinceSdkVersionMixin,
+ _NonTopLevelVariableOrParameter {
final ParameterElementImpl wrappedElement;
FormalParameterElementImpl(this.wrappedElement) {
ParameterElementImpl? fragment = wrappedElement;
while (fragment != null) {
fragment.element = this;
- fragment = fragment.nextFragment as ParameterElementImpl?;
+ fragment = fragment.nextFragment;
}
}
@@ -4406,8 +4532,7 @@ class FormalParameterElementImpl extends PromotableElementImpl2
String? get defaultValueCode => wrappedElement.defaultValueCode;
@override
- FormalParameterFragment get firstFragment =>
- wrappedElement as FormalParameterFragment;
+ ParameterElementImpl get firstFragment => wrappedElement;
@override
// TODO(augmentations): Implement the merge of formal parameters.
@@ -4415,6 +4540,16 @@ class FormalParameterElementImpl extends PromotableElementImpl2
.map((fragment) => (fragment as ParameterElementImpl).element)
.toList();
+ @override
+ List get fragments {
+ return [
+ for (ParameterElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
// TODO(augmentations): Implement the merge of formal parameters.
bool get hasDefaultValue => wrappedElement.hasDefaultValue;
@@ -4488,14 +4623,14 @@ class FormalParameterElementImpl extends PromotableElementImpl2
@override
// TODO(augmentations): Implement the merge of formal parameters.
- DartType get type => wrappedElement.type;
+ TypeImpl get type => wrappedElement.type;
@override
// TODO(augmentations): Implement the merge of formal parameters.
List get typeParameters2 => const [];
@override
- DartType get typeShared => type;
+ TypeImpl get typeShared => type;
@override
Element? get _enclosingFunction => wrappedElement._enclosingElement3;
@@ -4522,7 +4657,10 @@ class FormalParameterElementImpl extends PromotableElementImpl2
/// A mixin that provides a common implementation for methods defined in
/// [FormalParameterElement].
-mixin FormalParameterElementMixin implements FormalParameterElement {
+mixin FormalParameterElementMixin
+ implements
+ FormalParameterElement,
+ SharedNamedFunctionParameterStructure {
@override
void appendToWithoutDelimiters2(StringBuffer buffer) {
buffer.write(
@@ -4537,11 +4675,6 @@ mixin FormalParameterElementMixin implements FormalParameterElement {
}
}
-abstract class FormalParameterElementOrMember
- implements
- FormalParameterElement,
- SharedNamedFunctionParameterStructure {}
-
mixin FragmentedAnnotatableElementMixin
implements FragmentedElementMixin {
String? get documentationComment {
@@ -4869,12 +5002,12 @@ mixin FragmentedAnnotatableElementMixin
return result;
}
- Metadata get metadata2 => MetadataImpl(
- -1, metadata.cast(), () => sinceSdkVersion);
+ Metadata get metadata2 =>
+ MetadataImpl(-1, metadata.cast());
Version? get sinceSdkVersion {
if (this is Element2) {
- return SinceSdkVersionComputer().compute2(this as Element2);
+ return SinceSdkVersionComputer().compute(this as Element2);
}
return null;
}
@@ -5089,10 +5222,10 @@ class FunctionElementImpl extends ExecutableElementImpl
ElementKind get kind => ElementKind.FUNCTION;
@override
- ExecutableFragment? get nextFragment => augmentation;
+ FunctionElementImpl? get nextFragment => augmentation;
@override
- ExecutableFragment? get previousFragment => augmentationTarget;
+ FunctionElementImpl? get previousFragment => augmentationTarget;
@override
bool get _includeNameOffsetInIdentifier {
@@ -5133,7 +5266,7 @@ class GenericFunctionTypeElementImpl extends _ExistingElementImpl
FunctionTypedElementImpl,
GenericFunctionTypeFragment {
/// The declared return type of the function.
- DartType? _returnType;
+ TypeImpl? _returnType;
/// The elements representing the parameters of the function.
List _parameters = const [];
@@ -5192,7 +5325,7 @@ class GenericFunctionTypeElementImpl extends _ExistingElementImpl
int? get nameOffset2 => null;
@override
- GenericFunctionTypeFragment? get nextFragment => null;
+ GenericFunctionTypeElementImpl? get nextFragment => null;
@override
List get parameters {
@@ -5209,10 +5342,10 @@ class GenericFunctionTypeElementImpl extends _ExistingElementImpl
}
@override
- GenericFunctionTypeFragment? get previousFragment => null;
+ GenericFunctionTypeElementImpl? get previousFragment => null;
@override
- DartType get returnType {
+ TypeImpl get returnType {
return _returnType!;
}
@@ -5220,7 +5353,9 @@ class GenericFunctionTypeElementImpl extends _ExistingElementImpl
/// [returnType].
@override
set returnType(DartType returnType) {
- _returnType = returnType;
+ // TODO(paulberry): eliminate this cast by changing the setter parameter
+ // type to `TypeImpl`.
+ _returnType = returnType as TypeImpl;
}
@override
@@ -5266,7 +5401,7 @@ class GenericFunctionTypeElementImpl2 extends FunctionTypedElementImpl2
String? get documentationComment => _wrappedElement.documentationComment;
@override
- GenericFunctionTypeFragment get firstFragment => _wrappedElement;
+ GenericFunctionTypeElementImpl get firstFragment => _wrappedElement;
@override
List get formalParameters =>
@@ -5274,6 +5409,16 @@ class GenericFunctionTypeElementImpl2 extends FunctionTypedElementImpl2
.map((fragment) => fragment.element)
.toList();
+ @override
+ List get fragments {
+ return [
+ for (GenericFunctionTypeElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get isSimplyBounded => _wrappedElement.isSimplyBounded;
@@ -5316,7 +5461,8 @@ class GetterElementImpl extends PropertyAccessorElementImpl2
FragmentedFunctionTypedElementMixin,
FragmentedTypeParameterizedElementMixin,
FragmentedAnnotatableElementMixin,
- FragmentedElementMixin
+ FragmentedElementMixin,
+ _HasSinceSdkVersionMixin
implements GetterElement {
@override
final PropertyAccessorElementImpl firstFragment;
@@ -5325,7 +5471,7 @@ class GetterElementImpl extends PropertyAccessorElementImpl2
PropertyAccessorElementImpl? fragment = firstFragment;
while (fragment != null) {
fragment.element = this;
- fragment = fragment.nextFragment as PropertyAccessorElementImpl?;
+ fragment = fragment.nextFragment;
}
}
@@ -5336,6 +5482,16 @@ class GetterElementImpl extends PropertyAccessorElementImpl2
SetterElement? get correspondingSetter2 =>
firstFragment.correspondingSetter2?.element as SetterElement?;
+ @override
+ List get fragments {
+ return [
+ for (PropertyAccessorElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
ElementKind get kind => ElementKind.GETTER;
@@ -5349,6 +5505,14 @@ class GetterElementImpl extends PropertyAccessorElementImpl2
throw StateError('Synthetic getter has no variable');
}
+ @override
+ Version? get sinceSdkVersion {
+ if (isSynthetic) {
+ return variable3?.sinceSdkVersion;
+ }
+ return super.sinceSdkVersion;
+ }
+
@override
T? accept2(ElementVisitor2 visitor) {
return visitor.visitGetterElement(this);
@@ -5413,6 +5577,11 @@ abstract class InstanceElementImpl extends _ExistingElementImpl
@override
List get accessors {
+ if (!identical(_accessors, _Sentinel.propertyAccessorElement)) {
+ return _accessors;
+ }
+
+ linkedData?.readMembers(this);
return _accessors;
}
@@ -5442,6 +5611,11 @@ abstract class InstanceElementImpl extends _ExistingElementImpl
@override
List get fields {
+ if (!identical(_fields, _Sentinel.fieldElement)) {
+ return _fields;
+ }
+
+ linkedData?.readMembers(this);
return _fields;
}
@@ -5470,6 +5644,11 @@ abstract class InstanceElementImpl extends _ExistingElementImpl
@override
List get methods {
+ if (!identical(_methods, _Sentinel.methodElement)) {
+ return _methods;
+ }
+
+ linkedData?.readMembers(this);
return _methods;
}
@@ -5516,8 +5695,7 @@ abstract class InstanceElementImpl2 extends ElementImpl2
@override
List methods = [];
- @override
- List methods2 = [];
+ final List internal_methods2 = [];
@override
InstanceElement2 get baseElement => this;
@@ -5542,18 +5720,23 @@ abstract class InstanceElementImpl2 extends ElementImpl2
LibraryElement2 get enclosingElement2 => firstFragment.library;
@override
- List get fields2 =>
- fields.map((e) => e.asElement2 as FieldElement2?).nonNulls.toList();
+ List get fields2 {
+ _readMembers();
+ return fields.map((e) => e.asElement2 as FieldElement2?).nonNulls.toList();
+ }
@override
InstanceElementImpl get firstFragment;
@override
- List get getters2 => accessors
- .where((e) => e.isGetter)
- .map((e) => e.asElement2 as GetterElement?)
- .nonNulls
- .toList();
+ List get getters2 {
+ _readMembers();
+ return accessors
+ .where((e) => e.isGetter)
+ .map((e) => e.asElement2 as GetterElement?)
+ .nonNulls
+ .toList();
+ }
@override
String get identifier => name3 ?? firstFragment.identifier;
@@ -5582,6 +5765,12 @@ abstract class InstanceElementImpl2 extends ElementImpl2
@override
Metadata get metadata2 => firstFragment.metadata2;
+ @override
+ List get methods2 {
+ _readMembers();
+ return internal_methods2;
+ }
+
@override
String? get name3 => firstFragment.name;
@@ -5593,17 +5782,18 @@ abstract class InstanceElementImpl2 extends ElementImpl2
AnalysisSession? get session => firstFragment.session;
@override
- List get setters2 => accessors
- .where((e) => e.isSetter)
- .map((e) => e.asElement2 as SetterElement?)
- .nonNulls
- .toList();
+ List get setters2 {
+ _readMembers();
+ return accessors
+ .where((e) => e.isSetter)
+ .map((e) => e.asElement2 as SetterElement?)
+ .nonNulls
+ .toList();
+ }
@override
List get typeParameters2 =>
- firstFragment.typeParameters
- .map((fragment) => fragment.element)
- .toList();
+ firstFragment.typeParameters.map((fragment) => fragment.element).toList();
@override
String displayString2(
@@ -5874,6 +6064,11 @@ abstract class InstanceElementImpl2 extends ElementImpl2
Iterable _implementationsOfSetter2(String name) {
return _implementationsOfSetter(name).map((e) => e.asElement2);
}
+
+ void _readMembers() {
+ // TODO(scheglov): use better implementation
+ firstFragment.element;
+ }
}
abstract class InterfaceElementImpl extends InstanceElementImpl
@@ -5898,14 +6093,6 @@ abstract class InterfaceElementImpl extends InstanceElementImpl
/// of this class have been inferred.
bool hasBeenInferred = false;
- /// The non-nullable instance of this element, without alias.
- /// Should be used only when the element has no type parameters.
- InterfaceTypeImpl? _nonNullableInstance;
-
- /// The nullable instance of this element, without alias.
- /// Should be used only when the element has no type parameters.
- InterfaceTypeImpl? _nullableInstance;
-
List _constructors = _Sentinel.constructorElement;
/// Initialize a newly created class element to have the given [name] at the
@@ -5925,7 +6112,7 @@ abstract class InterfaceElementImpl extends InstanceElementImpl
InterfaceElementImpl? get augmentationTarget;
@override
- AugmentedInterfaceElement get augmented;
+ InterfaceElementImpl2 get augmented;
@override
List get children => [
@@ -5944,6 +6131,7 @@ abstract class InterfaceElementImpl extends InstanceElementImpl
}
_buildMixinAppConstructors();
+ linkedData?.readMembers(this);
return _constructors;
}
@@ -6028,7 +6216,7 @@ abstract class InterfaceElementImpl extends InstanceElementImpl
}
@override
- InterfaceType get thisType {
+ InterfaceTypeImpl get thisType {
return augmented.thisType;
}
@@ -6081,43 +6269,10 @@ abstract class InterfaceElementImpl extends InstanceElementImpl
required List typeArguments,
required NullabilitySuffix nullabilitySuffix,
}) {
- assert(typeArguments.length == typeParameters.length);
-
- if (typeArguments.isEmpty) {
- switch (nullabilitySuffix) {
- case NullabilitySuffix.none:
- if (_nonNullableInstance case var instance?) {
- return instance;
- }
- case NullabilitySuffix.question:
- if (_nullableInstance case var instance?) {
- return instance;
- }
- case NullabilitySuffix.star:
- // TODO(scheglov): remove together with `star`
- break;
- }
- }
-
- var result = InterfaceTypeImpl(
- element: this,
+ return element.instantiate(
typeArguments: typeArguments,
nullabilitySuffix: nullabilitySuffix,
);
-
- if (typeArguments.isEmpty) {
- switch (nullabilitySuffix) {
- case NullabilitySuffix.none:
- _nonNullableInstance = result;
- case NullabilitySuffix.question:
- _nullableInstance = result;
- case NullabilitySuffix.star:
- // TODO(scheglov): remove together with `star`
- break;
- }
- }
-
- return result;
}
@override
@@ -6339,7 +6494,16 @@ abstract class InterfaceElementImpl extends InstanceElementImpl
}
abstract class InterfaceElementImpl2 extends InstanceElementImpl2
+ with _HasSinceSdkVersionMixin
implements AugmentedInterfaceElement, InterfaceElement2 {
+ /// The non-nullable instance of this element, without alias.
+ /// Should be used only when the element has no type parameters.
+ InterfaceTypeImpl? _nonNullableInstance;
+
+ /// The nullable instance of this element, without alias.
+ /// Should be used only when the element has no type parameters.
+ InterfaceTypeImpl? _nullableInstance;
+
@override
List interfaces = [];
@@ -6348,7 +6512,7 @@ abstract class InterfaceElementImpl2 extends InstanceElementImpl2
@override
List constructors = [];
- InterfaceType? _thisType;
+ InterfaceTypeImpl? _thisType;
@override
List get allSupertypes => firstFragment.allSupertypes;
@@ -6363,7 +6527,7 @@ abstract class InterfaceElementImpl2 extends InstanceElementImpl2
@override
List get constructors2 {
- firstFragment.constructors; // TODO(scheglov): remove eventually
+ _readMembers();
return constructors
.map((constructor) =>
(constructor.declaration as ConstructorElementImpl).element)
@@ -6398,7 +6562,7 @@ abstract class InterfaceElementImpl2 extends InstanceElementImpl2
InterfaceType? get supertype => firstFragment.supertype;
@override
- InterfaceType get thisType {
+ InterfaceTypeImpl get thisType {
if (_thisType == null) {
List typeArguments;
var typeParameters = firstFragment.typeParameters;
@@ -6438,12 +6602,48 @@ abstract class InterfaceElementImpl2 extends InstanceElementImpl2
}
@override
- InterfaceType instantiate({
+ InterfaceTypeImpl instantiate({
required List typeArguments,
required NullabilitySuffix nullabilitySuffix,
- }) =>
- firstFragment.instantiate(
- typeArguments: typeArguments, nullabilitySuffix: nullabilitySuffix);
+ }) {
+ assert(typeArguments.length == typeParameters2.length);
+
+ if (typeArguments.isEmpty) {
+ switch (nullabilitySuffix) {
+ case NullabilitySuffix.none:
+ if (_nonNullableInstance case var instance?) {
+ return instance;
+ }
+ case NullabilitySuffix.question:
+ if (_nullableInstance case var instance?) {
+ return instance;
+ }
+ case NullabilitySuffix.star:
+ // TODO(scheglov): remove together with `star`
+ break;
+ }
+ }
+
+ var result = InterfaceTypeImpl.v2(
+ element: this,
+ typeArguments: typeArguments,
+ nullabilitySuffix: nullabilitySuffix,
+ );
+
+ if (typeArguments.isEmpty) {
+ switch (nullabilitySuffix) {
+ case NullabilitySuffix.none:
+ _nonNullableInstance = result;
+ case NullabilitySuffix.question:
+ _nullableInstance = result;
+ case NullabilitySuffix.star:
+ // TODO(scheglov): remove together with `star`
+ break;
+ }
+ }
+
+ return result;
+ }
@override
MethodElement2? lookUpConcreteMethod(
@@ -6532,12 +6732,12 @@ class JoinPatternVariableElementImpl extends PatternVariableElementImpl
}
@override
- JoinPatternVariableFragment? get nextFragment =>
- super.nextFragment as JoinPatternVariableFragment?;
+ JoinPatternVariableElementImpl? get nextFragment =>
+ super.nextFragment as JoinPatternVariableElementImpl?;
@override
- JoinPatternVariableFragment? get previousFragment =>
- super.previousFragment as JoinPatternVariableFragment?;
+ JoinPatternVariableElementImpl? get previousFragment =>
+ super.previousFragment as JoinPatternVariableElementImpl?;
/// Returns this variable, and variables that join into it.
List get transitiveVariables {
@@ -6566,8 +6766,18 @@ class JoinPatternVariableElementImpl2 extends PatternVariableElementImpl2
JoinPatternVariableElementImpl2(super._wrappedElement);
@override
- JoinPatternVariableFragment get firstFragment =>
- super.firstFragment as JoinPatternVariableFragment;
+ JoinPatternVariableElementImpl get firstFragment =>
+ super.firstFragment as JoinPatternVariableElementImpl;
+
+ @override
+ List get fragments {
+ return [
+ for (JoinPatternVariableElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
shared.JoinedPatternVariableInconsistency get inconsistency =>
_wrappedElement.inconsistency;
@@ -6657,10 +6867,10 @@ class LabelElementImpl extends ElementImpl
int? get nameOffset2 => nameOffset;
@override
- LabelFragment? get nextFragment => null;
+ LabelElementImpl? get nextFragment => null;
@override
- LabelFragment? get previousFragment => null;
+ LabelElementImpl? get previousFragment => null;
@override
T? accept(ElementVisitor visitor) => visitor.visitLabelElement(this);
@@ -6681,7 +6891,12 @@ class LabelElementImpl2 extends ElementImpl2
ExecutableElement2? get enclosingElement2 => null;
@override
- LabelFragment get firstFragment => _wrappedElement;
+ LabelElementImpl get firstFragment => _wrappedElement;
+
+ @override
+ List get fragments {
+ return [firstFragment];
+ }
@override
LibraryElement2 get library2 {
@@ -7028,6 +7243,11 @@ class LibraryElementImpl extends ElementImpl
return declarations.toList();
}
+ @override
+ Version? get sinceSdkVersion {
+ return SinceSdkVersionComputer().compute(this);
+ }
+
@override
Source get source {
return _definingCompilationUnit.source;
@@ -7414,6 +7634,16 @@ class LocalFunctionElementImpl extends ExecutableElementImpl2
.map((fragment) => fragment.element)
.toList();
+ @override
+ List get fragments {
+ return [
+ for (FunctionElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get hasImplicitReturnType => _wrappedElement.hasImplicitReturnType;
@@ -7516,10 +7746,10 @@ class LocalVariableElementImpl extends NonParameterVariableElementImpl
int? get nameOffset2 => nameOffset;
@override
- LocalVariableFragment? get nextFragment => null;
+ LocalVariableElementImpl? get nextFragment => null;
@override
- LocalVariableFragment? get previousFragment => null;
+ LocalVariableElementImpl? get previousFragment => null;
@override
T? accept(ElementVisitor visitor) =>
@@ -7541,7 +7771,12 @@ class LocalVariableElementImpl2 extends PromotableElementImpl2
String? get documentationComment => null;
@override
- LocalVariableFragment get firstFragment => _wrappedElement;
+ LocalVariableElementImpl get firstFragment => _wrappedElement;
+
+ @override
+ List get fragments {
+ return [firstFragment];
+ }
@override
bool get hasImplicitType => _wrappedElement.hasImplicitType;
@@ -7628,10 +7863,7 @@ final class MetadataImpl implements Metadata {
@override
final List annotations;
- final Version? Function() _sinceSdkVersionComputer;
-
- MetadataImpl(
- this._metadataFlags, this.annotations, this._sinceSdkVersionComputer);
+ MetadataImpl(this._metadataFlags, this.annotations);
@override
bool get hasAlwaysThrows {
@@ -7989,9 +8221,6 @@ final class MetadataImpl implements Metadata {
}
return false;
}
-
- @override
- Version? get sinceSdkVersion => _sinceSdkVersionComputer();
}
/// A concrete implementation of a [MethodElement].
@@ -8072,7 +8301,7 @@ class MethodElementImpl extends ExecutableElementImpl
}
@override
- MethodFragment? get nextFragment => augmentation;
+ MethodElementImpl? get nextFragment => augmentation;
@override
Element get nonSynthetic {
@@ -8083,7 +8312,7 @@ class MethodElementImpl extends ExecutableElementImpl
}
@override
- MethodFragment? get previousFragment => augmentationTarget;
+ MethodElementImpl? get previousFragment => augmentationTarget;
@override
T? accept(ElementVisitor visitor) => visitor.visitMethodElement(this);
@@ -8095,7 +8324,8 @@ class MethodElementImpl2 extends ExecutableElementImpl2
FragmentedFunctionTypedElementMixin,
FragmentedTypeParameterizedElementMixin,
FragmentedAnnotatableElementMixin,
- FragmentedElementMixin
+ FragmentedElementMixin,
+ _HasSinceSdkVersionMixin
implements MethodElement2 {
@override
final Reference reference;
@@ -8118,12 +8348,27 @@ class MethodElementImpl2 extends ExecutableElementImpl2
Element2? get enclosingElement2 =>
(firstFragment._enclosingElement3 as InstanceFragment).element;
+ @override
+ List get fragments {
+ return [
+ for (MethodElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get isOperator => firstFragment.isOperator;
@override
ElementKind get kind => ElementKind.METHOD;
+ @override
+ MethodElementImpl get lastFragment {
+ return super.lastFragment as MethodElementImpl;
+ }
+
@override
String? get lookupName {
if (name3 == '-' && formalParameters.isEmpty) {
@@ -8188,11 +8433,11 @@ class MixinElementImpl extends ClassOrMixinElementImpl
}
@override
- MixinFragment? get nextFragment => super.nextFragment as MixinFragment?;
+ MixinElementImpl? get nextFragment => super.nextFragment as MixinElementImpl?;
@override
- MixinFragment? get previousFragment =>
- super.previousFragment as MixinFragment?;
+ MixinElementImpl? get previousFragment =>
+ super.previousFragment as MixinElementImpl?;
@override
List get superclassConstraints {
@@ -8247,6 +8492,16 @@ class MixinElementImpl2 extends InterfaceElementImpl2
firstFragment.augmentedInternal = this;
}
+ @override
+ List get fragments {
+ return [
+ for (MixinElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get isBase => firstFragment.isBase;
@@ -8661,7 +8916,8 @@ class MultiplyDefinedElementImpl2 extends ElementImpl2
final List conflictingElements2;
@override
- late final firstFragment = MultiplyDefinedFragmentImpl(this);
+ late final MultiplyDefinedFragmentImpl firstFragment =
+ MultiplyDefinedFragmentImpl(this);
MultiplyDefinedElementImpl2(
this.libraryFragment,
@@ -8689,6 +8945,11 @@ class MultiplyDefinedElementImpl2 extends ElementImpl2
@override
Null get enclosingElement2 => null;
+ @override
+ List get fragments {
+ return [firstFragment];
+ }
+
@override
bool get isPrivate => false;
@@ -8892,6 +9153,11 @@ class NeverElementImpl2 extends TypeDefiningElementImpl2 {
@override
NeverElementImpl get firstFragment => NeverElementImpl.instance;
+ @override
+ List get fragments {
+ return [firstFragment];
+ }
+
@override
bool get isSynthetic => true;
@@ -8903,7 +9169,7 @@ class NeverElementImpl2 extends TypeDefiningElementImpl2 {
@override
Metadata get metadata2 {
- return MetadataImpl(0, const [], () => null);
+ return MetadataImpl(0, const []);
}
@override
@@ -9083,7 +9349,7 @@ class ParameterElementImpl extends VariableElementImpl
@override
// TODO(augmentations): Support chaining between the fragments.
- FormalParameterFragment? get nextFragment => null;
+ ParameterElementImpl? get nextFragment => null;
@override
List get parameters {
@@ -9101,7 +9367,7 @@ class ParameterElementImpl extends VariableElementImpl
@override
// TODO(augmentations): Support chaining between the fragments.
- FormalParameterFragment? get previousFragment => null;
+ ParameterElementImpl? get previousFragment => null;
@override
List get typeParameters {
@@ -9187,7 +9453,7 @@ class ParameterElementImpl_ofImplicitSetter extends ParameterElementImpl {
}
@override
- DartType get type => setter.variable2.type;
+ TypeImpl get type => setter.variable2.type;
@override
set type(DartType type) {
@@ -9312,12 +9578,12 @@ class PatternVariableElementImpl extends LocalVariableElementImpl
JoinPatternVariableFragment? get join2 => join;
@override
- PatternVariableFragment? get nextFragment =>
- super.nextFragment as PatternVariableFragment?;
+ PatternVariableElementImpl? get nextFragment =>
+ super.nextFragment as PatternVariableElementImpl?;
@override
- PatternVariableFragment? get previousFragment =>
- super.previousFragment as PatternVariableFragment?;
+ PatternVariableElementImpl? get previousFragment =>
+ super.previousFragment as PatternVariableElementImpl?;
/// Return the root [join], or self.
PatternVariableElementImpl get rootVariable {
@@ -9330,8 +9596,18 @@ class PatternVariableElementImpl2 extends LocalVariableElementImpl2
PatternVariableElementImpl2(super._wrappedElement);
@override
- PatternVariableFragment get firstFragment =>
- super.firstFragment as PatternVariableFragment;
+ PatternVariableElementImpl get firstFragment =>
+ super.firstFragment as PatternVariableElementImpl;
+
+ @override
+ List get fragments {
+ return [
+ for (PatternVariableElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
/// This flag is set to `true` while we are visiting the [WhenClause] of
/// the [GuardedPattern] that declares this variable.
@@ -9447,6 +9723,7 @@ class PrefixElementImpl2 extends ElementImpl2 implements PrefixElement2 {
@override
Null get enclosingElement2 => null;
+ @override
List get fragments {
return [
for (PrefixFragmentImpl? fragment = firstFragment;
@@ -9637,7 +9914,7 @@ class PropertyAccessorElementImpl extends ExecutableElementImpl
PropertyAccessorElementImpl firstFragment = this;
var previousFragment = firstFragment.previousFragment;
while (previousFragment != null) {
- firstFragment = previousFragment as PropertyAccessorElementImpl;
+ firstFragment = previousFragment;
previousFragment = firstFragment.previousFragment;
}
// As a side-effect of creating the element, all of the fragments in the
@@ -9717,10 +9994,10 @@ class PropertyAccessorElementImpl extends ExecutableElementImpl
}
@override
- PropertyAccessorFragment? get nextFragment => augmentation;
+ PropertyAccessorElementImpl? get nextFragment => augmentation;
@override
- PropertyAccessorFragment? get previousFragment => augmentationTarget;
+ PropertyAccessorElementImpl? get previousFragment => augmentationTarget;
@override
PropertyInducingElementImpl? get variable2 {
@@ -9762,6 +10039,11 @@ abstract class PropertyAccessorElementImpl2 extends ExecutableElementImpl2
@override
bool get isExternal => firstFragment.isExternal;
+ @override
+ PropertyAccessorElementImpl get lastFragment {
+ return super.lastFragment as PropertyAccessorElementImpl;
+ }
+
@override
String? get name3 => firstFragment.name2;
@@ -9805,7 +10087,7 @@ class PropertyAccessorElementImpl_ImplicitGetter
}
@override
- DartType get returnType => variable2.type;
+ TypeImpl get returnType => variable2.type;
@override
set returnType(DartType returnType) {
@@ -9871,7 +10153,7 @@ class PropertyAccessorElementImpl_ImplicitSetter
}
@override
- DartType get returnType => VoidTypeImpl.instance;
+ TypeImpl get returnType => VoidTypeImpl.instance;
@override
set returnType(DartType returnType) {
@@ -10010,7 +10292,7 @@ abstract class PropertyInducingElementImpl
}
@override
- DartType get type {
+ TypeImpl get type {
linkedData?.read(this);
if (_type != null) return _type!;
@@ -10018,7 +10300,7 @@ abstract class PropertyInducingElementImpl
if (getter != null) {
return _type = getter!.returnType;
} else if (setter != null) {
- List parameters = setter!.parameters;
+ var parameters = setter!.parameters;
return _type = parameters.isNotEmpty
? parameters[0].type
: DynamicTypeImpl.instance;
@@ -10108,7 +10390,7 @@ abstract class PropertyInducingElementImpl2 extends VariableElementImpl2
/// Instances of this class are set for fields and top-level variables
/// to perform top-level type inference during linking.
abstract class PropertyInducingElementTypeInference {
- DartType perform();
+ TypeImpl perform();
}
class SetterElementImpl extends PropertyAccessorElementImpl2
@@ -10117,7 +10399,8 @@ class SetterElementImpl extends PropertyAccessorElementImpl2
FragmentedFunctionTypedElementMixin,
FragmentedTypeParameterizedElementMixin,
FragmentedAnnotatableElementMixin,
- FragmentedElementMixin
+ FragmentedElementMixin,
+ _HasSinceSdkVersionMixin
implements SetterElement {
@override
final PropertyAccessorElementImpl firstFragment;
@@ -10126,7 +10409,7 @@ class SetterElementImpl extends PropertyAccessorElementImpl2
PropertyAccessorElementImpl? fragment = firstFragment;
while (fragment != null) {
fragment.element = this;
- fragment = fragment.nextFragment as PropertyAccessorElementImpl?;
+ fragment = fragment.nextFragment;
}
}
@@ -10140,6 +10423,16 @@ class SetterElementImpl extends PropertyAccessorElementImpl2
@override
Element2? get enclosingElement2 => firstFragment.enclosingFragment?.element;
+ @override
+ List get fragments {
+ return [
+ for (PropertyAccessorElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
ElementKind get kind => ElementKind.SETTER;
@@ -10161,6 +10454,14 @@ class SetterElementImpl extends PropertyAccessorElementImpl2
throw StateError('Synthetic setter has no variable');
}
+ @override
+ Version? get sinceSdkVersion {
+ if (isSynthetic) {
+ return variable3?.sinceSdkVersion;
+ }
+ return super.sinceSdkVersion;
+ }
+
@override
T? accept2(ElementVisitor2 visitor) {
return visitor.visitSetterElement(this);
@@ -10216,12 +10517,12 @@ class SuperFormalParameterElementImpl extends ParameterElementImpl
bool get isSuperFormal => true;
@override
- SuperFormalParameterFragment? get nextFragment =>
- super.nextFragment as SuperFormalParameterFragment?;
+ SuperFormalParameterElementImpl? get nextFragment =>
+ super.nextFragment as SuperFormalParameterElementImpl?;
@override
- SuperFormalParameterFragment? get previousFragment =>
- super.previousFragment as SuperFormalParameterFragment?;
+ SuperFormalParameterElementImpl? get previousFragment =>
+ super.previousFragment as SuperFormalParameterElementImpl?;
@override
ParameterElement? get superConstructorParameter {
@@ -10272,6 +10573,16 @@ class SuperFormalParameterElementImpl2 extends FormalParameterElementImpl
SuperFormalParameterElementImpl get firstFragment =>
super.firstFragment as SuperFormalParameterElementImpl;
+ @override
+ List get fragments {
+ return [
+ for (SuperFormalParameterElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
FormalParameterElement? get superConstructorParameter2 {
return firstFragment.superConstructorParameter?.asElement2;
@@ -10292,7 +10603,8 @@ class TopLevelFunctionElementImpl extends ExecutableElementImpl2
FragmentedFunctionTypedElementMixin,
FragmentedTypeParameterizedElementMixin,
FragmentedAnnotatableElementMixin,
- FragmentedElementMixin
+ FragmentedElementMixin,
+ _HasSinceSdkVersionMixin
implements TopLevelFunctionElement {
@override
final Reference reference;
@@ -10311,6 +10623,16 @@ class TopLevelFunctionElementImpl extends ExecutableElementImpl2
@override
Element2? get enclosingElement2 => firstFragment._enclosingElement3?.library2;
+ @override
+ List get fragments {
+ return [
+ for (FunctionElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get isDartCoreIdentical => firstFragment.isDartCoreIdentical;
@@ -10320,6 +10642,11 @@ class TopLevelFunctionElementImpl extends ExecutableElementImpl2
@override
ElementKind get kind => ElementKind.FUNCTION;
+ @override
+ FunctionElementImpl get lastFragment {
+ return super.lastFragment as FunctionElementImpl;
+ }
+
@override
LibraryElement2 get library2 {
return firstFragment.library2!;
@@ -10371,12 +10698,12 @@ class TopLevelVariableElementImpl extends PropertyInducingElementImpl
}
@override
- TopLevelVariableFragment? get nextFragment =>
- super.nextFragment as TopLevelVariableFragment?;
+ TopLevelVariableElementImpl? get nextFragment =>
+ super.nextFragment as TopLevelVariableElementImpl?;
@override
- TopLevelVariableFragment? get previousFragment =>
- super.previousFragment as TopLevelVariableFragment?;
+ TopLevelVariableElementImpl? get previousFragment =>
+ super.previousFragment as TopLevelVariableElementImpl?;
@override
T? accept(ElementVisitor visitor) =>
@@ -10386,7 +10713,8 @@ class TopLevelVariableElementImpl extends PropertyInducingElementImpl
class TopLevelVariableElementImpl2 extends PropertyInducingElementImpl2
with
FragmentedAnnotatableElementMixin,
- FragmentedElementMixin
+ FragmentedElementMixin,
+ _HasSinceSdkVersionMixin
implements TopLevelVariableElement2 {
@override
final Reference reference;
@@ -10406,6 +10734,16 @@ class TopLevelVariableElementImpl2 extends PropertyInducingElementImpl2
LibraryElement2 get enclosingElement2 =>
firstFragment.library as LibraryElement2;
+ @override
+ List get fragments {
+ return [
+ for (TopLevelVariableElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
GetterElement? get getter2 =>
firstFragment.getter2?.element as GetterElement?;
@@ -10588,11 +10926,11 @@ class TypeAliasElementImpl extends _ExistingElementImpl
@override
// TODO(augmentations): Support the fragment chain.
- TypeAliasFragment? get nextFragment => null;
+ TypeAliasElementImpl? get nextFragment => null;
@override
// TODO(augmentations): Support the fragment chain.
- TypeAliasFragment? get previousFragment => null;
+ TypeAliasElementImpl? get previousFragment => null;
/// Instantiates this type alias with its type parameters as arguments.
DartType get rawType {
@@ -10623,7 +10961,7 @@ class TypeAliasElementImpl extends _ExistingElementImpl
}
@override
- DartType instantiate({
+ TypeImpl instantiate({
required List typeArguments,
required NullabilitySuffix nullabilitySuffix,
}) {
@@ -10707,7 +11045,8 @@ class TypeAliasElementImpl extends _ExistingElementImpl
class TypeAliasElementImpl2 extends TypeDefiningElementImpl2
with
FragmentedAnnotatableElementMixin,
- FragmentedElementMixin
+ FragmentedElementMixin,
+ _HasSinceSdkVersionMixin
implements TypeAliasElement2 {
@override
final Reference reference;
@@ -10741,6 +11080,16 @@ class TypeAliasElementImpl2 extends TypeDefiningElementImpl2
LibraryElement2 get enclosingElement2 =>
firstFragment.library as LibraryElement2;
+ @override
+ List get fragments {
+ return [
+ for (TypeAliasElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get isSimplyBounded => firstFragment.isSimplyBounded;
@@ -10793,7 +11142,7 @@ class TypeParameterElementImpl extends ElementImpl
/// The type representing the bound associated with this parameter, or `null`
/// if this parameter does not have an explicit bound.
- DartType? _bound;
+ TypeImpl? _bound;
/// The value representing the variance modifier keyword, or `null` if
/// there is no explicit variance modifier, meaning legacy covariance.
@@ -10813,12 +11162,14 @@ class TypeParameterElementImpl extends ElementImpl
}
@override
- DartType? get bound {
+ TypeImpl? get bound {
return _bound;
}
set bound(DartType? bound) {
- _bound = bound;
+ // TODO(paulberry): Change the type of the parameter `bound` so that this
+ // cast isn't needed.
+ _bound = bound as TypeImpl?;
if (_element case var element?) {
if (!identical(element.bound, bound)) {
element.bound = bound;
@@ -10970,7 +11321,7 @@ class TypeParameterElementImpl2 extends TypeDefiningElementImpl2
FragmentedAnnotatableElementMixin,
FragmentedElementMixin,
_NonTopLevelVariableOrParameter
- implements TypeParameterElement2, SharedTypeParameterStructure {
+ implements TypeParameterElement2, SharedTypeParameterStructure {
@override
final TypeParameterElementImpl firstFragment;
@@ -10996,7 +11347,19 @@ class TypeParameterElementImpl2 extends TypeDefiningElementImpl2
TypeParameterElement2 get baseElement => this;
@override
- DartType? get boundShared => bound;
+ TypeImpl? get boundShared =>
+ // TODO(paulberry): get rid of this cast by changing the type of `bound`.
+ bound as TypeImpl?;
+
+ @override
+ List get fragments {
+ return [
+ for (TypeParameterElementImpl? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
bool get isLegacyCovariant => firstFragment.isLegacyCovariant;
@@ -11020,7 +11383,8 @@ class TypeParameterElementImpl2 extends TypeDefiningElementImpl2
TypeParameterType instantiate({
required NullabilitySuffix nullabilitySuffix,
}) {
- return firstFragment.instantiate(
+ return TypeParameterTypeImpl.v2(
+ element: this,
nullabilitySuffix: nullabilitySuffix,
);
}
@@ -11129,7 +11493,7 @@ abstract class UriReferencedElementImpl extends _ExistingElementImpl
abstract class VariableElementImpl extends ElementImpl
implements VariableElement {
/// The type of this variable.
- DartType? _type;
+ TypeImpl? _type;
/// Initialize a newly created variable element to have the given [name] and
/// [offset].
@@ -11221,10 +11585,12 @@ abstract class VariableElementImpl extends ElementImpl
String get name => super.name!;
@override
- DartType get type => _type!;
+ TypeImpl get type => _type!;
set type(DartType type) {
- _type = type;
+ // TODO(paulberry): eliminate this cast by changing the setter parameter
+ // type to `TypeImpl`.
+ _type = type as TypeImpl;
}
@override
@@ -11285,6 +11651,34 @@ mixin _HasLibraryMixin on ElementImpl {
Source get source => enclosingElement3!.source!;
}
+mixin _HasSinceSdkVersionMixin
+ on ElementImpl2, Annotatable
+ implements HasSinceSdkVersion {
+ /// Cached values for [sinceSdkVersion].
+ ///
+ /// Only very few elements have `@Since()` annotations, so instead of adding
+ /// an instance field to [ElementImpl2], we attach this information this way.
+ /// We ask it only when [Modifier.HAS_SINCE_SDK_VERSION_VALUE] is `true`, so
+ /// don't pay for a hash lookup when we know that the result is `null`.
+ static final Expando _sinceSdkVersion = Expando();
+
+ @override
+ Version? get sinceSdkVersion {
+ if (!hasModifier(Modifier.HAS_SINCE_SDK_VERSION_COMPUTED)) {
+ setModifier(Modifier.HAS_SINCE_SDK_VERSION_COMPUTED, true);
+ var result = SinceSdkVersionComputer().compute(this);
+ if (result != null) {
+ _sinceSdkVersion[this] = result;
+ setModifier(Modifier.HAS_SINCE_SDK_VERSION_VALUE, true);
+ }
+ }
+ if (hasModifier(Modifier.HAS_SINCE_SDK_VERSION_VALUE)) {
+ return _sinceSdkVersion[this];
+ }
+ return null;
+ }
+}
+
mixin _NonTopLevelVariableOrParameter on Element2 {
@override
Element2? get enclosingElement2 {
@@ -11352,7 +11746,7 @@ extension on Fragment {
Metadata get metadataOrEmpty {
return switch (this) {
Annotatable(:var metadata2) => metadata2,
- _ => MetadataImpl(-1, const [], () => null),
+ _ => MetadataImpl(-1, const []),
};
}
}
diff --git a/pkg/analyzer/lib/src/dart/element/extensions.dart b/pkg/analyzer/lib/src/dart/element/extensions.dart
index fa396172ea27..3c28355c0fbf 100644
--- a/pkg/analyzer/lib/src/dart/element/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/element/extensions.dart
@@ -187,6 +187,25 @@ extension ExecutableElementExtensionQuestion on ExecutableElement? {
}
}
+extension FormalParameterElementExtension on FormalParameterElement {
+ /// Returns [FormalParameterElementImpl] with the specified properties
+ /// replaced.
+ FormalParameterElementImpl copyWith({
+ DartType? type,
+ ParameterKind? kind,
+ bool? isCovariant,
+ }) {
+ var firstFragment = this.firstFragment as ParameterElement;
+ return FormalParameterElementImpl(
+ firstFragment.copyWith(
+ type: type,
+ kind: kind,
+ isCovariant: isCovariant,
+ ),
+ );
+ }
+}
+
extension InterfaceTypeExtension on InterfaceType {
bool get isDartCoreObjectNone {
return isDartCoreObject && nullabilitySuffix == NullabilitySuffix.none;
diff --git a/pkg/analyzer/lib/src/dart/element/greatest_lower_bound.dart b/pkg/analyzer/lib/src/dart/element/greatest_lower_bound.dart
index 70d4bb008e5a..78f59d87ae00 100644
--- a/pkg/analyzer/lib/src/dart/element/greatest_lower_bound.dart
+++ b/pkg/analyzer/lib/src/dart/element/greatest_lower_bound.dart
@@ -6,7 +6,6 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
-import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/extensions.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
@@ -27,7 +26,7 @@ class GreatestLowerBoundHelper {
///
/// https://github.com/dart-lang/language
/// See `resources/type-system/upper-lower-bounds.md`
- DartType getGreatestLowerBound(DartType T1, DartType T2) {
+ TypeImpl getGreatestLowerBound(TypeImpl T1, TypeImpl T2) {
// DOWN(T, T) = T
if (identical(T1, T2)) {
return T1;
@@ -103,11 +102,8 @@ class GreatestLowerBoundHelper {
}
}
- var T1_impl = T1 as TypeImpl;
- var T2_impl = T2 as TypeImpl;
-
- var T1_nullability = T1_impl.nullabilitySuffix;
- var T2_nullability = T2_impl.nullabilitySuffix;
+ var T1_nullability = T1.nullabilitySuffix;
+ var T2_nullability = T2.nullabilitySuffix;
// DOWN(Null, T2)
if (T1_nullability == NullabilitySuffix.none && T1.isDartCoreNull) {
@@ -153,7 +149,7 @@ class GreatestLowerBoundHelper {
}
// * NonNull(T2) if NonNull(T2) is non-nullable
- var T2_nonNull = _typeSystem.promoteToNonNull(T2_impl);
+ var T2_nonNull = _typeSystem.promoteToNonNull(T2);
if (_typeSystem.isNonNullable(T2_nonNull)) {
return T2_nonNull;
}
@@ -170,7 +166,7 @@ class GreatestLowerBoundHelper {
}
// * NonNull(T1) if NonNull(T1) is non-nullable
- var T1_nonNull = _typeSystem.promoteToNonNull(T1_impl);
+ var T1_nonNull = _typeSystem.promoteToNonNull(T1);
if (_typeSystem.isNonNullable(T1_nonNull)) {
return T1_nonNull;
}
@@ -184,12 +180,12 @@ class GreatestLowerBoundHelper {
// DOWN(T1, T2?) = S where S is DOWN(T1, T2)
if (T1_nullability != NullabilitySuffix.none ||
T2_nullability != NullabilitySuffix.none) {
- var T1_none = T1_impl.withNullability(NullabilitySuffix.none);
- var T2_none = T2_impl.withNullability(NullabilitySuffix.none);
+ var T1_none = T1.withNullability(NullabilitySuffix.none);
+ var T2_none = T2.withNullability(NullabilitySuffix.none);
var S = getGreatestLowerBound(T1_none, T2_none);
if (T1_nullability == NullabilitySuffix.question &&
T2_nullability == NullabilitySuffix.question) {
- return (S as TypeImpl).withNullability(NullabilitySuffix.question);
+ return S.withNullability(NullabilitySuffix.question);
}
return S;
}
@@ -258,7 +254,7 @@ class GreatestLowerBoundHelper {
///
/// https://github.com/dart-lang/language
/// See `resources/type-system/upper-lower-bounds.md`
- DartType _functionType(FunctionType f, FunctionType g) {
+ TypeImpl _functionType(FunctionTypeImpl f, FunctionTypeImpl g) {
var fTypeFormals = f.typeFormals;
var gTypeFormals = g.typeFormals;
@@ -378,7 +374,7 @@ class GreatestLowerBoundHelper {
);
}
- DartType _recordType(RecordTypeImpl T1, RecordTypeImpl T2) {
+ TypeImpl _recordType(RecordTypeImpl T1, RecordTypeImpl T2) {
var positional1 = T1.positionalFields;
var positional2 = T2.positionalFields;
if (positional1.length != positional2.length) {
diff --git a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
index c4b87a4eb81e..8a37ecb0f7bc 100644
--- a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
+++ b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
@@ -1071,6 +1071,11 @@ class InheritanceManager3 {
result.parameters = transformedParameters;
result.returnType = executable.returnType;
result.typeParameters = executable.typeParameters.cast();
+ result.element = MethodElementImpl2(
+ Reference.root(), // TODO(scheglov): wrong
+ executable.name,
+ result,
+ );
return result;
}
diff --git a/pkg/analyzer/lib/src/dart/element/least_greatest_closure.dart b/pkg/analyzer/lib/src/dart/element/least_greatest_closure.dart
index f0bceb82d60e..b9419a945cf4 100644
--- a/pkg/analyzer/lib/src/dart/element/least_greatest_closure.dart
+++ b/pkg/analyzer/lib/src/dart/element/least_greatest_closure.dart
@@ -47,17 +47,23 @@ class LeastGreatestClosureHelper extends ReplacementVisitor {
}
/// Returns a supertype of [type] for all values of [eliminationTargets].
- DartType eliminateToGreatest(DartType type) {
+ TypeImpl eliminateToGreatest(DartType type) {
_isCovariant = true;
_isLeastClosure = false;
- return type.accept(this) ?? type;
+ // TODO(paulberry): make this cast unnecessary by changing the type of
+ // `type` and by changing `ReplacementVisitor` to implement
+ // `TypeVisitor`.
+ return (type.accept(this) ?? type) as TypeImpl;
}
/// Returns a subtype of [type] for all values of [eliminationTargets].
- DartType eliminateToLeast(DartType type) {
+ TypeImpl eliminateToLeast(DartType type) {
_isCovariant = true;
_isLeastClosure = true;
- return type.accept(this) ?? type;
+ // TODO(paulberry): make this cast unnecessary by changing the type of
+ // `type` and by changing `ReplacementVisitor` to implement
+ // `TypeVisitor`.
+ return (type.accept(this) ?? type) as TypeImpl;
}
@override
diff --git a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
index d43cb0cd3297..898e4943ccf4 100644
--- a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
+++ b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
@@ -2,13 +2,11 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// ignore_for_file: analyzer_use_new_elements
-
import 'dart:math' show max;
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart'
show Variance;
-import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/extensions.dart';
@@ -61,10 +59,10 @@ class InterfaceLeastUpperBoundHelper {
return type1.withNullability(nullability);
}
- if (type1.element == type2.element) {
+ if (type1.element3 == type2.element3) {
var args1 = type1.typeArguments;
var args2 = type2.typeArguments;
- var params = type1.element.typeParameters;
+ var params = type1.element3.typeParameters2;
assert(args1.length == args2.length);
assert(args1.length == params.length);
@@ -93,8 +91,8 @@ class InterfaceLeastUpperBoundHelper {
}
}
- return InterfaceTypeImpl(
- element: type1.element,
+ return InterfaceTypeImpl.v2(
+ element: type1.element3,
typeArguments: args,
nullabilitySuffix: nullability,
);
@@ -124,7 +122,7 @@ class InterfaceLeastUpperBoundHelper {
return;
}
- if (type.element is ExtensionTypeElement) {
+ if (type.element3 is ExtensionTypeElement2) {
set.add(typeSystem.objectQuestion);
}
@@ -180,7 +178,7 @@ class InterfaceLeastUpperBoundHelper {
/// Object.
@visibleForTesting
static int computeLongestInheritancePathToObject(InterfaceType type) {
- return _computeLongestInheritancePathToObject(type, {});
+ return _computeLongestInheritancePathToObject(type, {});
}
static NullabilitySuffix _chooseNullability(
@@ -202,8 +200,8 @@ class InterfaceLeastUpperBoundHelper {
/// The set of [visitedElements] is used to prevent infinite recursion in the
/// case of a cyclic type structure.
static int _computeLongestInheritancePathToObject(
- InterfaceType type, Set visitedElements) {
- var element = type.element;
+ InterfaceType type, Set visitedElements) {
+ var element = type.element3;
// recursion
if (visitedElements.contains(element)) {
return 0;
@@ -213,14 +211,14 @@ class InterfaceLeastUpperBoundHelper {
return 1;
}
// Object case
- if (element is ClassElement) {
+ if (element is ClassElement2) {
if (element.isDartCoreObject) {
return type.nullabilitySuffix == NullabilitySuffix.none ? 1 : 0;
}
}
// Extension type without interfaces, implicit `Object?`
- if (element is ExtensionTypeElement) {
+ if (element is ExtensionTypeElement2) {
if (element.interfaces.isEmpty) {
return 1;
}
@@ -232,7 +230,7 @@ class InterfaceLeastUpperBoundHelper {
// loop through each of the superinterfaces recursively calling this
// method and keeping track of the longest path to return
- if (element is MixinElement) {
+ if (element is MixinElement2) {
for (InterfaceType interface in element.superclassConstraints) {
var pathLength = _computeLongestInheritancePathToObject(
interface, visitedElements);
@@ -248,7 +246,7 @@ class InterfaceLeastUpperBoundHelper {
longestPath = max(longestPath, 1 + pathLength);
}
- if (element is! ClassElement) {
+ if (element is! ClassElement2) {
return longestPath;
}
@@ -320,8 +318,8 @@ class LeastUpperBoundHelper {
LeastUpperBoundHelper(this._typeSystem);
- InterfaceType get _interfaceTypeFunctionNone {
- return _typeSystem.typeProvider.functionType.element.instantiate(
+ InterfaceTypeImpl get _interfaceTypeFunctionNone {
+ return _typeSystem.typeProvider.functionType.element3.instantiate(
typeArguments: const [],
nullabilitySuffix: NullabilitySuffix.none,
);
@@ -331,7 +329,7 @@ class LeastUpperBoundHelper {
///
/// https://github.com/dart-lang/language
/// See `resources/type-system/upper-lower-bounds.md`
- DartType getLeastUpperBound(DartType T1, DartType T2) {
+ TypeImpl getLeastUpperBound(TypeImpl T1, TypeImpl T2) {
// UP(T, T) = T
if (identical(T1, T2)) {
return T1;
@@ -441,11 +439,8 @@ class LeastUpperBoundHelper {
}
}
- var T1_impl = T1 as TypeImpl;
- var T2_impl = T2 as TypeImpl;
-
- var T1_nullability = T1_impl.nullabilitySuffix;
- var T2_nullability = T2_impl.nullabilitySuffix;
+ var T1_nullability = T1.nullabilitySuffix;
+ var T2_nullability = T2.nullabilitySuffix;
// UP(T1, T2) where NULL(T1)
if (T1_isNull) {
@@ -510,10 +505,10 @@ class LeastUpperBoundHelper {
// UP(T1, T2?) = S? where S is UP(T1, T2)
if (T1_nullability != NullabilitySuffix.none ||
T2_nullability != NullabilitySuffix.none) {
- var T1_none = T1_impl.withNullability(NullabilitySuffix.none);
- var T2_none = T2_impl.withNullability(NullabilitySuffix.none);
+ var T1_none = T1.withNullability(NullabilitySuffix.none);
+ var T2_none = T2.withNullability(NullabilitySuffix.none);
var S = getLeastUpperBound(T1_none, T2_none);
- return (S as TypeImpl).withNullability(NullabilitySuffix.question);
+ return S.withNullability(NullabilitySuffix.question);
}
assert(T1_nullability == NullabilitySuffix.none);
@@ -624,9 +619,9 @@ class LeastUpperBoundHelper {
///
/// https://github.com/dart-lang/language
/// See `resources/type-system/upper-lower-bounds.md`
- DartType _functionType(FunctionType f, FunctionType g) {
- var fTypeFormals = f.typeFormals;
- var gTypeFormals = g.typeFormals;
+ TypeImpl _functionType(FunctionTypeImpl f, FunctionTypeImpl g) {
+ var fTypeFormals = f.typeParameters;
+ var gTypeFormals = g.typeParameters;
// The number of type parameters must be the same.
// Otherwise the result is `Function`.
@@ -636,7 +631,10 @@ class LeastUpperBoundHelper {
// The bounds of type parameters must be equal.
// Otherwise the result is `Function`.
- var fresh = _typeSystem.relateTypeParameters(f.typeFormals, g.typeFormals);
+ var fresh = _typeSystem.relateTypeParameters2(
+ f.typeParameters,
+ g.typeParameters,
+ );
if (fresh == null) {
return _interfaceTypeFunctionNone;
}
@@ -644,10 +642,10 @@ class LeastUpperBoundHelper {
f = f.instantiate(fresh.typeParameterTypes);
g = g.instantiate(fresh.typeParameterTypes);
- var fParameters = f.parameters;
- var gParameters = g.parameters;
+ var fParameters = f.formalParameters;
+ var gParameters = g.formalParameters;
- var parameters = [];
+ var parameters = [];
var fIndex = 0;
var gIndex = 0;
while (fIndex < fParameters.length && gIndex < gParameters.length) {
@@ -679,7 +677,13 @@ class LeastUpperBoundHelper {
}
} else if (fParameter.isNamed) {
if (gParameter.isNamed) {
- var compareNames = fParameter.name.compareTo(gParameter.name);
+ var fName = fParameter.name3;
+ var gName = gParameter.name3;
+ if (fName == null || gName == null) {
+ return _interfaceTypeFunctionNone;
+ }
+
+ var compareNames = fName.compareTo(gName);
if (compareNames == 0) {
fIndex++;
gIndex++;
@@ -729,28 +733,28 @@ class LeastUpperBoundHelper {
var returnType = getLeastUpperBound(f.returnType, g.returnType);
- return FunctionTypeImpl(
- typeFormals: fresh.typeParameters,
- parameters: parameters,
+ return FunctionTypeImpl.v2(
+ typeParameters: fresh.typeParameters,
+ formalParameters: parameters,
returnType: returnType,
nullabilitySuffix: NullabilitySuffix.none,
);
}
- DartType? _futureOr(DartType T1, DartType T2) {
- var T1_futureOr = T1 is InterfaceType && T1.isDartAsyncFutureOr
+ TypeImpl? _futureOr(TypeImpl T1, TypeImpl T2) {
+ var T1_futureOr = T1 is InterfaceTypeImpl && T1.isDartAsyncFutureOr
? T1.typeArguments[0]
: null;
- var T1_future = T1 is InterfaceType && T1.isDartAsyncFuture
+ var T1_future = T1 is InterfaceTypeImpl && T1.isDartAsyncFuture
? T1.typeArguments[0]
: null;
- var T2_futureOr = T2 is InterfaceType && T2.isDartAsyncFutureOr
+ var T2_futureOr = T2 is InterfaceTypeImpl && T2.isDartAsyncFutureOr
? T2.typeArguments[0]
: null;
- var T2_future = T2 is InterfaceType && T2.isDartAsyncFuture
+ var T2_future = T2 is InterfaceTypeImpl && T2.isDartAsyncFuture
? T2.typeArguments[0]
: null;
@@ -787,11 +791,11 @@ class LeastUpperBoundHelper {
return null;
}
- DartType _parameterType(ParameterElement a, ParameterElement b) {
+ DartType _parameterType(FormalParameterElement a, FormalParameterElement b) {
return _typeSystem.greatestLowerBound(a.type, b.type);
}
- DartType _recordType(RecordTypeImpl T1, RecordTypeImpl T2) {
+ TypeImpl _recordType(RecordTypeImpl T1, RecordTypeImpl T2) {
var positional1 = T1.positionalFields;
var positional2 = T2.positionalFields;
if (positional1.length != positional2.length) {
@@ -841,7 +845,7 @@ class LeastUpperBoundHelper {
/// Return the promoted or declared bound of the type parameter.
DartType _typeParameterBound(TypeParameterTypeImpl type) {
- var bound = type.promotedBound ?? type.element.bound;
+ var bound = type.promotedBound ?? type.element3.bound;
if (bound != null) {
return bound;
}
diff --git a/pkg/analyzer/lib/src/dart/element/member.dart b/pkg/analyzer/lib/src/dart/element/member.dart
index 36334fa90563..efa4449e590a 100644
--- a/pkg/analyzer/lib/src/dart/element/member.dart
+++ b/pkg/analyzer/lib/src/dart/element/member.dart
@@ -13,6 +13,7 @@ import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/dart/element/display_string_builder.dart';
import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
import 'package:analyzer/src/generated/utilities_dart.dart';
@@ -61,6 +62,16 @@ class ConstructorMember extends ExecutableMember
@override
ConstructorFragment get firstFragment => _element2.firstFragment;
+ @override
+ List get fragments {
+ return [
+ for (ConstructorFragment? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get isConst => declaration.isConst;
@@ -101,7 +112,10 @@ class ConstructorMember extends ExecutableMember
}
@override
- InterfaceType get returnType => type.returnType as InterfaceType;
+ InterfaceTypeImpl get returnType =>
+ // TODO(paulberry): eliminate this cast by changing the type of `type` to
+ // `FunctionTypeImpl`.
+ type.returnType as InterfaceTypeImpl;
@override
Source get source => _declaration.source!;
@@ -228,6 +242,16 @@ abstract class ExecutableMember extends Member
List get formalParameters =>
parameters.map((fragment) => fragment.asElement2).toList();
+ @override
+ List get fragments {
+ return [
+ for (ExecutableFragment? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get hasImplicitReturnType => declaration.hasImplicitReturnType;
@@ -519,6 +543,16 @@ class FieldMember extends VariableMember
@override
FieldFragment get firstFragment => _element2.firstFragment;
+ @override
+ List get fragments {
+ return [
+ for (FieldFragment? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
PropertyAccessorElement? get getter {
var baseGetter = declaration.getter;
@@ -707,6 +741,16 @@ class GetterMember extends PropertyAccessorMember implements GetterElement {
@override
GetterFragment get firstFragment => _element2.firstFragment;
+ @override
+ List get fragments {
+ return [
+ for (GetterFragment? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment as GetterFragment?)
+ fragment,
+ ];
+ }
+
@override
String? get lookupName => _element2.lookupName;
@@ -1014,6 +1058,16 @@ class MethodMember extends ExecutableMember
@override
MethodFragment get firstFragment => _element2.firstFragment;
+ @override
+ List get fragments {
+ return [
+ for (MethodFragment? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
LibraryElement2 get library2 => _element2.library2;
@@ -1081,7 +1135,7 @@ class MethodMember extends ExecutableMember
/// type parameters are known.
class ParameterMember extends VariableMember
with ParameterElementMixin, FormalParameterElementMixin
- implements ParameterElement, FormalParameterElementOrMember {
+ implements ParameterElement {
@override
final List typeParameters;
@@ -1145,6 +1199,16 @@ class ParameterMember extends VariableMember
List get formalParameters =>
_element2.formalParameters;
+ @override
+ List get fragments {
+ return [
+ for (FormalParameterFragment? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment)
+ fragment,
+ ];
+ }
+
@override
bool get hasDefaultValue => declaration.hasDefaultValue;
@@ -1184,7 +1248,7 @@ class ParameterMember extends VariableMember
@override
List get parameters {
var type = this.type;
- if (type is FunctionType) {
+ if (type is FunctionTypeImpl) {
return type.parameters;
}
return const [];
@@ -1197,7 +1261,7 @@ class ParameterMember extends VariableMember
List get typeParameters2 => _element2.typeParameters2;
@override
- DartType get typeShared => type;
+ TypeImpl get typeShared => type;
@override
FormalParameterElement get _element2 => declaration.asElement2;
@@ -1431,6 +1495,16 @@ class SetterMember extends PropertyAccessorMember implements SetterElement {
@override
SetterFragment get firstFragment => _element2.firstFragment;
+ @override
+ List get fragments {
+ return [
+ for (SetterFragment? fragment = firstFragment;
+ fragment != null;
+ fragment = fragment.nextFragment as SetterFragment?)
+ fragment,
+ ];
+ }
+
@override
String? get lookupName => _element2.lookupName;
@@ -1573,7 +1647,7 @@ class TopLevelVariableMember extends VariableMember
/// A variable element defined in a parameterized type where the values of the
/// type parameters are known.
abstract class VariableMember extends Member implements VariableElement {
- DartType? _type;
+ TypeImpl? _type;
/// Initialize a newly created element to represent a variable, based on the
/// [declaration], with applied [substitution].
@@ -1605,13 +1679,15 @@ abstract class VariableMember extends Member implements VariableElement {
bool get isStatic => declaration.isStatic;
@override
- DartType get type {
+ TypeImpl get type {
if (_type != null) return _type!;
var result = declaration.type;
result = augmentationSubstitution.substituteType(result);
result = _substitution.substituteType(result);
- return _type = result;
+ // TODO(paulberry): remove this cast by changing the type of `declaration`
+ // and the return type of `substituteType`
+ return _type = result as TypeImpl;
}
@override
diff --git a/pkg/analyzer/lib/src/dart/element/name_union.dart b/pkg/analyzer/lib/src/dart/element/name_union.dart
index cc1b53a67178..7c0c05afad88 100644
--- a/pkg/analyzer/lib/src/dart/element/name_union.dart
+++ b/pkg/analyzer/lib/src/dart/element/name_union.dart
@@ -2,12 +2,10 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// ignore_for_file: analyzer_use_new_elements
-
import 'dart:typed_data';
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/dart/element/visitor.dart';
+import 'package:analyzer/dart/element/element2.dart';
+import 'package:analyzer/dart/element/visitor2.dart';
/// Union of a set of names.
class ElementNameUnion {
@@ -92,33 +90,45 @@ class ElementNameUnion {
return true;
}
- static ElementNameUnion forLibrary(LibraryElement libraryElement) {
+ static ElementNameUnion forLibrary(LibraryElement2 libraryElement) {
var result = ElementNameUnion.empty();
- libraryElement.accept(
- _ElementVisitor(result),
+ libraryElement.accept2(
+ _ElementVisitor2(result),
);
return result;
}
+
+ static bool _hasInterestingElements(Element2 element) {
+ if (element is ExecutableElement2) {
+ return false;
+ }
+ return true;
+ }
+
+ static bool _isInterestingElement(Element2 element) {
+ return element.enclosingElement2 is LibraryElement2 ||
+ element is FieldElement2 ||
+ element is MethodElement2 ||
+ element is PropertyAccessorElement2;
+ }
}
-class _ElementVisitor extends GeneralizingElementVisitor {
+class _ElementVisitor2 extends GeneralizingElementVisitor2 {
final ElementNameUnion union;
- _ElementVisitor(this.union);
+ _ElementVisitor2(this.union);
@override
- void visitElement(Element element) {
- var enclosing = element.enclosingElement3;
- if (enclosing is CompilationUnitElement ||
- element is FieldElement ||
- element is MethodElement ||
- element is PropertyAccessorElement) {
- var name = element.name;
+ void visitElement(Element2 element) {
+ if (ElementNameUnion._isInterestingElement(element)) {
+ var name = element.name3;
if (name != null) {
union.add(name);
}
}
- super.visitElement(element);
+ if (ElementNameUnion._hasInterestingElements(element)) {
+ super.visitElement(element);
+ }
}
}
diff --git a/pkg/analyzer/lib/src/dart/element/normalize.dart b/pkg/analyzer/lib/src/dart/element/normalize.dart
index cae916490734..9fed4174a5a0 100644
--- a/pkg/analyzer/lib/src/dart/element/normalize.dart
+++ b/pkg/analyzer/lib/src/dart/element/normalize.dart
@@ -2,9 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// ignore_for_file: analyzer_use_new_elements
-
-import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/extensions.dart';
@@ -22,7 +20,7 @@ class NormalizeHelper {
final TypeSystemImpl typeSystem;
final TypeProviderImpl typeProvider;
- final Set _typeParameters = {};
+ final Set _typeParameters = {};
NormalizeHelper(this.typeSystem) : typeProvider = typeSystem.typeProvider;
@@ -35,7 +33,7 @@ class NormalizeHelper {
/// * and B1 = NORM(B)
/// * and S1 = NORM(S)
FunctionTypeImpl _functionType(FunctionType functionType) {
- var fresh = getFreshTypeParameters(functionType.typeFormals);
+ var fresh = getFreshTypeParameters2(functionType.typeParameters);
for (var typeParameter in fresh.freshTypeParameters) {
var bound = typeParameter.firstFragment.bound;
if (bound != null) {
@@ -45,9 +43,9 @@ class NormalizeHelper {
functionType = fresh.applyToFunctionType(functionType);
- return FunctionTypeImpl(
- typeFormals: functionType.typeFormals,
- parameters: functionType.parameters.map((e) {
+ return FunctionTypeImpl.v2(
+ typeParameters: functionType.typeParameters,
+ formalParameters: functionType.formalParameters.map((e) {
return e.copyWith(
type: _normalize(e.type),
);
@@ -134,7 +132,7 @@ class NormalizeHelper {
// NORM(C) = C where Ri is NORM(Ti)
if (T is InterfaceType) {
- return T.element.instantiate(
+ return T.element3.instantiate(
typeArguments: T.typeArguments.map(_normalize).toFixedList(),
nullabilitySuffix: NullabilitySuffix.none,
);
@@ -202,7 +200,7 @@ class NormalizeHelper {
/// NORM(X & T)
/// NORM(X extends T)
DartType _typeParameterType(TypeParameterTypeImpl T) {
- var element = T.element;
+ var element = T.element3;
// NORM(X & T)
var promotedBound = T.promotedBound;
@@ -237,7 +235,7 @@ class NormalizeHelper {
/// NORM(X & T)
/// * let S be NORM(T)
- DartType _typeParameterType_promoted(TypeParameterElement X, DartType S) {
+ DartType _typeParameterType_promoted(TypeParameterElement2 X, DartType S) {
// * if S is Never then Never
if (identical(S, NeverTypeImpl.instance)) {
return NeverTypeImpl.instance;
@@ -245,7 +243,7 @@ class NormalizeHelper {
// * if S is a top type then X
if (typeSystem.isTop(S)) {
- return X.declaration.instantiate(
+ return X.instantiate(
nullabilitySuffix: NullabilitySuffix.none,
);
}
@@ -253,20 +251,20 @@ class NormalizeHelper {
// * if S is X then X
if (S is TypeParameterType &&
S.nullabilitySuffix == NullabilitySuffix.none &&
- S.element == X.declaration) {
- return X.declaration.instantiate(
+ S.element3 == X) {
+ return X.instantiate(
nullabilitySuffix: NullabilitySuffix.none,
);
}
// * if S is Object and NORM(B) is Object where B is the bound of X then X
if (S.nullabilitySuffix == NullabilitySuffix.none && S.isDartCoreObject) {
- var B = X.declaration.bound;
+ var B = X.bound;
if (B != null) {
var B_norm = _normalize(B);
if (B_norm.nullabilitySuffix == NullabilitySuffix.none &&
B_norm.isDartCoreObject) {
- return X.declaration.instantiate(
+ return X.instantiate(
nullabilitySuffix: NullabilitySuffix.none,
);
}
@@ -274,8 +272,8 @@ class NormalizeHelper {
}
// * else X & S
- return TypeParameterTypeImpl(
- element: X.declaration,
+ return TypeParameterTypeImpl.v2(
+ element: X,
nullabilitySuffix: NullabilitySuffix.none,
promotedBound: S,
);
diff --git a/pkg/analyzer/lib/src/dart/element/replace_top_bottom_visitor.dart b/pkg/analyzer/lib/src/dart/element/replace_top_bottom_visitor.dart
index 97925cbcde1c..97381ed7c794 100644
--- a/pkg/analyzer/lib/src/dart/element/replace_top_bottom_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/element/replace_top_bottom_visitor.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// ignore_for_file: analyzer_use_new_elements
-
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
@@ -42,7 +40,7 @@ class ReplaceTopBottomVisitor {
var alias = type.alias;
if (alias != null) {
return _instantiatedTypeAlias(type, alias, variance);
- } else if (type is InterfaceType) {
+ } else if (type is InterfaceTypeImpl) {
return _interfaceType(type, variance);
} else if (type is FunctionType) {
return _functionType(type, variance);
@@ -53,7 +51,7 @@ class ReplaceTopBottomVisitor {
DartType _functionType(FunctionType type, Variance variance) {
var newReturnType = process(type.returnType, variance);
- var newParameters = type.parameters.map((parameter) {
+ var newParameters = type.formalParameters.map((parameter) {
return parameter.copyWith(
type: process(
parameter.type,
@@ -62,9 +60,9 @@ class ReplaceTopBottomVisitor {
);
}).toList();
- return FunctionTypeImpl(
- typeFormals: type.typeFormals,
- parameters: newParameters,
+ return FunctionTypeImpl.v2(
+ typeParameters: type.typeParameters,
+ formalParameters: newParameters,
returnType: newReturnType,
nullabilitySuffix: type.nullabilitySuffix,
);
@@ -75,15 +73,15 @@ class ReplaceTopBottomVisitor {
InstantiatedTypeAliasElement alias,
Variance variance,
) {
- var aliasElement = alias.element;
+ var aliasElement = alias.element2;
var aliasArguments = alias.typeArguments;
- var typeParameters = aliasElement.typeParameters;
+ var typeParameters = aliasElement.typeParameters2;
assert(typeParameters.length == aliasArguments.length);
var newTypeArguments = [];
for (var i = 0; i < typeParameters.length; i++) {
- var typeParameter = typeParameters[i] as TypeParameterElementImpl;
+ var typeParameter = typeParameters[i] as TypeParameterElementImpl2;
newTypeArguments.add(
process(
aliasArguments[i],
@@ -98,8 +96,8 @@ class ReplaceTopBottomVisitor {
);
}
- DartType _interfaceType(InterfaceType type, Variance variance) {
- var typeParameters = type.element.typeParameters;
+ InterfaceTypeImpl _interfaceType(InterfaceTypeImpl type, Variance variance) {
+ var typeParameters = type.element3.typeParameters2;
if (typeParameters.isEmpty) {
return type;
}
@@ -113,8 +111,8 @@ class ReplaceTopBottomVisitor {
newTypeArguments.add(newTypeArgument);
}
- return InterfaceTypeImpl(
- element: type.element,
+ return InterfaceTypeImpl.v2(
+ element: type.element3,
nullabilitySuffix: type.nullabilitySuffix,
typeArguments: newTypeArguments,
);
diff --git a/pkg/analyzer/lib/src/dart/element/since_sdk_version.dart b/pkg/analyzer/lib/src/dart/element/since_sdk_version.dart
index 0bbb26f7e1a6..edbf3cd29da8 100644
--- a/pkg/analyzer/lib/src/dart/element/since_sdk_version.dart
+++ b/pkg/analyzer/lib/src/dart/element/since_sdk_version.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// ignore_for_file: analyzer_use_new_elements
-
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
@@ -14,49 +12,13 @@ class SinceSdkVersionComputer {
/// The [element] is a `dart:xyz` library, so it can have `@Since` annotations.
/// Evaluates its annotations and returns the version.
- Version? compute(ElementImpl element) {
- // Must be in a `dart:` library.
- var librarySource = element.librarySource;
- if (librarySource == null || !librarySource.uri.isScheme('dart')) {
- return null;
- }
-
- // Fields cannot be referenced outside.
- if (element is FieldElementImpl && element.isSynthetic) {
- return null;
- }
-
- // We cannot add required parameters.
- if (element is ParameterElementImpl && element.isRequired) {
- return null;
- }
-
- var specified = _specifiedVersion(element);
- if (element.enclosingElement3 case var enclosingElement?) {
- var enclosing = enclosingElement.sinceSdkVersion;
- return specified.maxWith(enclosing);
- } else if (element.library case var libraryElement?) {
- var enclosing = libraryElement.sinceSdkVersion;
- return specified.maxWith(enclosing);
- } else {
- return specified;
- }
- }
-
- /// The [element] is a `dart:xyz` library, so it can have `@Since` annotations.
- /// Evaluates its annotations and returns the version.
- Version? compute2(Element2 element) {
+ Version? compute(Element2 element) {
// Must be in a `dart:` library.
var libraryUri = element.library2?.uri;
if (libraryUri == null || !libraryUri.isScheme('dart')) {
return null;
}
- // Fields cannot be referenced outside.
- if (element is FieldElement2 && element.isSynthetic) {
- return null;
- }
-
// We cannot add required parameters.
if (element is FormalParameterElement && element.isRequired) {
return null;
@@ -64,13 +26,13 @@ class SinceSdkVersionComputer {
Version? specified;
if (element is Annotatable) {
- specified = _specifiedVersion2(element as Annotatable);
+ specified = _specifiedVersion(element as Annotatable);
}
- if (element.enclosingElement2 case Annotatable enclosingElement?) {
- var enclosing = enclosingElement.metadata2.sinceSdkVersion;
- return specified.maxWith(enclosing);
- } else if (element.library2 case var libraryElement?) {
- var enclosing = libraryElement.metadata2.sinceSdkVersion;
+
+ if (element is LibraryElement2) {
+ return specified;
+ } else if (element.enclosingElement2 case HasSinceSdkVersion hasSince?) {
+ var enclosing = hasSince.sinceSdkVersion;
return specified.maxWith(enclosing);
} else {
return specified;
@@ -93,26 +55,7 @@ class SinceSdkVersionComputer {
}
/// Returns the maximal specified `@Since()` version, `null` if none.
- static Version? _specifiedVersion(ElementImpl element) {
- Version? result;
- for (var annotation in element.metadata) {
- if (annotation.isDartInternalSince) {
- var arguments = annotation.annotationAst.arguments?.arguments;
- var versionNode = arguments?.singleOrNull;
- if (versionNode is SimpleStringLiteralImpl) {
- var versionStr = versionNode.value;
- var version = _parseVersion(versionStr);
- if (version != null) {
- result = result.maxWith(version);
- }
- }
- }
- }
- return result;
- }
-
- /// Returns the maximal specified `@Since()` version, `null` if none.
- static Version? _specifiedVersion2(Annotatable element) {
+ static Version? _specifiedVersion(Annotatable element) {
var annotations =
element.metadata2.annotations.cast();
Version? result;
diff --git a/pkg/analyzer/lib/src/dart/element/subtype.dart b/pkg/analyzer/lib/src/dart/element/subtype.dart
index 64c91650d34c..0b9de69dd0be 100644
--- a/pkg/analyzer/lib/src/dart/element/subtype.dart
+++ b/pkg/analyzer/lib/src/dart/element/subtype.dart
@@ -2,11 +2,9 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// ignore_for_file: analyzer_use_new_elements
-
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart'
show Variance;
-import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
@@ -92,7 +90,7 @@ class SubtypeHelper {
T0 is TypeParameterTypeImpl) {
var S = T0.promotedBound;
if (S == null) {
- var B = T0.element.bound ?? _objectQuestion;
+ var B = T0.element3.bound ?? _objectQuestion;
return isSubtypeOf(B, _objectNone);
} else {
return isSubtypeOf(S, _objectNone);
@@ -115,7 +113,7 @@ class SubtypeHelper {
return false;
}
// Extension types require explicit `Object` implementation.
- if (T0 is InterfaceTypeImpl && T0.element is ExtensionTypeElement) {
+ if (T0 is InterfaceTypeImpl && T0.element3 is ExtensionTypeElement2) {
for (var interface in T0.interfaces) {
if (isSubtypeOf(interface, T1_)) {
return true;
@@ -179,7 +177,7 @@ class SubtypeHelper {
if (T0 is TypeParameterTypeImpl &&
T1 is TypeParameterTypeImpl &&
T1.promotedBound == null &&
- T0.element == T1.element) {
+ T0.element3 == T1.element3) {
return true;
}
@@ -188,8 +186,8 @@ class SubtypeHelper {
if (T1 is TypeParameterTypeImpl) {
var T1_promotedBound = T1.promotedBound;
if (T1_promotedBound != null) {
- var X1 = TypeParameterTypeImpl(
- element: T1.element,
+ var X1 = TypeParameterTypeImpl.v2(
+ element: T1.element3,
nullabilitySuffix: T1.nullabilitySuffix,
);
return isSubtypeOf(T0, X1) && isSubtypeOf(T0, T1_promotedBound);
@@ -221,7 +219,7 @@ class SubtypeHelper {
if (S0 != null && isSubtypeOf(S0, T1)) {
return true;
}
- var B0 = T0.element.bound;
+ var B0 = T0.element3.bound;
if (B0 != null && isSubtypeOf(B0, T1)) {
return true;
}
@@ -249,7 +247,7 @@ class SubtypeHelper {
if (S0 != null && isSubtypeOf(S0, T1)) {
return true;
}
- var B0 = T0.element.bound;
+ var B0 = T0.element3.bound;
if (B0 != null && isSubtypeOf(B0, T1)) {
return true;
}
@@ -275,7 +273,7 @@ class SubtypeHelper {
return true;
}
- var B0 = T0.element.bound;
+ var B0 = T0.element3.bound;
if (B0 != null && isSubtypeOf(B0, T1)) {
return true;
}
@@ -305,11 +303,11 @@ class SubtypeHelper {
}
bool _interfaceArguments(
- InterfaceElement element,
+ InterfaceElement2 element,
InterfaceType subType,
InterfaceType superType,
) {
- List parameters = element.typeParameters;
+ List parameters = element.typeParameters2;
List subArguments = subType.typeArguments;
List superArguments = superType.typeArguments;
@@ -317,7 +315,7 @@ class SubtypeHelper {
assert(parameters.length == subArguments.length);
for (int i = 0; i < subArguments.length; i++) {
- var parameter = parameters[i] as TypeParameterElementImpl;
+ var parameter = parameters[i] as TypeParameterElementImpl2;
var subArgument = subArguments[i];
var superArgument = superArguments[i];
@@ -347,7 +345,8 @@ class SubtypeHelper {
/// Check that [f] is a subtype of [g].
bool _isFunctionSubtypeOf(FunctionType f, FunctionType g) {
- var fresh = _typeSystem.relateTypeParameters(f.typeFormals, g.typeFormals);
+ var fresh =
+ _typeSystem.relateTypeParameters2(f.typeParameters, g.typeParameters);
if (fresh == null) {
return false;
}
@@ -359,8 +358,8 @@ class SubtypeHelper {
return false;
}
- var fParameters = f.parameters;
- var gParameters = g.parameters;
+ var fParameters = f.formalParameters;
+ var gParameters = g.formalParameters;
var fIndex = 0;
var gIndex = 0;
@@ -391,27 +390,33 @@ class SubtypeHelper {
}
} else if (fParameter.isNamed) {
if (gParameter.isNamed) {
- var compareNames = fParameter.name.compareTo(gParameter.name);
- if (compareNames == 0) {
- if (fParameter.isRequiredNamed && !gParameter.isRequiredNamed) {
- return false;
- } else if (isSubtypeOf(gParameter.type, fParameter.type)) {
- fIndex++;
- gIndex++;
- } else {
- return false;
- }
- } else if (compareNames < 0) {
- if (fParameter.isRequiredNamed) {
- return false;
- } else {
- fIndex++;
- }
- } else {
- assert(compareNames > 0);
- // The subtype must accept all parameters of the supertype.
+ var fName = fParameter.name3;
+ var gName = gParameter.name3;
+ if (fName == null || gName == null) {
return false;
}
+
+ var compareNames = fName.compareTo(gName);
+ switch (compareNames) {
+ case 0:
+ if (fParameter.isRequiredNamed && !gParameter.isRequiredNamed) {
+ return false;
+ } else if (isSubtypeOf(gParameter.type, fParameter.type)) {
+ fIndex++;
+ gIndex++;
+ } else {
+ return false;
+ }
+ case < 0:
+ if (fParameter.isRequiredNamed) {
+ return false;
+ } else {
+ fIndex++;
+ }
+ default:
+ // The subtype must accept all parameters of the supertype.
+ return false;
+ }
} else {
break;
}
@@ -448,8 +453,8 @@ class SubtypeHelper {
return false;
}
- var subElement = subType.element;
- var superElement = superType.element;
+ var subElement = subType.element3;
+ var superElement = superType.element3;
if (identical(subElement, superElement)) {
return _interfaceArguments(superElement, subType, superType);
}
@@ -460,7 +465,7 @@ class SubtypeHelper {
}
for (var interface in subElement.allSupertypes) {
- if (identical(interface.element, superElement)) {
+ if (identical(interface.element3, superElement)) {
var substitution = Substitution.fromInterfaceType(subType);
var substitutedInterface =
substitution.substituteType(interface) as InterfaceType;
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 10989f5da66a..7c4e0771b331 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -35,7 +35,7 @@ List fixedTypeList(DartType e1, [DartType? e2]) {
/// The [Type] representing the type `dynamic`.
class DynamicTypeImpl extends TypeImpl
- implements DynamicType, SharedDynamicTypeStructure {
+ implements DynamicType, SharedDynamicTypeStructure {
/// The unique instance of this class.
static final DynamicTypeImpl instance = DynamicTypeImpl._();
@@ -90,13 +90,13 @@ class DynamicTypeImpl extends TypeImpl
class FunctionTypeImpl extends TypeImpl
implements
FunctionType,
- SharedFunctionTypeStructure {
+ SharedFunctionTypeStructure {
@override
late int hashCode = _computeHashCode();
@override
- final DartType returnType;
+ final TypeImpl returnType;
@override
final List typeFormals;
@@ -108,7 +108,7 @@ class FunctionTypeImpl extends TypeImpl
final NullabilitySuffix nullabilitySuffix;
@override
- final List positionalParameterTypes;
+ final List positionalParameterTypes;
@override
final int requiredPositionalParameterCount;
@@ -125,7 +125,7 @@ class FunctionTypeImpl extends TypeImpl
}) {
int? firstNamedParameterIndex;
var requiredPositionalParameterCount = 0;
- var positionalParameterTypes =