Version 2.19.0-45.0.dev
Merge commit 'b7edb69e974d630122e73f13ddeb425728dc3537' into 'dev'
diff --git a/DEPS b/DEPS
index aad6a1a..1ad3427 100644
--- a/DEPS
+++ b/DEPS
@@ -135,11 +135,10 @@
"path_rev": "9955b27b9bb98d87591208e19eb01c51d29fd467",
"ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
"pool_rev": "fa84ddd0e39f45bf3f09dcc5d6b9fbdda7820fef",
- "protobuf_rev": "a840335449e6a2a9617d2ebe5ecd0d577e071248",
+ "protobuf_rev": "9aad6aadcc0fc616051c7e0eaef78c26b3dd7b60",
"pub_rev": "9bf4289d6fd5d6872a8929d6312bbd7098f3ea9c", # manually rev'd
"pub_semver_rev": "5c0b4bfd5ca57fe16f1319c581dc8c882e9b8cb2",
"root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
- "rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
"shelf_rev": "0371a64bd3b99044ee3158bacf8813bba735a9c7",
"source_map_stack_trace_rev": "72dbf21a33293b2b8434d0a9751e36f9463981ac",
"source_maps_rev": "e93565b43a7b6b367789de8ffba969c4ebeeb317",
@@ -556,23 +555,12 @@
"dep_type": "cipd",
},
- Var("dart_root") + "/buildtools/" + Var("host_os") + "-" + Var("host_cpu") + "/rust": {
- "packages": [
- {
- "package": "fuchsia/rust/${{platform}}",
- "version": "git_revision:" + Var("rust_revision"),
- },
- ],
- "condition": "(host_os == 'linux' or host_os == 'mac') and host_cpu == 'x64'",
- "dep_type": "cipd",
- },
-
# Update from https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/gn
Var("dart_root") + "/third_party/fuchsia/sdk/mac": {
"packages": [
{
"package": "fuchsia/sdk/gn/mac-amd64",
- "version": "git_revision:c9bdf5da65647923cb79c391824434125cb00bbe"
+ "version": "git_revision:8658717573be7924d71b88c3d404ea2b4ca4984b"
}
],
"condition": 'host_os == "mac" and host_cpu == "x64"',
@@ -582,7 +570,7 @@
"packages": [
{
"package": "fuchsia/sdk/gn/linux-amd64",
- "version": "git_revision:c9bdf5da65647923cb79c391824434125cb00bbe"
+ "version": "git_revision:8658717573be7924d71b88c3d404ea2b4ca4984b"
}
],
"condition": 'host_os == "linux" and host_cpu == "x64"',
diff --git a/pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart b/pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart
index ceb8a33..ca17b51 100644
--- a/pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart
@@ -478,7 +478,11 @@
@override
void visitPrefixedIdentifier(PrefixedIdentifier node) {
- collect(node.identifier);
+ // Don't collect prefixed identifiers that are just type names. We only
+ // want invocations and tear-offs.
+ if (node.parent is! NamedType) {
+ collect(node.identifier);
+ }
super.visitPrefixedIdentifier(node);
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index f9cbe3b..956e51e 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -19,7 +19,8 @@
import 'package:analysis_server/src/services/completion/yaml/fix_data_generator.dart';
import 'package:analysis_server/src/services/completion/yaml/pubspec_generator.dart';
import 'package:analysis_server/src/services/completion/yaml/yaml_completion_generator.dart';
-import 'package:analysis_server/src/services/snippets/dart/snippet_manager.dart';
+import 'package:analysis_server/src/services/snippets/dart_snippet_request.dart';
+import 'package:analysis_server/src/services/snippets/snippet_manager.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/ast/ast.dart' as ast;
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index d7ada24..34e7274 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -17,7 +17,7 @@
import 'package:analysis_server/src/lsp/source_edits.dart';
import 'package:analysis_server/src/protocol_server.dart' as server
hide AnalysisError;
-import 'package:analysis_server/src/services/snippets/dart/snippet_manager.dart';
+import 'package:analysis_server/src/services/snippets/snippet.dart';
import 'package:analyzer/dart/analysis/results.dart' as server;
import 'package:analyzer/error/error.dart' as server;
import 'package:analyzer/source/line_info.dart' as server;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
index 96a232c..388e79e 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
@@ -32,10 +32,6 @@
/// An object that can compute a correction (fix or assist) in a Dart file.
abstract class CorrectionProducer extends SingleCorrectionProducer {
- /// Return the code style options for the current context.
- CodeStyleOptions get codeStyleOptions =>
- sessionHelper.session.analysisContext.analysisOptions.codeStyleOptions;
-
/// Return the type for the class `bool` from `dart:core`.
DartType get coreTypeBool => resolvedResult.typeProvider.boolType;
@@ -390,6 +386,9 @@
/// Return `true` if the fixes are being built for the bulk-fix request.
bool get applyingBulkFixes => _context.applyingBulkFixes;
+ CodeStyleOptions get codeStyleOptions =>
+ sessionHelper.session.analysisContext.analysisOptions.codeStyleOptions;
+
/// The most deeply nested node that completely covers the highlight region of
/// the diagnostic, or `null` if there is no diagnostic or if such a node does
/// not exist.
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/assign_to_local_variable.dart b/pkg/analysis_server/lib/src/services/correction/dart/assign_to_local_variable.dart
index 3b00724..90aacb1 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/assign_to_local_variable.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/assign_to_local_variable.dart
@@ -6,7 +6,6 @@
import 'package:analysis_server/src/services/correction/assist.dart';
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/name_suggestion.dart';
-import 'package:analysis_server/src/services/linter/lint_names.dart';
import 'package:analysis_server/src/utilities/extensions/ast.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
@@ -19,7 +18,7 @@
AssistKind get assistKind => DartAssistKind.ASSIGN_TO_LOCAL_VARIABLE;
String get _declarationKeyword {
- if (_isLintEnabled(LintNames.prefer_final_locals)) {
+ if (codeStyleOptions.makeLocalsFinal) {
return 'final';
} else {
return 'var';
@@ -75,11 +74,6 @@
}
}
- bool _isLintEnabled(String name) {
- var analysisOptions = unit.declaredElement?.context.analysisOptions;
- return analysisOptions?.isLintEnabled(name) ?? false;
- }
-
/// Return `true` if the given [statement] resulted from a recovery case that
/// would make the change create even worse errors than the original code.
static bool _hasPrecedingStatementRecovery(Statement statement) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart b/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
index 5d333c2..d066710 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
@@ -7,7 +7,6 @@
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/correction/namespace.dart';
-import 'package:analysis_server/src/services/linter/lint_names.dart';
import 'package:analysis_server/src/utilities/extensions/element.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
@@ -220,7 +219,7 @@
_ImportAbsoluteLibrary(fixKind, library),
]);
}
- if (isLintEnabled(LintNames.prefer_relative_imports)) {
+ if (codeStyleOptions.useRelativeUris) {
return Stream.fromIterable([
_ImportRelativeLibrary(fixKind, library),
_ImportAbsoluteLibrary(fixKind, library),
diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
index ac4fb50..5f2bacf 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
@@ -8,11 +8,11 @@
import 'package:analysis_server/src/services/correction/name_suggestion.dart';
import 'package:analysis_server/src/services/correction/status.dart';
import 'package:analysis_server/src/services/correction/util.dart';
-import 'package:analysis_server/src/services/linter/lint_names.dart';
import 'package:analysis_server/src/services/refactoring/naming_conventions.dart';
import 'package:analysis_server/src/services/refactoring/refactoring.dart';
import 'package:analysis_server/src/services/refactoring/refactoring_internal.dart';
import 'package:analysis_server/src/utilities/strings.dart';
+import 'package:analyzer/dart/analysis/code_style_options.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
@@ -61,6 +61,9 @@
utils = CorrectionUtils(resolveResult);
}
+ CodeStyleOptions get codeStyleOptions =>
+ resolveResult.session.analysisContext.analysisOptions.codeStyleOptions;
+
String get file => resolveResult.path;
@override
@@ -73,7 +76,7 @@
String get _declarationKeyword {
if (_isPartOfConstantExpression(singleExpression)) {
return 'const';
- } else if (_isLintEnabled(LintNames.prefer_final_locals)) {
+ } else if (codeStyleOptions.makeLocalsFinal) {
return 'final';
} else {
return 'var';
@@ -437,11 +440,6 @@
return null;
}
- bool _isLintEnabled(String name) {
- var analysisOptions = unitElement.context.analysisOptions;
- return analysisOptions.isLintEnabled(name);
- }
-
bool _isPartOfConstantExpression(AstNode? node) {
if (node == null) {
return false;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart
index c85b15d..b8b519c 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart
@@ -408,6 +408,9 @@
builder.addInsertion(_enclosingUnitMember!.end, (builder) {
builder.writeln();
builder.writeln();
+ var useSuperParameters = _featureSet.isEnabled(Feature.super_parameters);
+ var paramsToInitialize =
+ _parameters.where((p) => p.constructorName != p.name).toList();
builder.writeClassDeclaration(
name,
superclass: classStatelessWidget!.instantiate(
@@ -425,15 +428,19 @@
// Add the required `key` parameter.
builder.write(' ');
- builder.writeParameter(
- 'key',
- type: classKey!.instantiate(
- typeArguments: const [],
- nullabilitySuffix: _isNonNullable
- ? NullabilitySuffix.question
- : NullabilitySuffix.star,
- ),
- );
+ if (useSuperParameters) {
+ builder.write('super.key');
+ } else {
+ builder.writeParameter(
+ 'key',
+ type: classKey!.instantiate(
+ typeArguments: const [],
+ nullabilitySuffix: _isNonNullable
+ ? NullabilitySuffix.question
+ : NullabilitySuffix.star,
+ ),
+ );
+ }
builder.writeln(',');
// Add parameters for fields, local, and method parameters.
@@ -459,17 +466,25 @@
builder.write(' }');
},
- initializerWriter: () {
- for (var parameter in _parameters) {
- if (parameter.constructorName != parameter.name) {
- builder.write(parameter.name);
- builder.write(' = ');
- builder.write(parameter.constructorName);
- builder.write(', ');
- }
- }
- builder.write('super(key: key)');
- },
+ initializerWriter: useSuperParameters && paramsToInitialize.isEmpty
+ ? null
+ : () {
+ for (var i = 0; i < paramsToInitialize.length; ++i) {
+ var parameter = paramsToInitialize[i];
+ if (i > 0) {
+ builder.write(', ');
+ }
+ builder.write(parameter.name);
+ builder.write(' = ');
+ builder.write(parameter.constructorName);
+ }
+ if (!useSuperParameters) {
+ if (paramsToInitialize.isNotEmpty) {
+ builder.write(', ');
+ }
+ builder.write('super(key: key)');
+ }
+ },
);
builder.writeln();
builder.writeln();
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/class_declaration.dart b/pkg/analysis_server/lib/src/services/snippets/dart/class_declaration.dart
new file mode 100644
index 0000000..aa126db
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/class_declaration.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a class definition.
+class ClassDeclaration extends DartSnippetProducer {
+ static const prefix = 'class';
+ static const label = 'class';
+
+ ClassDeclaration(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ builder.write('class ');
+ builder.addSimpleLinkedEdit('className', 'ClassName');
+ builder.writeln(' {');
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndented('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a class definition.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/dart_snippet_producers.dart b/pkg/analysis_server/lib/src/services/snippets/dart/dart_snippet_producers.dart
deleted file mode 100644
index b943eda..0000000
--- a/pkg/analysis_server/lib/src/services/snippets/dart/dart_snippet_producers.dart
+++ /dev/null
@@ -1,588 +0,0 @@
-// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:analysis_server/src/services/correction/util.dart';
-import 'package:analysis_server/src/services/linter/lint_names.dart';
-import 'package:analysis_server/src/services/snippets/dart/snippet_manager.dart';
-import 'package:analyzer/dart/analysis/features.dart';
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/dart/element/nullability_suffix.dart';
-import 'package:analyzer/src/dart/analysis/session_helper.dart';
-import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/lint/linter.dart' show LinterContextImpl;
-import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
-
-/// Produces a [Snippet] that creates a Class definition.
-class DartClassSnippetProducer extends DartSnippetProducer {
- static const prefix = 'class';
- static const label = 'class';
-
- DartClassSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- builder.write('class ');
- builder.addSimpleLinkedEdit('className', 'ClassName');
- builder.writeln(' {');
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndented('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a class definition.',
- builder.sourceChange,
- );
- }
-
- static DartClassSnippetProducer newInstance(DartSnippetRequest request) =>
- DartClassSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates a `do while` loop.
-class DartDoWhileLoopSnippetProducer extends DartSnippetProducer {
- static const prefix = 'do';
- static const label = 'do while';
-
- DartDoWhileLoopSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- builder.writeln('do {');
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndented('} while (');
- builder.addSimpleLinkedEdit('condition', 'condition');
- builder.write(');');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a do-while loop.',
- builder.sourceChange,
- );
- }
-
- static DartDoWhileLoopSnippetProducer newInstance(
- DartSnippetRequest request) =>
- DartDoWhileLoopSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates a `for in` loop.
-class DartForInLoopSnippetProducer extends DartSnippetProducer {
- static const prefix = 'forin';
- static const label = 'for in';
-
- DartForInLoopSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
- final varOrFinal =
- isLintEnabled(LintNames.prefer_final_locals) ? 'final' : 'var';
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- builder.write('for ($varOrFinal ');
- builder.addSimpleLinkedEdit('elementName', 'element');
- builder.write(' in ');
- builder.addSimpleLinkedEdit('collectionName', 'collection');
- builder.writeln(') {');
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndented('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a for-in loop.',
- builder.sourceChange,
- );
- }
-
- static DartForInLoopSnippetProducer newInstance(DartSnippetRequest request) =>
- DartForInLoopSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates a `for` loop.
-class DartForLoopSnippetProducer extends DartSnippetProducer {
- static const prefix = 'for';
- static const label = 'for';
-
- DartForLoopSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- builder.write('for (var i = 0; i < ');
- builder.addSimpleLinkedEdit('count', 'count');
- builder.writeln('; i++) {');
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndented('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a for loop.',
- builder.sourceChange,
- );
- }
-
- static DartForLoopSnippetProducer newInstance(DartSnippetRequest request) =>
- DartForLoopSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates a function definition.
-class DartFunctionSnippetProducer extends DartSnippetProducer {
- static const prefix = 'fun';
- static const label = 'fun';
-
- DartFunctionSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
-
- builder.addSimpleLinkedEdit('returnType', 'void');
- builder.write(' ');
- builder.addSimpleLinkedEdit('name', 'name');
- builder.write('(');
- builder.addSimpleLinkedEdit('params', 'params');
- builder.writeln(') {');
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndented('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a function definition.',
- builder.sourceChange,
- );
- }
-
- static DartFunctionSnippetProducer newInstance(DartSnippetRequest request) =>
- DartFunctionSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates an if/else statement.
-class DartIfElseSnippetProducer extends DartSnippetProducer {
- static const prefix = 'ife';
- static const label = 'ife';
-
- DartIfElseSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- void writeIndentedln(String string) =>
- builder.writeln('$indent$string');
- builder.write('if (');
- builder.addSimpleLinkedEdit('condition', 'condition');
- builder.writeln(') {');
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndentedln('} else {');
- writeIndentedln(' ');
- writeIndented('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert an if/else statement.',
- builder.sourceChange,
- );
- }
-
- static DartIfElseSnippetProducer newInstance(DartSnippetRequest request) =>
- DartIfElseSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates an if statement.
-class DartIfSnippetProducer extends DartSnippetProducer {
- static const prefix = 'if';
- static const label = 'if';
-
- DartIfSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- builder.write('if (');
- builder.addSimpleLinkedEdit('condition', 'condition');
- builder.writeln(') {');
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndented('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert an if statement.',
- builder.sourceChange,
- );
- }
-
- static DartIfSnippetProducer newInstance(DartSnippetRequest request) =>
- DartIfSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates a top-level `main` function.
-///
-/// A `List<String> args` parameter will be included when generating inside a
-/// file in `bin` or `tool` folders.
-class DartMainFunctionSnippetProducer extends DartSnippetProducer {
- static const prefix = 'main';
- static const label = 'main()';
-
- DartMainFunctionSnippetProducer._(super.request);
-
- /// Whether to insert a `List<String> args` parameter in the generated
- /// function.
- ///
- /// The parameter is suppressed for any known test directories.
- bool get _insertArgsParameter => !isInTestDirectory;
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
-
- final typeProvider = request.unit.typeProvider;
- final listString = typeProvider.listType(typeProvider.stringType);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- builder.writeFunctionDeclaration(
- 'main',
- returnType: VoidTypeImpl.instance,
- parameterWriter: _insertArgsParameter
- ? () => builder.writeParameter('args', type: listString)
- : null,
- bodyWriter: () {
- builder.writeln('{');
- builder.write(' ');
- builder.selectHere();
- builder.writeln();
- builder.write('}');
- },
- );
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a main function, used as an entry point.',
- builder.sourceChange,
- );
- }
-
- static DartMainFunctionSnippetProducer newInstance(
- DartSnippetRequest request) =>
- DartMainFunctionSnippetProducer._(request);
-}
-
-abstract class DartSnippetProducer extends SnippetProducer {
- final AnalysisSessionHelper sessionHelper;
- final CorrectionUtils utils;
- final LibraryElement libraryElement;
- final bool useSuperParams;
-
- DartSnippetProducer(super.request)
- : sessionHelper = AnalysisSessionHelper(request.analysisSession),
- utils = CorrectionUtils(request.unit),
- libraryElement = request.unit.libraryElement,
- useSuperParams = request.unit.libraryElement.featureSet
- .isEnabled(Feature.super_parameters);
-
- bool get isInTestDirectory {
- final path = request.unit.path;
- return LinterContextImpl.testDirectories
- .any((testDir) => path.contains(testDir));
- }
-
- /// The nullable suffix to use in this library.
- NullabilitySuffix get nullableSuffix => libraryElement.isNonNullableByDefault
- ? NullabilitySuffix.question
- : NullabilitySuffix.none;
-
- bool isLintEnabled(String name) {
- var analysisOptions = sessionHelper.session.analysisContext.analysisOptions;
- return analysisOptions.isLintEnabled(name);
- }
-}
-
-/// Produces a [Snippet] that creates an if statement.
-class DartSwitchSnippetProducer extends DartSnippetProducer {
- static const prefix = 'switch';
- static const label = 'switch case';
-
- DartSwitchSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- void writeIndentedln(String string) =>
- builder.writeln('$indent$string');
- builder.write('switch (');
- builder.addSimpleLinkedEdit('expression', 'expression');
- builder.writeln(') {');
- writeIndented(' case ');
- builder.addSimpleLinkedEdit('value', 'value');
- builder.writeln(':');
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndentedln(' break;');
- writeIndentedln(' default:');
- writeIndented('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a switch statement.',
- builder.sourceChange,
- );
- }
-
- static DartSwitchSnippetProducer newInstance(DartSnippetRequest request) =>
- DartSwitchSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates a `test()` block.
-class DartTestBlockSnippetProducer extends DartSnippetProducer {
- static const prefix = 'test';
- static const label = 'test';
-
- DartTestBlockSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- builder.write("test('");
- builder.addSimpleLinkedEdit('testName', 'test name');
- builder.writeln("', () {");
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndented('});');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a test block.',
- builder.sourceChange,
- );
- }
-
- @override
- Future<bool> isValid() async {
- if (!await super.isValid()) {
- return false;
- }
-
- return isInTestDirectory;
- }
-
- static DartTestBlockSnippetProducer newInstance(DartSnippetRequest request) =>
- DartTestBlockSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates a `test()` block.
-class DartTestGroupBlockSnippetProducer extends DartSnippetProducer {
- static const prefix = 'group';
- static const label = 'group';
-
- DartTestGroupBlockSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- builder.write("group('");
- builder.addSimpleLinkedEdit('groupName', 'group name');
- builder.writeln("', () {");
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndented('});');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a test group block.',
- builder.sourceChange,
- );
- }
-
- @override
- Future<bool> isValid() async {
- if (!await super.isValid()) {
- return false;
- }
-
- return isInTestDirectory;
- }
-
- static DartTestGroupBlockSnippetProducer newInstance(
- DartSnippetRequest request) =>
- DartTestGroupBlockSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates a try/catch statement.
-class DartTryCatchSnippetProducer extends DartSnippetProducer {
- static const prefix = 'try';
- static const label = 'try';
-
- DartTryCatchSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- void writeIndentedln(String string) =>
- builder.writeln('$indent$string');
- builder.writeln('try {');
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndented('} catch (');
- builder.addLinkedEdit('exceptionName', (builder) {
- builder.write('e');
- });
- builder.writeln(') {');
- writeIndentedln(' ');
- writeIndented('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a try/catch statement.',
- builder.sourceChange,
- );
- }
-
- static DartTryCatchSnippetProducer newInstance(DartSnippetRequest request) =>
- DartTryCatchSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates a `while` loop.
-class DartWhileLoopSnippetProducer extends DartSnippetProducer {
- static const prefix = 'while';
- static const label = 'while';
-
- DartWhileLoopSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
- final indent = utils.getLinePrefix(request.offset);
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- void writeIndented(String string) => builder.write('$indent$string');
- builder.write('while (');
- builder.addSimpleLinkedEdit('condition', 'condition');
- builder.writeln(') {');
- writeIndented(' ');
- builder.selectHere();
- builder.writeln();
- writeIndented('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a while loop.',
- builder.sourceChange,
- );
- }
-
- static DartWhileLoopSnippetProducer newInstance(DartSnippetRequest request) =>
- DartWhileLoopSnippetProducer._(request);
-}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/do_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/do_statement.dart
new file mode 100644
index 0000000..4a58903
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/do_statement.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a `do while` loop.
+class DoStatement extends DartSnippetProducer {
+ static const prefix = 'do';
+ static const label = 'do while';
+
+ DoStatement(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ builder.writeln('do {');
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndented('} while (');
+ builder.addSimpleLinkedEdit('condition', 'condition');
+ builder.write(');');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a do-while loop.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_snippet_producers.dart b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_snippet_producers.dart
deleted file mode 100644
index 9644c99..0000000
--- a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_snippet_producers.dart
+++ /dev/null
@@ -1,416 +0,0 @@
-// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:analysis_server/src/services/snippets/dart/dart_snippet_producers.dart';
-import 'package:analysis_server/src/services/snippets/dart/snippet_manager.dart';
-import 'package:analysis_server/src/utilities/flutter.dart';
-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/type.dart';
-import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
-import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
-import 'package:meta/meta.dart';
-
-abstract class FlutterSnippetProducer extends DartSnippetProducer {
- final flutter = Flutter.instance;
-
- late ClassElement? classWidget;
- late ClassElement? classContainer;
-
- FlutterSnippetProducer(super.request);
-
- @override
- @mustCallSuper
- Future<bool> isValid() async {
- if ((classWidget = await _getClass('Widget')) == null) {
- return false;
- }
-
- if ((classContainer = await _getClass('Container')) == null) {
- return false;
- }
-
- return super.isValid();
- }
-
- Future<ClassElement?> _getClass(String name) =>
- sessionHelper.getClass(flutter.widgetsUri, name);
-
- DartType _getType(
- ClassElement classElement, [
- NullabilitySuffix nullabilitySuffix = NullabilitySuffix.none,
- ]) =>
- classElement.instantiate(
- typeArguments: const [],
- nullabilitySuffix: nullabilitySuffix,
- );
-}
-
-/// Produces a [Snippet] that creates a Flutter StatefulWidget and related State
-/// class.
-class FlutterStatefulWidgetSnippetProducer extends FlutterSnippetProducer
- with FlutterWidgetSnippetProducerMixin {
- static const prefix = 'stful';
- static const label = 'Flutter Stateful Widget';
-
- late ClassElement? classStatefulWidget;
- late ClassElement? classState;
- @override
- late ClassElement? classBuildContext;
- @override
- late ClassElement? classKey;
-
- FlutterStatefulWidgetSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
-
- // Checked by isValid().
- final classStatefulWidget = this.classStatefulWidget!;
- final classState = this.classState!;
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- // Write the StatefulWidget class
- builder.writeClassDeclaration(
- widgetClassName,
- nameGroupName: 'name',
- superclass: _getType(classStatefulWidget),
- membersWriter: () {
- writeWidgetConstructor(builder);
- builder.writeln();
- builder.writeln();
-
- writeCreateStateMethod(builder);
- },
- );
- builder.writeln();
- builder.writeln();
-
- // Write the State class.
- builder.write('class _');
- builder.addSimpleLinkedEdit('name', widgetClassName);
- builder.write('State extends ');
- builder.writeReference(classState);
- builder.write('<');
- builder.addSimpleLinkedEdit('name', widgetClassName);
- builder.writeln('> {');
- {
- writeBuildMethod(builder);
- }
- builder.write('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a Flutter StatefulWidget.',
- builder.sourceChange,
- );
- }
-
- @override
- Future<bool> isValid() async {
- if (!await super.isValid()) {
- return false;
- }
-
- if ((classStatefulWidget = await _getClass('StatefulWidget')) == null ||
- (classState = await _getClass('State')) == null ||
- (classBuildContext = await _getClass('BuildContext')) == null ||
- (classKey = await _getClass('Key')) == null) {
- return false;
- }
-
- return true;
- }
-
- static FlutterStatefulWidgetSnippetProducer newInstance(
- DartSnippetRequest request) =>
- FlutterStatefulWidgetSnippetProducer._(request);
-}
-
-/// Produces a [Snippet] that creates a Flutter StatefulWidget with a
-/// AnimationController and related State class.
-class FlutterStatefulWidgetWithAnimationControllerSnippetProducer
- extends FlutterSnippetProducer with FlutterWidgetSnippetProducerMixin {
- static const prefix = 'stanim';
- static const label = 'Flutter Widget with AnimationController';
-
- late ClassElement? classStatefulWidget;
- late ClassElement? classState;
- @override
- late ClassElement? classBuildContext;
- @override
- late ClassElement? classKey;
- late ClassElement? classAnimationController;
- late ClassElement? classSingleTickerProviderStateMixin;
-
- FlutterStatefulWidgetWithAnimationControllerSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
-
- // Checked by isValid().
- final classStatefulWidget = this.classStatefulWidget!;
- final classState = this.classState!;
- final classAnimationController = this.classAnimationController!;
- final classSingleTickerProviderStateMixin =
- this.classSingleTickerProviderStateMixin!;
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- // Write the StatefulWidget class
- builder.writeClassDeclaration(
- widgetClassName,
- nameGroupName: 'name',
- superclass: _getType(classStatefulWidget),
- membersWriter: () {
- writeWidgetConstructor(builder);
- builder.writeln();
- builder.writeln();
-
- writeCreateStateMethod(builder);
- },
- );
- builder.writeln();
- builder.writeln();
-
- // Write the State class.
- builder.write('class _');
- builder.addSimpleLinkedEdit('name', widgetClassName);
- builder.write('State extends ');
- builder.writeReference(classState);
- builder.write('<');
- builder.addSimpleLinkedEdit('name', widgetClassName);
- builder.writeln('>');
- builder.write(' with ');
- builder.writeReference(classSingleTickerProviderStateMixin);
- builder.writeln(' {');
- builder.write(' late ');
- builder.writeReference(classAnimationController);
- builder.writeln(' _controller;');
- builder.writeln();
- {
- // Add the initState method.
- builder.writeln(' @override');
- builder.write(' ');
- builder.writeFunctionDeclaration(
- 'initState',
- returnType: VoidTypeImpl.instance,
- bodyWriter: () {
- builder.writeln('{');
- builder.writeln(' super.initState();');
- builder.write(' _controller = ');
- builder.writeReference(classAnimationController);
- builder.writeln('(vsync: this);');
- builder.writeln(' }');
- },
- );
- }
- builder.writeln();
- {
- // Add the dispose method.
- builder.writeln(' @override');
- builder.write(' ');
- builder.writeFunctionDeclaration(
- 'dispose',
- returnType: VoidTypeImpl.instance,
- bodyWriter: () {
- builder.writeln('{');
- builder.writeln(' _controller.dispose();');
- builder.writeln(' super.dispose();');
- builder.writeln(' }');
- },
- );
- }
- builder.writeln();
- {
- writeBuildMethod(builder);
- }
- builder.write('}');
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a Flutter StatefulWidget with an AnimationController.',
- builder.sourceChange,
- );
- }
-
- @override
- Future<bool> isValid() async {
- if (!await super.isValid()) {
- return false;
- }
-
- if ((classStatefulWidget = await _getClass('StatefulWidget')) == null ||
- (classState = await _getClass('State')) == null ||
- (classBuildContext = await _getClass('BuildContext')) == null ||
- (classKey = await _getClass('Key')) == null ||
- (classAnimationController = await _getClass('AnimationController')) ==
- null ||
- (classSingleTickerProviderStateMixin =
- await _getClass('SingleTickerProviderStateMixin')) ==
- null) {
- return false;
- }
-
- return true;
- }
-
- static FlutterStatefulWidgetWithAnimationControllerSnippetProducer
- newInstance(DartSnippetRequest request) =>
- FlutterStatefulWidgetWithAnimationControllerSnippetProducer._(
- request);
-}
-
-/// Produces a [Snippet] that creates a Flutter StatelessWidget.
-class FlutterStatelessWidgetSnippetProducer extends FlutterSnippetProducer
- with FlutterWidgetSnippetProducerMixin {
- static const prefix = 'stless';
- static const label = 'Flutter Stateless Widget';
-
- late ClassElement? classStatelessWidget;
- @override
- late ClassElement? classBuildContext;
- @override
- late ClassElement? classKey;
-
- FlutterStatelessWidgetSnippetProducer._(super.request);
-
- @override
- Future<Snippet> compute() async {
- final builder = ChangeBuilder(session: request.analysisSession);
-
- // Checked by isValid().
- final classStatelessWidget = this.classStatelessWidget!;
-
- await builder.addDartFileEdit(request.filePath, (builder) {
- builder.addReplacement(request.replacementRange, (builder) {
- builder.writeClassDeclaration(
- widgetClassName,
- nameGroupName: 'name',
- superclass: _getType(classStatelessWidget),
- membersWriter: () {
- writeWidgetConstructor(builder);
- builder.writeln();
- builder.writeln();
-
- writeBuildMethod(builder);
- },
- );
- });
- });
-
- return Snippet(
- prefix,
- label,
- 'Insert a Flutter StatelessWidget.',
- builder.sourceChange,
- );
- }
-
- @override
- Future<bool> isValid() async {
- if (!await super.isValid()) {
- return false;
- }
-
- if ((classStatelessWidget = await _getClass('StatelessWidget')) == null ||
- (classBuildContext = await _getClass('BuildContext')) == null ||
- (classKey = await _getClass('Key')) == null) {
- return false;
- }
-
- return true;
- }
-
- static FlutterStatelessWidgetSnippetProducer newInstance(
- DartSnippetRequest request) =>
- FlutterStatelessWidgetSnippetProducer._(request);
-}
-
-/// A mixin that provides some common methods for producers that build snippets
-/// for Flutter widget classes.
-mixin FlutterWidgetSnippetProducerMixin on FlutterSnippetProducer {
- ClassElement? get classBuildContext;
- ClassElement? get classKey;
- String get widgetClassName => 'MyWidget';
-
- void writeBuildMethod(DartEditBuilder builder) {
- // Checked by isValid() before this will be called.
- final classBuildContext = this.classBuildContext!;
- final classWidget = this.classWidget!;
- final classContainer = this.classContainer!;
-
- // Add the build method.
- builder.writeln(' @override');
- builder.write(' ');
- builder.writeFunctionDeclaration(
- 'build',
- returnType: _getType(classWidget),
- parameterWriter: () {
- builder.writeParameter(
- 'context',
- type: _getType(classBuildContext),
- );
- },
- bodyWriter: () {
- builder.writeln('{');
- builder.write(' return ');
- builder.selectAll(() {
- builder.writeType(_getType(classContainer));
- builder.write('()');
- });
- builder.writeln(';');
- builder.writeln(' }');
- },
- );
- }
-
- void writeCreateStateMethod(DartEditBuilder builder) {
- builder.writeln(' @override');
- builder.write(' State<');
- builder.addSimpleLinkedEdit('name', widgetClassName);
- builder.write('> createState() => _');
- builder.addSimpleLinkedEdit('name', widgetClassName);
- builder.writeln('State();');
- }
-
- void writeWidgetConstructor(DartEditBuilder builder) {
- // Checked by isValid() before this will be called.
- final classKey = this.classKey!;
-
- String keyName;
- DartType? keyType;
- void Function()? keyInitializer;
- if (useSuperParams) {
- keyName = 'super.key';
- } else {
- keyName = 'key';
- keyType = _getType(classKey, nullableSuffix);
- keyInitializer = () => builder.write('super(key: key)');
- }
-
- builder.write(' ');
- builder.writeConstructorDeclaration(
- widgetClassName,
- classNameGroupName: 'name',
- isConst: true,
- parameterWriter: () {
- builder.write('{');
- builder.writeParameter(keyName, type: keyType);
- builder.write('}');
- },
- initializerWriter: keyInitializer,
- );
- }
-}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget.dart b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget.dart
new file mode 100644
index 0000000..7babe62
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget.dart
@@ -0,0 +1,90 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a Flutter StatefulWidget and related State
+/// class.
+class FlutterStatefulWidget extends FlutterSnippetProducer
+ with FlutterWidgetSnippetProducerMixin {
+ static const prefix = 'stful';
+ static const label = 'Flutter Stateful Widget';
+
+ late ClassElement? classStatefulWidget;
+ late ClassElement? classState;
+ @override
+ late ClassElement? classBuildContext;
+ @override
+ late ClassElement? classKey;
+
+ FlutterStatefulWidget(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+
+ // Checked by isValid().
+ final classStatefulWidget = this.classStatefulWidget!;
+ final classState = this.classState!;
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ // Write the StatefulWidget class
+ builder.writeClassDeclaration(
+ widgetClassName,
+ nameGroupName: 'name',
+ superclass: getType(classStatefulWidget),
+ membersWriter: () {
+ writeWidgetConstructor(builder);
+ builder.writeln();
+ builder.writeln();
+
+ writeCreateStateMethod(builder);
+ },
+ );
+ builder.writeln();
+ builder.writeln();
+
+ // Write the State class.
+ builder.write('class _');
+ builder.addSimpleLinkedEdit('name', widgetClassName);
+ builder.write('State extends ');
+ builder.writeReference(classState);
+ builder.write('<');
+ builder.addSimpleLinkedEdit('name', widgetClassName);
+ builder.writeln('> {');
+ {
+ writeBuildMethod(builder);
+ }
+ builder.write('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a Flutter StatefulWidget.',
+ builder.sourceChange,
+ );
+ }
+
+ @override
+ Future<bool> isValid() async {
+ if (!await super.isValid()) {
+ return false;
+ }
+
+ if ((classStatefulWidget = await getClass('StatefulWidget')) == null ||
+ (classState = await getClass('State')) == null ||
+ (classBuildContext = await getClass('BuildContext')) == null ||
+ (classKey = await getClass('Key')) == null) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart
new file mode 100644
index 0000000..48f9208
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart
@@ -0,0 +1,142 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a Flutter StatefulWidget with a
+/// AnimationController and related State class.
+class FlutterStatefulWidgetWithAnimationController
+ extends FlutterSnippetProducer with FlutterWidgetSnippetProducerMixin {
+ static const prefix = 'stanim';
+ static const label = 'Flutter Widget with AnimationController';
+
+ late ClassElement? classStatefulWidget;
+ late ClassElement? classState;
+ @override
+ late ClassElement? classBuildContext;
+ @override
+ late ClassElement? classKey;
+ late ClassElement? classAnimationController;
+ late ClassElement? classSingleTickerProviderStateMixin;
+
+ FlutterStatefulWidgetWithAnimationController(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+
+ // Checked by isValid().
+ final classStatefulWidget = this.classStatefulWidget!;
+ final classState = this.classState!;
+ final classAnimationController = this.classAnimationController!;
+ final classSingleTickerProviderStateMixin =
+ this.classSingleTickerProviderStateMixin!;
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ // Write the StatefulWidget class
+ builder.writeClassDeclaration(
+ widgetClassName,
+ nameGroupName: 'name',
+ superclass: getType(classStatefulWidget),
+ membersWriter: () {
+ writeWidgetConstructor(builder);
+ builder.writeln();
+ builder.writeln();
+
+ writeCreateStateMethod(builder);
+ },
+ );
+ builder.writeln();
+ builder.writeln();
+
+ // Write the State class.
+ builder.write('class _');
+ builder.addSimpleLinkedEdit('name', widgetClassName);
+ builder.write('State extends ');
+ builder.writeReference(classState);
+ builder.write('<');
+ builder.addSimpleLinkedEdit('name', widgetClassName);
+ builder.writeln('>');
+ builder.write(' with ');
+ builder.writeReference(classSingleTickerProviderStateMixin);
+ builder.writeln(' {');
+ builder.write(' late ');
+ builder.writeReference(classAnimationController);
+ builder.writeln(' _controller;');
+ builder.writeln();
+ {
+ // Add the initState method.
+ builder.writeln(' @override');
+ builder.write(' ');
+ builder.writeFunctionDeclaration(
+ 'initState',
+ returnType: VoidTypeImpl.instance,
+ bodyWriter: () {
+ builder.writeln('{');
+ builder.writeln(' super.initState();');
+ builder.write(' _controller = ');
+ builder.writeReference(classAnimationController);
+ builder.writeln('(vsync: this);');
+ builder.writeln(' }');
+ },
+ );
+ }
+ builder.writeln();
+ {
+ // Add the dispose method.
+ builder.writeln(' @override');
+ builder.write(' ');
+ builder.writeFunctionDeclaration(
+ 'dispose',
+ returnType: VoidTypeImpl.instance,
+ bodyWriter: () {
+ builder.writeln('{');
+ builder.writeln(' _controller.dispose();');
+ builder.writeln(' super.dispose();');
+ builder.writeln(' }');
+ },
+ );
+ }
+ builder.writeln();
+ {
+ writeBuildMethod(builder);
+ }
+ builder.write('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a Flutter StatefulWidget with an AnimationController.',
+ builder.sourceChange,
+ );
+ }
+
+ @override
+ Future<bool> isValid() async {
+ if (!await super.isValid()) {
+ return false;
+ }
+
+ if ((classStatefulWidget = await getClass('StatefulWidget')) == null ||
+ (classState = await getClass('State')) == null ||
+ (classBuildContext = await getClass('BuildContext')) == null ||
+ (classKey = await getClass('Key')) == null ||
+ (classAnimationController = await getClass('AnimationController')) ==
+ null ||
+ (classSingleTickerProviderStateMixin =
+ await getClass('SingleTickerProviderStateMixin')) ==
+ null) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateless_widget.dart b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateless_widget.dart
new file mode 100644
index 0000000..e7711d3
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_stateless_widget.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a Flutter StatelessWidget.
+class FlutterStatelessWidget extends FlutterSnippetProducer
+ with FlutterWidgetSnippetProducerMixin {
+ static const prefix = 'stless';
+ static const label = 'Flutter Stateless Widget';
+
+ late ClassElement? classStatelessWidget;
+ @override
+ late ClassElement? classBuildContext;
+ @override
+ late ClassElement? classKey;
+
+ FlutterStatelessWidget(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+
+ // Checked by isValid().
+ final classStatelessWidget = this.classStatelessWidget!;
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ builder.writeClassDeclaration(
+ widgetClassName,
+ nameGroupName: 'name',
+ superclass: getType(classStatelessWidget),
+ membersWriter: () {
+ writeWidgetConstructor(builder);
+ builder.writeln();
+ builder.writeln();
+
+ writeBuildMethod(builder);
+ },
+ );
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a Flutter StatelessWidget.',
+ builder.sourceChange,
+ );
+ }
+
+ @override
+ Future<bool> isValid() async {
+ if (!await super.isValid()) {
+ return false;
+ }
+
+ if ((classStatelessWidget = await getClass('StatelessWidget')) == null ||
+ (classBuildContext = await getClass('BuildContext')) == null ||
+ (classKey = await getClass('Key')) == null) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/for_in_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/for_in_statement.dart
new file mode 100644
index 0000000..c9c481b
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/for_in_statement.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a `for in` loop.
+class ForInStatement extends DartSnippetProducer {
+ static const prefix = 'forin';
+ static const label = 'for in';
+
+ ForInStatement(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+ final varOrFinal =
+ isLintEnabled(LintNames.prefer_final_locals) ? 'final' : 'var';
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ builder.write('for ($varOrFinal ');
+ builder.addSimpleLinkedEdit('elementName', 'element');
+ builder.write(' in ');
+ builder.addSimpleLinkedEdit('collectionName', 'collection');
+ builder.writeln(') {');
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndented('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a for-in loop.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/for_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/for_statement.dart
new file mode 100644
index 0000000..557bb11
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/for_statement.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a `for` loop.
+class ForStatement extends DartSnippetProducer {
+ static const prefix = 'for';
+ static const label = 'for';
+
+ ForStatement(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ builder.write('for (var i = 0; i < ');
+ builder.addSimpleLinkedEdit('count', 'count');
+ builder.writeln('; i++) {');
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndented('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a for loop.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/function_declaration.dart b/pkg/analysis_server/lib/src/services/snippets/dart/function_declaration.dart
new file mode 100644
index 0000000..5471752
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/function_declaration.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a function definition.
+class FunctionDeclaration extends DartSnippetProducer {
+ static const prefix = 'fun';
+ static const label = 'fun';
+
+ FunctionDeclaration(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+
+ builder.addSimpleLinkedEdit('returnType', 'void');
+ builder.write(' ');
+ builder.addSimpleLinkedEdit('name', 'name');
+ builder.write('(');
+ builder.addSimpleLinkedEdit('params', 'params');
+ builder.writeln(') {');
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndented('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a function definition.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/if_else_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/if_else_statement.dart
new file mode 100644
index 0000000..78b1b88
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/if_else_statement.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates an if/else statement.
+class IfElseStatement extends DartSnippetProducer {
+ static const prefix = 'ife';
+ static const label = 'ife';
+
+ IfElseStatement(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ void writeIndentedln(String string) =>
+ builder.writeln('$indent$string');
+ builder.write('if (');
+ builder.addSimpleLinkedEdit('condition', 'condition');
+ builder.writeln(') {');
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndentedln('} else {');
+ writeIndentedln(' ');
+ writeIndented('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert an if/else statement.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/if_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/if_statement.dart
new file mode 100644
index 0000000..3f667b1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/if_statement.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates an if statement.
+class IfStatement extends DartSnippetProducer {
+ static const prefix = 'if';
+ static const label = 'if';
+
+ IfStatement(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ builder.write('if (');
+ builder.addSimpleLinkedEdit('condition', 'condition');
+ builder.writeln(') {');
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndented('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert an if statement.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/main_function.dart b/pkg/analysis_server/lib/src/services/snippets/dart/main_function.dart
new file mode 100644
index 0000000..69976c5
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/main_function.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a top-level `main` function.
+///
+/// A `List<String> args` parameter will be included when generating inside a
+/// file in `bin` or `tool` folders.
+class MainFunction extends DartSnippetProducer {
+ static const prefix = 'main';
+ static const label = 'main()';
+
+ MainFunction(super.request);
+
+ /// Whether to insert a `List<String> args` parameter in the generated
+ /// function.
+ ///
+ /// The parameter is suppressed for any known test directories.
+ bool get _insertArgsParameter => !isInTestDirectory;
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+
+ final typeProvider = request.unit.typeProvider;
+ final listString = typeProvider.listType(typeProvider.stringType);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ builder.writeFunctionDeclaration(
+ 'main',
+ returnType: VoidTypeImpl.instance,
+ parameterWriter: _insertArgsParameter
+ ? () => builder.writeParameter('args', type: listString)
+ : null,
+ bodyWriter: () {
+ builder.writeln('{');
+ builder.write(' ');
+ builder.selectHere();
+ builder.writeln();
+ builder.write('}');
+ },
+ );
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a main function, used as an entry point.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/snippet_manager.dart b/pkg/analysis_server/lib/src/services/snippets/dart/snippet_manager.dart
deleted file mode 100644
index 006870c..0000000
--- a/pkg/analysis_server/lib/src/services/snippets/dart/snippet_manager.dart
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:analysis_server/src/protocol_server.dart';
-import 'package:analysis_server/src/provisional/completion/completion_core.dart';
-import 'package:analysis_server/src/services/snippets/dart/dart_snippet_producers.dart';
-import 'package:analysis_server/src/services/snippets/dart/flutter_snippet_producers.dart';
-import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart';
-import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/source/source_range.dart';
-import 'package:analyzer/src/util/file_paths.dart' as file_paths;
-import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart';
-
-typedef SnippetProducerGenerator = SnippetProducer Function(DartSnippetRequest);
-
-/// [DartSnippetManager] determines if a snippet request is Dart specific
-/// and forwards those requests to all Snippet Producers that return `true` from
-/// their `isValid()` method.
-class DartSnippetManager {
- final producerGenerators =
- const <SnippetContext, List<SnippetProducerGenerator>>{
- SnippetContext.atTopLevel: [
- DartMainFunctionSnippetProducer.newInstance,
- FlutterStatefulWidgetSnippetProducer.newInstance,
- FlutterStatefulWidgetWithAnimationControllerSnippetProducer.newInstance,
- FlutterStatelessWidgetSnippetProducer.newInstance,
- DartClassSnippetProducer.newInstance,
- DartFunctionSnippetProducer.newInstance,
- ],
- SnippetContext.inBlock: [
- DartFunctionSnippetProducer.newInstance,
- DartDoWhileLoopSnippetProducer.newInstance,
- DartForInLoopSnippetProducer.newInstance,
- DartForLoopSnippetProducer.newInstance,
- DartIfElseSnippetProducer.newInstance,
- DartIfSnippetProducer.newInstance,
- DartSwitchSnippetProducer.newInstance,
- DartTryCatchSnippetProducer.newInstance,
- DartWhileLoopSnippetProducer.newInstance,
- DartTestBlockSnippetProducer.newInstance,
- DartTestGroupBlockSnippetProducer.newInstance,
- ],
- SnippetContext.inClass: [
- DartFunctionSnippetProducer.newInstance,
- ]
- };
-
- Future<List<Snippet>> computeSnippets(
- DartSnippetRequest request,
- ) async {
- var pathContext = request.resourceProvider.pathContext;
- if (!file_paths.isDart(pathContext, request.filePath)) {
- return const [];
- }
-
- try {
- final snippets = <Snippet>[];
- final generators = producerGenerators[request.context];
- if (generators == null) {
- return snippets;
- }
- for (final generator in generators) {
- final producer = generator(request);
- if (await producer.isValid()) {
- snippets.add(await producer.compute());
- }
- }
- return snippets;
- } on InconsistentAnalysisException {
- // The state of the code being analyzed has changed, so results are likely
- // to be inconsistent. Just abort the operation.
- throw AbortCompletion();
- }
- }
-}
-
-/// The information about a request for a list of snippets within a Dart file.
-class DartSnippetRequest {
- /// The resolved unit for the file that snippets are being requested for.
- final ResolvedUnitResult unit;
-
- /// The path of the file snippets are being requested for.
- final String filePath;
-
- /// The offset within the source at which snippets are being
- /// requested for.
- final int offset;
-
- /// The context in which the snippet request is being made.
- late final SnippetContext context;
-
- /// The source range that represents the region of text that should be
- /// replaced if the snippet is selected.
- late final SourceRange replacementRange;
-
- DartSnippetRequest({
- required this.unit,
- required this.offset,
- }) : filePath = unit.path {
- final target = CompletionTarget.forOffset(unit.unit, offset);
- context = _getContext(target);
- replacementRange = target.computeReplacementRange(offset);
- }
-
- /// The analysis session that produced the elements of the request.
- AnalysisSession get analysisSession => unit.session;
-
- /// The resource provider associated with this request.
- ResourceProvider get resourceProvider => analysisSession.resourceProvider;
-
- static SnippetContext _getContext(CompletionTarget target) {
- final entity = target.entity;
- if (entity is Token) {
- final tokenType = (entity.beforeSynthetic ?? entity).type;
-
- if (tokenType == TokenType.MULTI_LINE_COMMENT ||
- tokenType == TokenType.SINGLE_LINE_COMMENT) {
- return SnippetContext.inComment;
- }
-
- if (tokenType == TokenType.STRING ||
- tokenType == TokenType.STRING_INTERPOLATION_EXPRESSION ||
- tokenType == TokenType.STRING_INTERPOLATION_IDENTIFIER) {
- return SnippetContext.inString;
- }
- }
-
- AstNode? node = target.containingNode;
- while (node != null) {
- if (node is Comment) {
- return SnippetContext.inComment;
- }
-
- if (node is StringLiteral) {
- return SnippetContext.inString;
- }
-
- if (node is Block) {
- return SnippetContext.inBlock;
- }
-
- if (node is Statement || node is Expression || node is Annotation) {
- return SnippetContext.inExpressionOrStatement;
- }
-
- if (node is BlockFunctionBody) {
- return SnippetContext.inBlock;
- }
-
- if (node is ClassOrMixinDeclaration || node is ExtensionDeclaration) {
- return SnippetContext.inClass;
- }
-
- node = node.parent;
- }
-
- return SnippetContext.atTopLevel;
- }
-}
-
-class Snippet {
- /// The text the user will type to use this snippet.
- final String prefix;
-
- /// The label/title of this snippet.
- final String label;
-
- /// A description of/documentation for the snippet.
- final String? documentation;
-
- /// The source changes to be made to insert this snippet.
- final SourceChange change;
-
- Snippet(
- this.prefix,
- this.label,
- this.documentation,
- this.change,
- );
-}
-
-/// The context in which a snippet request was made.
-///
-/// This is used to filter the available snippets (for example preventing
-/// snippets that create classes showing up when inside an existing class or
-/// function body).
-enum SnippetContext {
- atTopLevel,
- inClass,
- inBlock,
- inExpressionOrStatement,
- inComment,
- inString,
-}
-
-abstract class SnippetProducer {
- final DartSnippetRequest request;
-
- SnippetProducer(this.request);
-
- Future<Snippet> compute();
-
- Future<bool> isValid() async {
- // File edit builders will not produce edits for files outside of the
- // analysis roots so we should not try to produce any snippets.
- final analysisContext = request.analysisSession.analysisContext;
- return analysisContext.contextRoot.isAnalyzed(request.filePath);
- }
-}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/switch_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/switch_statement.dart
new file mode 100644
index 0000000..5896568
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/switch_statement.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates an if statement.
+class SwitchStatement extends DartSnippetProducer {
+ static const prefix = 'switch';
+ static const label = 'switch case';
+
+ SwitchStatement(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ void writeIndentedln(String string) =>
+ builder.writeln('$indent$string');
+ builder.write('switch (');
+ builder.addSimpleLinkedEdit('expression', 'expression');
+ builder.writeln(') {');
+ writeIndented(' case ');
+ builder.addSimpleLinkedEdit('value', 'value');
+ builder.writeln(':');
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndentedln(' break;');
+ writeIndentedln(' default:');
+ writeIndented('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a switch statement.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/test_definition.dart b/pkg/analysis_server/lib/src/services/snippets/dart/test_definition.dart
new file mode 100644
index 0000000..29a348d
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/test_definition.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a `test()` block.
+class TestDefinition extends DartSnippetProducer {
+ static const prefix = 'test';
+ static const label = 'test';
+
+ TestDefinition(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ builder.write("test('");
+ builder.addSimpleLinkedEdit('testName', 'test name');
+ builder.writeln("', () {");
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndented('});');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a test block.',
+ builder.sourceChange,
+ );
+ }
+
+ @override
+ Future<bool> isValid() async {
+ if (!await super.isValid()) {
+ return false;
+ }
+
+ return isInTestDirectory;
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/test_group_definition.dart b/pkg/analysis_server/lib/src/services/snippets/dart/test_group_definition.dart
new file mode 100644
index 0000000..5bb4616
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/test_group_definition.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a `test()` block.
+class TestGroupDefinition extends DartSnippetProducer {
+ static const prefix = 'group';
+ static const label = 'group';
+
+ TestGroupDefinition(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ builder.write("group('");
+ builder.addSimpleLinkedEdit('groupName', 'group name');
+ builder.writeln("', () {");
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndented('});');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a test group block.',
+ builder.sourceChange,
+ );
+ }
+
+ @override
+ Future<bool> isValid() async {
+ if (!await super.isValid()) {
+ return false;
+ }
+
+ return isInTestDirectory;
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/try_catch_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/try_catch_statement.dart
new file mode 100644
index 0000000..5320ab3
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/try_catch_statement.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a try/catch statement.
+class TryCatchStatement extends DartSnippetProducer {
+ static const prefix = 'try';
+ static const label = 'try';
+
+ TryCatchStatement(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ void writeIndentedln(String string) =>
+ builder.writeln('$indent$string');
+ builder.writeln('try {');
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndented('} catch (');
+ builder.addLinkedEdit('exceptionName', (builder) {
+ builder.write('e');
+ });
+ builder.writeln(') {');
+ writeIndentedln(' ');
+ writeIndented('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a try/catch statement.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart/while_statement.dart b/pkg/analysis_server/lib/src/services/snippets/dart/while_statement.dart
new file mode 100644
index 0000000..48776ad
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/while_statement.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+
+/// Produces a [Snippet] that creates a `while` loop.
+class WhileStatement extends DartSnippetProducer {
+ static const prefix = 'while';
+ static const label = 'while';
+
+ WhileStatement(super.request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+ final indent = utils.getLinePrefix(request.offset);
+
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ void writeIndented(String string) => builder.write('$indent$string');
+ builder.write('while (');
+ builder.addSimpleLinkedEdit('condition', 'condition');
+ builder.writeln(') {');
+ writeIndented(' ');
+ builder.selectHere();
+ builder.writeln();
+ writeIndented('}');
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a while loop.',
+ builder.sourceChange,
+ );
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/dart_snippet_request.dart b/pkg/analysis_server/lib/src/services/snippets/dart_snippet_request.dart
new file mode 100644
index 0000000..93ab0a8
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart_snippet_request.dart
@@ -0,0 +1,96 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/snippets/snippet_context.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart';
+
+/// The information about a request for a list of snippets within a Dart file.
+class DartSnippetRequest {
+ /// The resolved unit for the file that snippets are being requested for.
+ final ResolvedUnitResult unit;
+
+ /// The path of the file snippets are being requested for.
+ final String filePath;
+
+ /// The offset within the source at which snippets are being
+ /// requested for.
+ final int offset;
+
+ /// The context in which the snippet request is being made.
+ late final SnippetContext context;
+
+ /// The source range that represents the region of text that should be
+ /// replaced if the snippet is selected.
+ late final SourceRange replacementRange;
+
+ DartSnippetRequest({
+ required this.unit,
+ required this.offset,
+ }) : filePath = unit.path {
+ final target = CompletionTarget.forOffset(unit.unit, offset);
+ context = _getContext(target);
+ replacementRange = target.computeReplacementRange(offset);
+ }
+
+ /// The analysis session that produced the elements of the request.
+ AnalysisSession get analysisSession => unit.session;
+
+ /// The resource provider associated with this request.
+ ResourceProvider get resourceProvider => analysisSession.resourceProvider;
+
+ static SnippetContext _getContext(CompletionTarget target) {
+ final entity = target.entity;
+ if (entity is Token) {
+ final tokenType = (entity.beforeSynthetic ?? entity).type;
+
+ if (tokenType == TokenType.MULTI_LINE_COMMENT ||
+ tokenType == TokenType.SINGLE_LINE_COMMENT) {
+ return SnippetContext.inComment;
+ }
+
+ if (tokenType == TokenType.STRING ||
+ tokenType == TokenType.STRING_INTERPOLATION_EXPRESSION ||
+ tokenType == TokenType.STRING_INTERPOLATION_IDENTIFIER) {
+ return SnippetContext.inString;
+ }
+ }
+
+ AstNode? node = target.containingNode;
+ while (node != null) {
+ if (node is Comment) {
+ return SnippetContext.inComment;
+ }
+
+ if (node is StringLiteral) {
+ return SnippetContext.inString;
+ }
+
+ if (node is Block) {
+ return SnippetContext.inBlock;
+ }
+
+ if (node is Statement || node is Expression || node is Annotation) {
+ return SnippetContext.inExpressionOrStatement;
+ }
+
+ if (node is BlockFunctionBody) {
+ return SnippetContext.inBlock;
+ }
+
+ if (node is ClassOrMixinDeclaration || node is ExtensionDeclaration) {
+ return SnippetContext.inClass;
+ }
+
+ node = node.parent;
+ }
+
+ return SnippetContext.atTopLevel;
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/snippet.dart b/pkg/analysis_server/lib/src/services/snippets/snippet.dart
new file mode 100644
index 0000000..a19bc96
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/snippet.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+
+class Snippet {
+ /// The text the user will type to use this snippet.
+ final String prefix;
+
+ /// The label/title of this snippet.
+ final String label;
+
+ /// A description of/documentation for the snippet.
+ final String? documentation;
+
+ /// The source changes to be made to insert this snippet.
+ final SourceChange change;
+
+ Snippet(
+ this.prefix,
+ this.label,
+ this.documentation,
+ this.change,
+ );
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/snippet_context.dart b/pkg/analysis_server/lib/src/services/snippets/snippet_context.dart
new file mode 100644
index 0000000..9b5d3af
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/snippet_context.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2022, 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.
+
+/// The context in which a snippet request was made.
+///
+/// This is used to filter the available snippets (for example preventing
+/// snippets that create classes showing up when inside an existing class or
+/// function body).
+enum SnippetContext {
+ atTopLevel,
+ inClass,
+ inBlock,
+ inExpressionOrStatement,
+ inComment,
+ inString,
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/snippet_manager.dart b/pkg/analysis_server/lib/src/services/snippets/snippet_manager.dart
new file mode 100644
index 0000000..26b94bf
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/snippet_manager.dart
@@ -0,0 +1,90 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/provisional/completion/completion_core.dart';
+import 'package:analysis_server/src/services/snippets/dart/class_declaration.dart';
+import 'package:analysis_server/src/services/snippets/dart/do_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/flutter_stateful_widget.dart';
+import 'package:analysis_server/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart';
+import 'package:analysis_server/src/services/snippets/dart/flutter_stateless_widget.dart';
+import 'package:analysis_server/src/services/snippets/dart/for_in_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/for_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/function_declaration.dart';
+import 'package:analysis_server/src/services/snippets/dart/if_else_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/if_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/main_function.dart';
+import 'package:analysis_server/src/services/snippets/dart/switch_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/test_definition.dart';
+import 'package:analysis_server/src/services/snippets/dart/test_group_definition.dart';
+import 'package:analysis_server/src/services/snippets/dart/try_catch_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/while_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart_snippet_request.dart';
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_context.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+
+typedef SnippetProducerGenerator = SnippetProducer Function(DartSnippetRequest);
+
+/// [DartSnippetManager] determines if a snippet request is Dart specific
+/// and forwards those requests to all Snippet Producers that return `true` from
+/// their `isValid()` method.
+class DartSnippetManager {
+ final producerGenerators =
+ const <SnippetContext, List<SnippetProducerGenerator>>{
+ SnippetContext.atTopLevel: [
+ ClassDeclaration.new,
+ FlutterStatefulWidget.new,
+ FlutterStatefulWidgetWithAnimationController.new,
+ FlutterStatelessWidget.new,
+ FunctionDeclaration.new,
+ MainFunction.new,
+ ],
+ SnippetContext.inBlock: [
+ DoStatement.new,
+ ForInStatement.new,
+ ForStatement.new,
+ FunctionDeclaration.new,
+ IfElseStatement.new,
+ IfStatement.new,
+ SwitchStatement.new,
+ TestDefinition.new,
+ TestGroupDefinition.new,
+ TryCatchStatement.new,
+ WhileStatement.new,
+ ],
+ SnippetContext.inClass: [
+ FunctionDeclaration.new,
+ ]
+ };
+
+ Future<List<Snippet>> computeSnippets(
+ DartSnippetRequest request,
+ ) async {
+ var pathContext = request.resourceProvider.pathContext;
+ if (!file_paths.isDart(pathContext, request.filePath)) {
+ return const [];
+ }
+
+ try {
+ final snippets = <Snippet>[];
+ final generators = producerGenerators[request.context];
+ if (generators == null) {
+ return snippets;
+ }
+ for (final generator in generators) {
+ final producer = generator(request);
+ if (await producer.isValid()) {
+ snippets.add(await producer.compute());
+ }
+ }
+ return snippets;
+ } on InconsistentAnalysisException {
+ // The state of the code being analyzed has changed, so results are likely
+ // to be inconsistent. Just abort the operation.
+ throw AbortCompletion();
+ }
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/snippets/snippet_producer.dart b/pkg/analysis_server/lib/src/services/snippets/snippet_producer.dart
new file mode 100644
index 0000000..52c1307
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/snippet_producer.dart
@@ -0,0 +1,173 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analysis_server/src/services/snippets/dart_snippet_request.dart';
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/utilities/flutter.dart';
+import 'package:analyzer/dart/analysis/features.dart';
+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/analysis/session_helper.dart';
+import 'package:analyzer/src/lint/linter.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:meta/meta.dart';
+
+abstract class DartSnippetProducer extends SnippetProducer {
+ final AnalysisSessionHelper sessionHelper;
+ final CorrectionUtils utils;
+ final LibraryElement libraryElement;
+ final bool useSuperParams;
+
+ DartSnippetProducer(super.request)
+ : sessionHelper = AnalysisSessionHelper(request.analysisSession),
+ utils = CorrectionUtils(request.unit),
+ libraryElement = request.unit.libraryElement,
+ useSuperParams = request.unit.libraryElement.featureSet
+ .isEnabled(Feature.super_parameters);
+
+ bool get isInTestDirectory {
+ final path = request.unit.path;
+ return LinterContextImpl.testDirectories
+ .any((testDir) => path.contains(testDir));
+ }
+
+ /// The nullable suffix to use in this library.
+ NullabilitySuffix get nullableSuffix => libraryElement.isNonNullableByDefault
+ ? NullabilitySuffix.question
+ : NullabilitySuffix.none;
+
+ bool isLintEnabled(String name) {
+ var analysisOptions = sessionHelper.session.analysisContext.analysisOptions;
+ return analysisOptions.isLintEnabled(name);
+ }
+}
+
+abstract class FlutterSnippetProducer extends DartSnippetProducer {
+ final flutter = Flutter.instance;
+
+ late ClassElement? classWidget;
+ late ClassElement? classContainer;
+
+ FlutterSnippetProducer(super.request);
+
+ Future<ClassElement?> getClass(String name) =>
+ sessionHelper.getClass(flutter.widgetsUri, name);
+
+ DartType getType(
+ ClassElement classElement, [
+ NullabilitySuffix nullabilitySuffix = NullabilitySuffix.none,
+ ]) =>
+ classElement.instantiate(
+ typeArguments: const [],
+ nullabilitySuffix: nullabilitySuffix,
+ );
+
+ @override
+ @mustCallSuper
+ Future<bool> isValid() async {
+ if ((classWidget = await getClass('Widget')) == null) {
+ return false;
+ }
+
+ if ((classContainer = await getClass('Container')) == null) {
+ return false;
+ }
+
+ return super.isValid();
+ }
+}
+
+/// A mixin that provides some common methods for producers that build snippets
+/// for Flutter widget classes.
+mixin FlutterWidgetSnippetProducerMixin on FlutterSnippetProducer {
+ ClassElement? get classBuildContext;
+ ClassElement? get classKey;
+ String get widgetClassName => 'MyWidget';
+
+ void writeBuildMethod(DartEditBuilder builder) {
+ // Checked by isValid() before this will be called.
+ final classBuildContext = this.classBuildContext!;
+ final classWidget = this.classWidget!;
+ final classContainer = this.classContainer!;
+
+ // Add the build method.
+ builder.writeln(' @override');
+ builder.write(' ');
+ builder.writeFunctionDeclaration(
+ 'build',
+ returnType: getType(classWidget),
+ parameterWriter: () {
+ builder.writeParameter(
+ 'context',
+ type: getType(classBuildContext),
+ );
+ },
+ bodyWriter: () {
+ builder.writeln('{');
+ builder.write(' return ');
+ builder.selectAll(() {
+ builder.writeType(getType(classContainer));
+ builder.write('()');
+ });
+ builder.writeln(';');
+ builder.writeln(' }');
+ },
+ );
+ }
+
+ void writeCreateStateMethod(DartEditBuilder builder) {
+ builder.writeln(' @override');
+ builder.write(' State<');
+ builder.addSimpleLinkedEdit('name', widgetClassName);
+ builder.write('> createState() => _');
+ builder.addSimpleLinkedEdit('name', widgetClassName);
+ builder.writeln('State();');
+ }
+
+ void writeWidgetConstructor(DartEditBuilder builder) {
+ // Checked by isValid() before this will be called.
+ final classKey = this.classKey!;
+
+ String keyName;
+ DartType? keyType;
+ void Function()? keyInitializer;
+ if (useSuperParams) {
+ keyName = 'super.key';
+ } else {
+ keyName = 'key';
+ keyType = getType(classKey, nullableSuffix);
+ keyInitializer = () => builder.write('super(key: key)');
+ }
+
+ builder.write(' ');
+ builder.writeConstructorDeclaration(
+ widgetClassName,
+ classNameGroupName: 'name',
+ isConst: true,
+ parameterWriter: () {
+ builder.write('{');
+ builder.writeParameter(keyName, type: keyType);
+ builder.write('}');
+ },
+ initializerWriter: keyInitializer,
+ );
+ }
+}
+
+abstract class SnippetProducer {
+ final DartSnippetRequest request;
+
+ SnippetProducer(this.request);
+
+ Future<Snippet> compute();
+
+ Future<bool> isValid() async {
+ // File edit builders will not produce edits for files outside of the
+ // analysis roots so we should not try to produce any snippets.
+ final analysisContext = request.analysisSession.analysisContext;
+ return analysisContext.contextRoot.isAnalyzed(request.filePath);
+ }
+}
diff --git a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
index eadd2aa..e334a60 100644
--- a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
@@ -677,9 +677,11 @@
class ExtractWidgetRefactorCodeActionsTest extends AbstractCodeActionsTest {
final extractWidgetTitle = 'Extract Widget';
- /// Nullability suffix expected in this test class.
- String get expectedNullableSuffix => '?';
-
+ String get expectedNewWidgetConstructorDeclaration => '''
+const NewWidget({
+ super.key,
+ });
+''';
@override
void setUp() {
super.setUp();
@@ -728,10 +730,7 @@
}
class NewWidget extends StatelessWidget {
- const NewWidget({
- Key$expectedNullableSuffix key,
- }) : super(key: key);
-
+ $expectedNewWidgetConstructorDeclaration
@override
Widget build(BuildContext context) {
return new Column(
@@ -776,7 +775,11 @@
class ExtractWidgetRefactorCodeActionsWithoutNullSafetyTest
extends ExtractWidgetRefactorCodeActionsTest {
@override
- String get expectedNullableSuffix => '';
+ String get expectedNewWidgetConstructorDeclaration => '''
+const NewWidget({
+ Key key,
+ }) : super(key: key);
+''';
@override
String get testPackageLanguageVersion => '2.9';
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index 30298a0..9684d3f 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -5,8 +5,22 @@
import 'package:analysis_server/lsp_protocol/protocol.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/services/linter/lint_names.dart';
-import 'package:analysis_server/src/services/snippets/dart/dart_snippet_producers.dart';
-import 'package:analysis_server/src/services/snippets/dart/flutter_snippet_producers.dart';
+import 'package:analysis_server/src/services/snippets/dart/class_declaration.dart';
+import 'package:analysis_server/src/services/snippets/dart/do_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/flutter_stateful_widget.dart';
+import 'package:analysis_server/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart';
+import 'package:analysis_server/src/services/snippets/dart/flutter_stateless_widget.dart';
+import 'package:analysis_server/src/services/snippets/dart/for_in_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/for_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/function_declaration.dart';
+import 'package:analysis_server/src/services/snippets/dart/if_else_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/if_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/main_function.dart';
+import 'package:analysis_server/src/services/snippets/dart/switch_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/test_definition.dart';
+import 'package:analysis_server/src/services/snippets/dart/test_group_definition.dart';
+import 'package:analysis_server/src/services/snippets/dart/try_catch_statement.dart';
+import 'package:analysis_server/src/services/snippets/dart/while_statement.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:collection/collection.dart';
@@ -2679,8 +2693,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartClassSnippetProducer.prefix,
- label: DartClassSnippetProducer.label,
+ prefix: ClassDeclaration.prefix,
+ label: ClassDeclaration.label,
);
expect(updated, r'''
@@ -2720,8 +2734,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartDoWhileLoopSnippetProducer.prefix,
- label: DartDoWhileLoopSnippetProducer.label,
+ prefix: DoStatement.prefix,
+ label: DoStatement.label,
);
expect(updated, r'''
@@ -2746,7 +2760,7 @@
await initializeWithSnippetSupport();
await expectNoSnippet(
content,
- FlutterStatelessWidgetSnippetProducer.prefix,
+ FlutterStatelessWidget.prefix,
);
}
@@ -2760,8 +2774,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartForLoopSnippetProducer.prefix,
- label: DartForLoopSnippetProducer.label,
+ prefix: ForStatement.prefix,
+ label: ForStatement.label,
);
expect(updated, r'''
@@ -2783,8 +2797,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartForInLoopSnippetProducer.prefix,
- label: DartForInLoopSnippetProducer.label,
+ prefix: ForInStatement.prefix,
+ label: ForInStatement.label,
);
expect(updated, r'''
@@ -2806,8 +2820,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartFunctionSnippetProducer.prefix,
- label: DartFunctionSnippetProducer.label,
+ prefix: FunctionDeclaration.prefix,
+ label: FunctionDeclaration.label,
);
expect(updated, r'''
@@ -2829,8 +2843,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartFunctionSnippetProducer.prefix,
- label: DartFunctionSnippetProducer.label,
+ prefix: FunctionDeclaration.prefix,
+ label: FunctionDeclaration.label,
);
expect(updated, r'''
@@ -2850,8 +2864,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartFunctionSnippetProducer.prefix,
- label: DartFunctionSnippetProducer.label,
+ prefix: FunctionDeclaration.prefix,
+ label: FunctionDeclaration.label,
);
expect(updated, r'''
@@ -2871,8 +2885,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartIfSnippetProducer.prefix,
- label: DartIfSnippetProducer.label,
+ prefix: IfStatement.prefix,
+ label: IfStatement.label,
);
expect(updated, r'''
@@ -2894,10 +2908,9 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartIfElseSnippetProducer.prefix,
- label: DartIfElseSnippetProducer.label,
+ prefix: IfElseStatement.prefix,
+ label: IfElseStatement.label,
);
-
expect(updated, r'''
void f() {
if (${1:condition}) {
@@ -2921,8 +2934,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartMainFunctionSnippetProducer.prefix,
- label: DartMainFunctionSnippetProducer.label,
+ prefix: MainFunction.prefix,
+ label: MainFunction.label,
);
expect(updated, r'''
@@ -2957,8 +2970,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartSwitchSnippetProducer.prefix,
- label: DartSwitchSnippetProducer.label,
+ prefix: SwitchStatement.prefix,
+ label: SwitchStatement.label,
);
expect(updated, r'''
@@ -2985,8 +2998,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartTestBlockSnippetProducer.prefix,
- label: DartTestBlockSnippetProducer.label,
+ prefix: TestDefinition.prefix,
+ label: TestDefinition.label,
);
expect(updated, r'''
@@ -3010,8 +3023,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartTestGroupBlockSnippetProducer.prefix,
- label: DartTestGroupBlockSnippetProducer.label,
+ prefix: TestGroupDefinition.prefix,
+ label: TestGroupDefinition.label,
);
expect(updated, r'''
@@ -3033,8 +3046,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartTryCatchSnippetProducer.prefix,
- label: DartTryCatchSnippetProducer.label,
+ prefix: TryCatchStatement.prefix,
+ label: TryCatchStatement.label,
);
expect(updated, r'''
@@ -3058,8 +3071,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: DartWhileLoopSnippetProducer.prefix,
- label: DartWhileLoopSnippetProducer.label,
+ prefix: WhileStatement.prefix,
+ label: WhileStatement.label,
);
expect(updated, r'''
@@ -3112,8 +3125,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: FlutterStatefulWidgetSnippetProducer.prefix,
- label: FlutterStatefulWidgetSnippetProducer.label,
+ prefix: FlutterStatefulWidget.prefix,
+ label: FlutterStatefulWidget.label,
);
expect(updated, '''
@@ -3153,9 +3166,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix:
- FlutterStatefulWidgetWithAnimationControllerSnippetProducer.prefix,
- label: FlutterStatefulWidgetWithAnimationControllerSnippetProducer.label,
+ prefix: FlutterStatefulWidgetWithAnimationController.prefix,
+ label: FlutterStatefulWidgetWithAnimationController.label,
);
expect(updated, '''
@@ -3210,8 +3222,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: FlutterStatelessWidgetSnippetProducer.prefix,
- label: FlutterStatelessWidgetSnippetProducer.label,
+ prefix: FlutterStatelessWidget.prefix,
+ label: FlutterStatelessWidget.label,
);
expect(updated, '''
@@ -3244,8 +3256,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: FlutterStatelessWidgetSnippetProducer.prefix,
- label: FlutterStatelessWidgetSnippetProducer.label,
+ prefix: FlutterStatelessWidget.prefix,
+ label: FlutterStatelessWidget.label,
);
expect(updated, '''
@@ -3274,8 +3286,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: FlutterStatelessWidgetSnippetProducer.prefix,
- label: FlutterStatelessWidgetSnippetProducer.label,
+ prefix: FlutterStatelessWidget.prefix,
+ label: FlutterStatelessWidget.label,
);
expect(updated, '''
@@ -3300,8 +3312,8 @@
await initializeWithSnippetSupport();
final updated = await expectAndApplySnippet(
content,
- prefix: FlutterStatelessWidgetSnippetProducer.prefix,
- label: FlutterStatelessWidgetSnippetProducer.label,
+ prefix: FlutterStatelessWidget.prefix,
+ label: FlutterStatelessWidget.label,
);
expect(updated, '''
@@ -3330,7 +3342,7 @@
await initializeWithSnippetSupport();
await expectNoSnippet(
content,
- FlutterStatelessWidgetSnippetProducer.prefix,
+ FlutterStatelessWidget.prefix,
);
}
diff --git a/pkg/analysis_server/test/services/refactoring/extract_widget_test.dart b/pkg/analysis_server/test/services/refactoring/extract_widget_test.dart
index ab67627..eabe685 100644
--- a/pkg/analysis_server/test/services/refactoring/extract_widget_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/extract_widget_test.dart
@@ -126,8 +126,8 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
- }) : super(key: key);
+ super.key,
+ });
@override
Widget build(BuildContext context) {
@@ -177,8 +177,8 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
- }) : super(key: key);
+ super.key,
+ });
@override
Widget build(BuildContext context) {
@@ -213,8 +213,8 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
- }) : super(key: key);
+ super.key,
+ });
@override
Widget build(BuildContext context) {
@@ -246,8 +246,8 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
- }) : super(key: key);
+ super.key,
+ });
@override
Widget build(BuildContext context) {
@@ -300,8 +300,8 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
- }) : super(key: key);
+ super.key,
+ });
@override
Widget build(BuildContext context) {
@@ -403,9 +403,9 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required this.c,
- }) : super(key: key);
+ });
final C c;
@@ -455,8 +455,8 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
- }) : super(key: key);
+ super.key,
+ });
@override
Widget build(BuildContext context) {
@@ -519,11 +519,11 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required this.foo,
required this.p1,
required this.p2,
- }) : super(key: key);
+ });
final String foo;
final String p1;
@@ -594,11 +594,11 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required this.foo,
required this.p1,
required this.p2,
- }) : super(key: key);
+ });
final String foo;
final String p1;
@@ -649,9 +649,9 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required this.field,
- }) : super(key: key);
+ });
final String field;
@@ -700,9 +700,9 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required this.c,
- }) : super(key: key);
+ });
final C c;
@@ -743,8 +743,8 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
- }) : super(key: key);
+ super.key,
+ });
@override
Widget build(BuildContext context) {
@@ -848,9 +848,9 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required this.c,
- }) : super(key: key);
+ });
final C c;
@@ -912,9 +912,9 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required this.local,
- }) : super(key: key);
+ });
final String local;
@@ -982,9 +982,9 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required String field,
- }) : _field = field, super(key: key);
+ }) : _field = field;
final String _field;
@@ -1031,10 +1031,10 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required this.field,
required String field2,
- }) : _field = field2, super(key: key);
+ }) : _field = field2;
final int field;
final String _field;
@@ -1087,10 +1087,10 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required this.field,
required this.local,
- }) : super(key: key);
+ });
final String field;
final String local;
@@ -1156,10 +1156,10 @@
class Test extends StatelessWidget {
const Test({
- Key? key,
+ super.key,
required this.index,
required this.a,
- }) : super(key: key);
+ });
final int index;
final String a;
@@ -1209,6 +1209,68 @@
RefactoringProblemSeverity.FATAL);
}
+ Future<void> test_useSuperParameters_disabled() async {
+ await indexTestUnit('''
+// No super params.
+// @dart = 2.15
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return new Row(
+ children: <Widget>[
+ new Column(
+ children: <Widget>[
+ new Text('AAA'),
+ new Text('BBB'),
+ ],
+ ),
+ new Text('CCC'),
+ new Text('DDD'),
+ ],
+ );
+ }
+}
+''');
+ _createRefactoringForStringOffset('new Column');
+
+ await _assertSuccessfulRefactoring('''
+// No super params.
+// @dart = 2.15
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return new Row(
+ children: <Widget>[
+ Test(),
+ new Text('CCC'),
+ new Text('DDD'),
+ ],
+ );
+ }
+}
+
+class Test extends StatelessWidget {
+ const Test({
+ Key? key,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return new Column(
+ children: <Widget>[
+ new Text('AAA'),
+ new Text('BBB'),
+ ],
+ );
+ }
+}
+''');
+ }
+
Future<void> _assertRefactoringChange(String expectedCode) async {
var refactoringChange = await refactoring.createChange();
this.refactoringChange = refactoringChange;
diff --git a/pkg/analysis_server/test/services/snippets/dart/class_declaration_test.dart b/pkg/analysis_server/test/services/snippets/dart/class_declaration_test.dart
new file mode 100644
index 0000000..93d7eed
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/class_declaration_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/class_declaration.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ClassDeclarationTest);
+ });
+}
+
+@reflectiveTest
+class ClassDeclarationTest extends DartSnippetProducerTest {
+ @override
+ final generator = ClassDeclaration.new;
+
+ @override
+ String get label => ClassDeclaration.label;
+
+ @override
+ String get prefix => ClassDeclaration.prefix;
+
+ Future<void> test_class() async {
+ var code = r'''
+class A {}
+
+^
+
+class B {}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+class A {}
+
+class ClassName {
+
+}
+
+class B {}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 34);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 20},
+ ],
+ 'length': 9,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/dart_snippet_producers_test.dart b/pkg/analysis_server/test/services/snippets/dart/dart_snippet_producers_test.dart
deleted file mode 100644
index 8ce0a7d..0000000
--- a/pkg/analysis_server/test/services/snippets/dart/dart_snippet_producers_test.dart
+++ /dev/null
@@ -1,974 +0,0 @@
-// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:analysis_server/src/protocol_server.dart';
-import 'package:analysis_server/src/services/snippets/dart/dart_snippet_producers.dart';
-import 'package:analysis_server/src/services/snippets/dart/snippet_manager.dart';
-import 'package:test/test.dart';
-import 'package:test_reflective_loader/test_reflective_loader.dart';
-
-import '../../../abstract_single_unit.dart';
-import 'test_support.dart';
-
-void main() {
- defineReflectiveSuite(() {
- defineReflectiveTests(DartDoWhileLoopSnippetProducerTest);
- defineReflectiveTests(DartForInLoopSnippetProducerTest);
- defineReflectiveTests(DartForLoopSnippetProducerTest);
- defineReflectiveTests(DartIfElseSnippetProducerTest);
- defineReflectiveTests(DartIfSnippetProducerTest);
- defineReflectiveTests(DartMainFunctionSnippetProducerTest);
- defineReflectiveTests(DartSwitchSnippetProducerTest);
- defineReflectiveTests(DartTryCatchSnippetProducerTest);
- defineReflectiveTests(DartWhileLoopSnippetProducerTest);
- defineReflectiveTests(DartClassSnippetProducerTest);
- defineReflectiveTests(DartFunctionSnippetProducerTest);
- defineReflectiveTests(DartTestBlockSnippetProducerTest);
- defineReflectiveTests(DartTestGroupBlockSnippetProducerTest);
- });
-}
-
-@reflectiveTest
-class DartClassSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartClassSnippetProducer.newInstance;
-
- @override
- String get label => DartClassSnippetProducer.label;
-
- @override
- String get prefix => DartClassSnippetProducer.prefix;
-
- Future<void> test_class() async {
- var code = r'''
-class A {}
-
-^
-
-class B {}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-class A {}
-
-class ClassName {
-
-}
-
-class B {}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 34);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 20},
- ],
- 'length': 9,
- 'suggestions': []
- }
- ]);
- }
-}
-
-@reflectiveTest
-class DartDoWhileLoopSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartDoWhileLoopSnippetProducer.newInstance;
-
- @override
- String get label => DartDoWhileLoopSnippetProducer.label;
-
- @override
- String get prefix => DartDoWhileLoopSnippetProducer.prefix;
-
- Future<void> test_do() async {
- var code = r'''
-void f() {
- do^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- do {
-
- } while (condition);
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 22);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 34},
- ],
- 'length': 9,
- 'suggestions': []
- }
- ]);
- }
-}
-
-@reflectiveTest
-class DartForInLoopSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartForInLoopSnippetProducer.newInstance;
-
- @override
- String get label => DartForInLoopSnippetProducer.label;
-
- @override
- String get prefix => DartForInLoopSnippetProducer.prefix;
-
- Future<void> test_for() async {
- var code = r'''
-void f() {
- forin^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- for (var element in collection) {
-
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 51);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 22},
- ],
- 'length': 7,
- 'suggestions': []
- },
- {
- 'positions': [
- {'file': testFile, 'offset': 33},
- ],
- 'length': 10,
- 'suggestions': []
- }
- ]);
- }
-}
-
-@reflectiveTest
-class DartForLoopSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartForLoopSnippetProducer.newInstance;
-
- @override
- String get label => DartForLoopSnippetProducer.label;
-
- @override
- String get prefix => DartForLoopSnippetProducer.prefix;
-
- Future<void> test_for() async {
- var code = r'''
-void f() {
- for^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- for (var i = 0; i < count; i++) {
-
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 51);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 33},
- ],
- 'length': 5,
- 'suggestions': []
- }
- ]);
- }
-}
-
-@reflectiveTest
-class DartFunctionSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartFunctionSnippetProducer.newInstance;
-
- @override
- String get label => DartFunctionSnippetProducer.label;
-
- @override
- String get prefix => DartFunctionSnippetProducer.prefix;
-
- Future<void> test_classMethod() async {
- var code = r'''
-class A {
- ^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-class A {
- void name(params) {
-
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 36);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 12},
- ],
- 'length': 4,
- 'suggestions': []
- },
- {
- 'positions': [
- {'file': testFile, 'offset': 17},
- ],
- 'length': 4,
- 'suggestions': []
- },
- {
- 'positions': [
- {'file': testFile, 'offset': 22},
- ],
- 'length': 6,
- 'suggestions': []
- },
- ]);
- }
-
- Future<void> test_nested() async {
- var code = r'''
-void a() {
- ^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void a() {
- void name(params) {
-
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 37);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 13},
- ],
- 'length': 4,
- 'suggestions': []
- },
- {
- 'positions': [
- {'file': testFile, 'offset': 18},
- ],
- 'length': 4,
- 'suggestions': []
- },
- {
- 'positions': [
- {'file': testFile, 'offset': 23},
- ],
- 'length': 6,
- 'suggestions': []
- },
- ]);
- }
-
- Future<void> test_topLevel() async {
- var code = r'''
-class A {}
-
-^
-
-class B {}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-class A {}
-
-void name(params) {
-
-}
-
-class B {}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 36);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 14},
- ],
- 'length': 4,
- 'suggestions': []
- },
- {
- 'positions': [
- {'file': testFile, 'offset': 19},
- ],
- 'length': 4,
- 'suggestions': []
- },
- {
- 'positions': [
- {'file': testFile, 'offset': 24},
- ],
- 'length': 6,
- 'suggestions': []
- },
- ]);
- }
-}
-
-@reflectiveTest
-class DartIfElseSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartIfElseSnippetProducer.newInstance;
-
- @override
- String get label => DartIfElseSnippetProducer.label;
-
- @override
- String get prefix => DartIfElseSnippetProducer.prefix;
-
- Future<void> test_ifElse() async {
- var code = r'''
-void f() {
- if^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- if (condition) {
-
- } else {
-
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 34);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 17},
- ],
- 'length': 9,
- 'suggestions': []
- }
- ]);
- }
-
- Future<void> test_ifElse_indentedInsideBlock() async {
- var code = r'''
-void f() {
- if (true) {
- if^
- }
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- if (true) {
- if (condition) {
-
- } else {
-
- }
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 52);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 33},
- ],
- 'length': 9,
- 'suggestions': []
- }
- ]);
- }
-}
-
-@reflectiveTest
-class DartIfSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartIfSnippetProducer.newInstance;
-
- @override
- String get label => DartIfSnippetProducer.label;
-
- @override
- String get prefix => DartIfSnippetProducer.prefix;
-
- Future<void> test_if() async {
- var code = r'''
-void f() {
- if^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- if (condition) {
-
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 34);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 17},
- ],
- 'length': 9,
- 'suggestions': []
- }
- ]);
- }
-
- Future<void> test_if_indentedInsideBlock() async {
- var code = r'''
-void f() {
- if (true) {
- if^
- }
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- if (true) {
- if (condition) {
-
- }
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 52);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 33},
- ],
- 'length': 9,
- 'suggestions': []
- }
- ]);
- }
-}
-
-@reflectiveTest
-class DartMainFunctionSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartMainFunctionSnippetProducer.newInstance;
-
- @override
- String get label => DartMainFunctionSnippetProducer.label;
-
- @override
- String get prefix => DartMainFunctionSnippetProducer.prefix;
-
- Future<void> test_noParams_testFolder() => testInFile(
- convertPath('$testPackageLibPath/test/foo_test.dart'),
- expectArgsParameter: false,
- );
-
- Future<void> test_params_binFolder() => testInFile(
- convertPath('$testPackageLibPath/bin/main.dart'),
- expectArgsParameter: true,
- );
-
- Future<void> test_params_projectRoot() => testInFile(
- convertPath('$testPackageRootPath/foo.dart'),
- expectArgsParameter: true,
- );
-
- Future<void> test_params_toolFolder() => testInFile(
- convertPath('$testPackageLibPath/tool/tool.dart'),
- expectArgsParameter: true,
- );
-
- Future<void> test_typedPrefix() => testInFile(
- testFile,
- code: '$prefix^',
- expectArgsParameter: true,
- );
-
- Future<void> testInFile(
- String file, {
- String code = '^',
- required bool expectArgsParameter,
- }) async {
- testFile = file;
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- final expectedParams = expectArgsParameter ? 'List<String> args' : '';
- expect(code, '''
-void main($expectedParams) {
-
-}''');
- expect(snippet.change.selection!.file, file);
- expect(snippet.change.selection!.offset, 16 + expectedParams.length);
- expect(snippet.change.linkedEditGroups, isEmpty);
- }
-}
-
-abstract class DartSnippetProducerTest extends AbstractSingleUnitTest {
- SnippetProducerGenerator get generator;
- String get label;
- String get prefix;
-
- /// Override the package root because it usually contains /test/ and some
- /// snippets behave differently for test files.
- @override
- String get testPackageRootPath => '$workspaceRootPath/my_package';
-
- @override
- bool get verifyNoTestUnitErrors => false;
-
- Future<void> expectNotValidSnippet(
- String code,
- ) async {
- await resolveTestCode(withoutMarkers(code));
- final request = DartSnippetRequest(
- unit: testAnalysisResult,
- offset: offsetFromMarker(code),
- );
-
- final producer = generator(request);
- expect(await producer.isValid(), isFalse);
- }
-
- Future<Snippet> expectValidSnippet(String code) async {
- await resolveTestCode(withoutMarkers(code));
- final request = DartSnippetRequest(
- unit: testAnalysisResult,
- offset: offsetFromMarker(code),
- );
-
- final producer = generator(request);
- expect(await producer.isValid(), isTrue);
- return producer.compute();
- }
-}
-
-@reflectiveTest
-class DartSwitchSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartSwitchSnippetProducer.newInstance;
-
- @override
- String get label => DartSwitchSnippetProducer.label;
-
- @override
- String get prefix => DartSwitchSnippetProducer.prefix;
-
- Future<void> test_switch() async {
- var code = r'''
-void f() {
- sw^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- switch (expression) {
- case value:
-
- break;
- default:
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 57);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- // expression
- {
- 'positions': [
- {'file': testFile, 'offset': 21},
- ],
- 'length': 10,
- 'suggestions': []
- },
- // value
- {
- 'positions': [
- {'file': testFile, 'offset': 44},
- ],
- 'length': 5,
- 'suggestions': []
- },
- ]);
- }
-
- Future<void> test_switch_indentedInsideBlock() async {
- var code = r'''
-void f() {
- if (true) {
- sw^
- }
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- if (true) {
- switch (expression) {
- case value:
-
- break;
- default:
- }
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 77);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- // expression
- {
- 'positions': [
- {'file': testFile, 'offset': 37},
- ],
- 'length': 10,
- 'suggestions': []
- },
- // value
- {
- 'positions': [
- {'file': testFile, 'offset': 62},
- ],
- 'length': 5,
- 'suggestions': []
- },
- ]);
- }
-}
-
-@reflectiveTest
-class DartTestBlockSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartTestBlockSnippetProducer.newInstance;
-
- @override
- String get label => DartTestBlockSnippetProducer.label;
-
- @override
- String get prefix => DartTestBlockSnippetProducer.prefix;
-
- Future<void> test_inTestFile() async {
- testFile = convertPath('$testPackageLibPath/test/foo_test.dart');
- var code = r'''
-void f() {
- test^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- test('test name', () {
-
- });
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 40);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 19},
- ],
- 'length': 9,
- 'suggestions': []
- }
- ]);
- }
-
- Future<void> test_notTestFile() async {
- var code = r'''
-void f() {
- test^
-}''';
- await expectNotValidSnippet(code);
- }
-}
-
-@reflectiveTest
-class DartTestGroupBlockSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartTestGroupBlockSnippetProducer.newInstance;
-
- @override
- String get label => DartTestGroupBlockSnippetProducer.label;
-
- @override
- String get prefix => DartTestGroupBlockSnippetProducer.prefix;
-
- Future<void> test_inTestFile() async {
- testFile = convertPath('$testPackageLibPath/test/foo_test.dart');
- var code = r'''
-void f() {
- group^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- group('group name', () {
-
- });
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 42);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 20},
- ],
- 'length': 10,
- 'suggestions': []
- }
- ]);
- }
-
- Future<void> test_notTestFile() async {
- var code = r'''
-void f() {
- group^
-}''';
- await expectNotValidSnippet(code);
- }
-}
-
-@reflectiveTest
-class DartTryCatchSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartTryCatchSnippetProducer.newInstance;
-
- @override
- String get label => DartTryCatchSnippetProducer.label;
-
- @override
- String get prefix => DartTryCatchSnippetProducer.prefix;
-
- Future<void> test_tryCatch() async {
- var code = r'''
-void f() {
- tr^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- try {
-
- } catch (e) {
-
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 23);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 35},
- ],
- 'length': 1,
- 'suggestions': []
- }
- ]);
- }
-
- Future<void> test_tryCatch_indentedInsideBlock() async {
- var code = r'''
-void f() {
- if (true) {
- tr^
- }
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- if (true) {
- try {
-
- } catch (e) {
-
- }
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 41);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 55},
- ],
- 'length': 1,
- 'suggestions': []
- }
- ]);
- }
-}
-
-@reflectiveTest
-class DartWhileLoopSnippetProducerTest extends DartSnippetProducerTest {
- @override
- final generator = DartWhileLoopSnippetProducer.newInstance;
-
- @override
- String get label => DartWhileLoopSnippetProducer.label;
-
- @override
- String get prefix => DartWhileLoopSnippetProducer.prefix;
-
- Future<void> test_while() async {
- var code = r'''
-void f() {
- while^
-}''';
- final snippet = await expectValidSnippet(code);
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- expect(snippet.change.edits, hasLength(1));
- code = withoutMarkers(code);
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-void f() {
- while (condition) {
-
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 37);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 20},
- ],
- 'length': 9,
- 'suggestions': []
- }
- ]);
- }
-}
diff --git a/pkg/analysis_server/test/services/snippets/dart/do_statement_test.dart b/pkg/analysis_server/test/services/snippets/dart/do_statement_test.dart
new file mode 100644
index 0000000..a2b388c
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/do_statement_test.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/do_statement.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(DoStatementTest);
+ });
+}
+
+@reflectiveTest
+class DoStatementTest extends DartSnippetProducerTest {
+ @override
+ final generator = DoStatement.new;
+
+ @override
+ String get label => DoStatement.label;
+
+ @override
+ String get prefix => DoStatement.prefix;
+
+ Future<void> test_do() async {
+ var code = r'''
+void f() {
+ do^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ do {
+
+ } while (condition);
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 22);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 34},
+ ],
+ 'length': 9,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/flutter_snippet_producers_test.dart b/pkg/analysis_server/test/services/snippets/dart/flutter_snippet_producers_test.dart
deleted file mode 100644
index fcce56b..0000000
--- a/pkg/analysis_server/test/services/snippets/dart/flutter_snippet_producers_test.dart
+++ /dev/null
@@ -1,381 +0,0 @@
-// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:analysis_server/src/protocol_server.dart';
-import 'package:analysis_server/src/services/snippets/dart/flutter_snippet_producers.dart';
-import 'package:test/test.dart';
-import 'package:test_reflective_loader/test_reflective_loader.dart';
-
-import 'dart_snippet_producers_test.dart';
-
-void main() {
- defineReflectiveSuite(() {
- defineReflectiveTests(FlutterStatefulWidgetSnippetProducerTest);
- defineReflectiveTests(
- FlutterStatefulWidgetWithAnimationControllerSnippetProducerTest);
- defineReflectiveTests(FlutterStatelessWidgetSnippetProducerTest);
- });
-}
-
-abstract class FlutterSnippetProducerTest extends DartSnippetProducerTest {
- /// Checks snippets can produce edits where the imports and snippet will be
- /// inserted at the same location.
- ///
- /// For example, when a document is completely empty besides the snippet
- /// prefix, the imports will be inserted at offset 0 and the snippet will
- /// replace from 0 to the end of the typed prefix.
- Future<void> test_valid_importsAndEditsOverlap() async {
- writeTestPackageConfig(flutter: true);
-
- final snippet = await expectValidSnippet('$prefix^');
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
-
- // Main edits replace $prefix.length characters starting at $prefix
- final mainEdit = snippet.change.edits[0].edits[0];
- expect(mainEdit.offset, testCode.indexOf(prefix));
- expect(mainEdit.length, prefix.length);
-
- // Imports inserted at start of doc (0)
- final importEdit = snippet.change.edits[0].edits[1];
- expect(importEdit.offset, 0);
- expect(importEdit.length, 0);
- }
-
- Future<void> test_valid_suffixReplacement() async {
- writeTestPackageConfig(flutter: true);
-
- final snippet = await expectValidSnippet('''
-class A {}
-
-$prefix^
-''');
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
-
- // Main edits replace $prefix.length characters starting at $prefix
- final mainEdit = snippet.change.edits[0].edits[0];
- expect(mainEdit.offset, testCode.indexOf(prefix));
- expect(mainEdit.length, prefix.length);
-
- // Imports inserted at start of doc (0)
- final importEdit = snippet.change.edits[0].edits[1];
- expect(importEdit.offset, 0);
- expect(importEdit.length, 0);
- }
-}
-
-@reflectiveTest
-class FlutterStatefulWidgetSnippetProducerTest
- extends FlutterSnippetProducerTest {
- @override
- final generator = FlutterStatefulWidgetSnippetProducer.newInstance;
-
- @override
- String get label => FlutterStatefulWidgetSnippetProducer.label;
-
- @override
- String get prefix => FlutterStatefulWidgetSnippetProducer.prefix;
-
- Future<void> test_noSuperParams() async {
- writeTestPackageConfig(flutter: true, languageVersion: '2.16');
-
- final snippet = await expectValidSnippet('^');
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- var code = '';
- expect(snippet.change.edits, hasLength(1));
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-import 'package:flutter/src/foundation/key.dart';
-import 'package:flutter/src/widgets/container.dart';
-import 'package:flutter/src/widgets/framework.dart';
-
-class MyWidget extends StatefulWidget {
- const MyWidget({Key? key}) : super(key: key);
-
- @override
- State<MyWidget> createState() => _MyWidgetState();
-}
-
-class _MyWidgetState extends State<MyWidget> {
- @override
- Widget build(BuildContext context) {
- return Container();
- }
-}''');
- }
-
- Future<void> test_notValid_notFlutterProject() async {
- writeTestPackageConfig();
-
- await expectNotValidSnippet('^');
- }
-
- Future<void> test_valid() async {
- writeTestPackageConfig(flutter: true);
-
- final snippet = await expectValidSnippet('^');
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- var code = '';
- expect(snippet.change.edits, hasLength(1));
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-import 'package:flutter/src/widgets/container.dart';
-import 'package:flutter/src/widgets/framework.dart';
-
-class MyWidget extends StatefulWidget {
- const MyWidget({super.key});
-
- @override
- State<MyWidget> createState() => _MyWidgetState();
-}
-
-class _MyWidgetState extends State<MyWidget> {
- @override
- Widget build(BuildContext context) {
- return Container();
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 356);
- expect(snippet.change.selectionLength, 11);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 113},
- {'file': testFile, 'offset': 155},
- {'file': testFile, 'offset': 199},
- {'file': testFile, 'offset': 227},
- {'file': testFile, 'offset': 254},
- {'file': testFile, 'offset': 282},
- ],
- 'length': 8,
- 'suggestions': []
- }
- ]);
- }
-}
-
-@reflectiveTest
-class FlutterStatefulWidgetWithAnimationControllerSnippetProducerTest
- extends FlutterSnippetProducerTest {
- @override
- final generator =
- FlutterStatefulWidgetWithAnimationControllerSnippetProducer.newInstance;
-
- @override
- String get label =>
- FlutterStatefulWidgetWithAnimationControllerSnippetProducer.label;
-
- @override
- String get prefix =>
- FlutterStatefulWidgetWithAnimationControllerSnippetProducer.prefix;
-
- Future<void> test_noSuperParams() async {
- writeTestPackageConfig(flutter: true, languageVersion: '2.16');
-
- final snippet = await expectValidSnippet('^');
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- var code = '';
- expect(snippet.change.edits, hasLength(1));
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-import 'package:flutter/src/animation/animation_controller.dart';
-import 'package:flutter/src/foundation/key.dart';
-import 'package:flutter/src/widgets/container.dart';
-import 'package:flutter/src/widgets/framework.dart';
-import 'package:flutter/src/widgets/ticker_provider.dart';
-
-class MyWidget extends StatefulWidget {
- const MyWidget({Key? key}) : super(key: key);
-
- @override
- State<MyWidget> createState() => _MyWidgetState();
-}
-
-class _MyWidgetState extends State<MyWidget>
- with SingleTickerProviderStateMixin {
- late AnimationController _controller;
-
- @override
- void initState() {
- super.initState();
- _controller = AnimationController(vsync: this);
- }
-
- @override
- void dispose() {
- _controller.dispose();
- super.dispose();
- }
-
- @override
- Widget build(BuildContext context) {
- return Container();
- }
-}''');
- }
-
- Future<void> test_notValid_notFlutterProject() async {
- writeTestPackageConfig();
-
- await expectNotValidSnippet('^');
- }
-
- Future<void> test_valid() async {
- writeTestPackageConfig(flutter: true);
-
- final snippet = await expectValidSnippet('^');
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- var code = '';
- expect(snippet.change.edits, hasLength(1));
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-import 'package:flutter/src/animation/animation_controller.dart';
-import 'package:flutter/src/widgets/container.dart';
-import 'package:flutter/src/widgets/framework.dart';
-import 'package:flutter/src/widgets/ticker_provider.dart';
-
-class MyWidget extends StatefulWidget {
- const MyWidget({super.key});
-
- @override
- State<MyWidget> createState() => _MyWidgetState();
-}
-
-class _MyWidgetState extends State<MyWidget>
- with SingleTickerProviderStateMixin {
- late AnimationController _controller;
-
- @override
- void initState() {
- super.initState();
- _controller = AnimationController(vsync: this);
- }
-
- @override
- void dispose() {
- _controller.dispose();
- super.dispose();
- }
-
- @override
- Widget build(BuildContext context) {
- return Container();
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 759);
- expect(snippet.change.selectionLength, 11);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 238},
- {'file': testFile, 'offset': 280},
- {'file': testFile, 'offset': 324},
- {'file': testFile, 'offset': 352},
- {'file': testFile, 'offset': 379},
- {'file': testFile, 'offset': 407},
- ],
- 'length': 8,
- 'suggestions': []
- }
- ]);
- }
-}
-
-@reflectiveTest
-class FlutterStatelessWidgetSnippetProducerTest
- extends FlutterSnippetProducerTest {
- @override
- final generator = FlutterStatelessWidgetSnippetProducer.newInstance;
-
- @override
- String get label => FlutterStatelessWidgetSnippetProducer.label;
-
- @override
- String get prefix => FlutterStatelessWidgetSnippetProducer.prefix;
-
- Future<void> test_noSuperParams() async {
- writeTestPackageConfig(flutter: true, languageVersion: '2.16');
-
- final snippet = await expectValidSnippet('^');
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- var code = '';
- expect(snippet.change.edits, hasLength(1));
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-import 'package:flutter/src/foundation/key.dart';
-import 'package:flutter/src/widgets/container.dart';
-import 'package:flutter/src/widgets/framework.dart';
-
-class MyWidget extends StatelessWidget {
- const MyWidget({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return Container();
- }
-}''');
- }
-
- Future<void> test_notValid_notFlutterProject() async {
- writeTestPackageConfig();
-
- await expectNotValidSnippet('^');
- }
-
- Future<void> test_valid() async {
- writeTestPackageConfig(flutter: true);
-
- final snippet = await expectValidSnippet('^');
- expect(snippet.prefix, prefix);
- expect(snippet.label, label);
- var code = '';
- expect(snippet.change.edits, hasLength(1));
- for (var edit in snippet.change.edits) {
- code = SourceEdit.applySequence(code, edit.edits);
- }
- expect(code, '''
-import 'package:flutter/src/widgets/container.dart';
-import 'package:flutter/src/widgets/framework.dart';
-
-class MyWidget extends StatelessWidget {
- const MyWidget({super.key});
-
- @override
- Widget build(BuildContext context) {
- return Container();
- }
-}''');
- expect(snippet.change.selection!.file, testFile);
- expect(snippet.change.selection!.offset, 242);
- expect(snippet.change.selectionLength, 11);
- expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
- {
- 'positions': [
- {'file': testFile, 'offset': 113},
- {'file': testFile, 'offset': 156},
- ],
- 'length': 8,
- 'suggestions': []
- }
- ]);
- }
-}
diff --git a/pkg/analysis_server/test/services/snippets/dart/flutter_stateful_widget_test.dart b/pkg/analysis_server/test/services/snippets/dart/flutter_stateful_widget_test.dart
new file mode 100644
index 0000000..2b97ffe
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/flutter_stateful_widget_test.dart
@@ -0,0 +1,112 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/flutter_stateful_widget.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(FlutterStatefulWidgetTest);
+ });
+}
+
+@reflectiveTest
+class FlutterStatefulWidgetTest extends FlutterSnippetProducerTest {
+ @override
+ final generator = FlutterStatefulWidget.new;
+
+ @override
+ String get label => FlutterStatefulWidget.label;
+
+ @override
+ String get prefix => FlutterStatefulWidget.prefix;
+
+ Future<void> test_noSuperParams() async {
+ writeTestPackageConfig(flutter: true, languageVersion: '2.16');
+
+ final snippet = await expectValidSnippet('^');
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ var code = '';
+ expect(snippet.change.edits, hasLength(1));
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+import 'package:flutter/src/foundation/key.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+
+class MyWidget extends StatefulWidget {
+ const MyWidget({Key? key}) : super(key: key);
+
+ @override
+ State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget> {
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}''');
+ }
+
+ Future<void> test_notValid_notFlutterProject() async {
+ writeTestPackageConfig();
+
+ await expectNotValidSnippet('^');
+ }
+
+ Future<void> test_valid() async {
+ writeTestPackageConfig(flutter: true);
+
+ final snippet = await expectValidSnippet('^');
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ var code = '';
+ expect(snippet.change.edits, hasLength(1));
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+
+class MyWidget extends StatefulWidget {
+ const MyWidget({super.key});
+
+ @override
+ State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget> {
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 356);
+ expect(snippet.change.selectionLength, 11);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 113},
+ {'file': testFile, 'offset': 155},
+ {'file': testFile, 'offset': 199},
+ {'file': testFile, 'offset': 227},
+ {'file': testFile, 'offset': 254},
+ {'file': testFile, 'offset': 282},
+ ],
+ 'length': 8,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/flutter_stateful_widget_with_animation_controller_test.dart b/pkg/analysis_server/test/services/snippets/dart/flutter_stateful_widget_with_animation_controller_test.dart
new file mode 100644
index 0000000..5975c84
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/flutter_stateful_widget_with_animation_controller_test.dart
@@ -0,0 +1,147 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/flutter_stateful_widget_with_animation.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(FlutterStatefulWidgetWithAnimationControllerTest);
+ });
+}
+
+@reflectiveTest
+class FlutterStatefulWidgetWithAnimationControllerTest
+ extends FlutterSnippetProducerTest {
+ @override
+ final generator = FlutterStatefulWidgetWithAnimationController.new;
+
+ @override
+ String get label => FlutterStatefulWidgetWithAnimationController.label;
+
+ @override
+ String get prefix => FlutterStatefulWidgetWithAnimationController.prefix;
+
+ Future<void> test_noSuperParams() async {
+ writeTestPackageConfig(flutter: true, languageVersion: '2.16');
+
+ final snippet = await expectValidSnippet('^');
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ var code = '';
+ expect(snippet.change.edits, hasLength(1));
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+import 'package:flutter/src/animation/animation_controller.dart';
+import 'package:flutter/src/foundation/key.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter/src/widgets/ticker_provider.dart';
+
+class MyWidget extends StatefulWidget {
+ const MyWidget({Key? key}) : super(key: key);
+
+ @override
+ State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget>
+ with SingleTickerProviderStateMixin {
+ late AnimationController _controller;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = AnimationController(vsync: this);
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}''');
+ }
+
+ Future<void> test_notValid_notFlutterProject() async {
+ writeTestPackageConfig();
+
+ await expectNotValidSnippet('^');
+ }
+
+ Future<void> test_valid() async {
+ writeTestPackageConfig(flutter: true);
+
+ final snippet = await expectValidSnippet('^');
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ var code = '';
+ expect(snippet.change.edits, hasLength(1));
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+import 'package:flutter/src/animation/animation_controller.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter/src/widgets/ticker_provider.dart';
+
+class MyWidget extends StatefulWidget {
+ const MyWidget({super.key});
+
+ @override
+ State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget>
+ with SingleTickerProviderStateMixin {
+ late AnimationController _controller;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = AnimationController(vsync: this);
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 759);
+ expect(snippet.change.selectionLength, 11);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 238},
+ {'file': testFile, 'offset': 280},
+ {'file': testFile, 'offset': 324},
+ {'file': testFile, 'offset': 352},
+ {'file': testFile, 'offset': 379},
+ {'file': testFile, 'offset': 407},
+ ],
+ 'length': 8,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/flutter_stateless_widget_test.dart b/pkg/analysis_server/test/services/snippets/dart/flutter_stateless_widget_test.dart
new file mode 100644
index 0000000..d01c58d
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/flutter_stateless_widget_test.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/flutter_stateless_widget.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(FlutterStatelessWidgetTest);
+ });
+}
+
+@reflectiveTest
+class FlutterStatelessWidgetTest extends FlutterSnippetProducerTest {
+ @override
+ final generator = FlutterStatelessWidget.new;
+
+ @override
+ String get label => FlutterStatelessWidget.label;
+
+ @override
+ String get prefix => FlutterStatelessWidget.prefix;
+
+ Future<void> test_noSuperParams() async {
+ writeTestPackageConfig(flutter: true, languageVersion: '2.16');
+
+ final snippet = await expectValidSnippet('^');
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ var code = '';
+ expect(snippet.change.edits, hasLength(1));
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+import 'package:flutter/src/foundation/key.dart';
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+
+class MyWidget extends StatelessWidget {
+ const MyWidget({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}''');
+ }
+
+ Future<void> test_notValid_notFlutterProject() async {
+ writeTestPackageConfig();
+
+ await expectNotValidSnippet('^');
+ }
+
+ Future<void> test_valid() async {
+ writeTestPackageConfig(flutter: true);
+
+ final snippet = await expectValidSnippet('^');
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ var code = '';
+ expect(snippet.change.edits, hasLength(1));
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+import 'package:flutter/src/widgets/container.dart';
+import 'package:flutter/src/widgets/framework.dart';
+
+class MyWidget extends StatelessWidget {
+ const MyWidget({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 242);
+ expect(snippet.change.selectionLength, 11);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 113},
+ {'file': testFile, 'offset': 156},
+ ],
+ 'length': 8,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/for_in_statement_test.dart b/pkg/analysis_server/test/services/snippets/dart/for_in_statement_test.dart
new file mode 100644
index 0000000..9f1d457
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/for_in_statement_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/for_in_statement.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ForInStatementTest);
+ });
+}
+
+@reflectiveTest
+class ForInStatementTest extends DartSnippetProducerTest {
+ @override
+ final generator = ForInStatement.new;
+
+ @override
+ String get label => ForInStatement.label;
+
+ @override
+ String get prefix => ForInStatement.prefix;
+
+ Future<void> test_for() async {
+ var code = r'''
+void f() {
+ forin^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ for (var element in collection) {
+
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 51);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 22},
+ ],
+ 'length': 7,
+ 'suggestions': []
+ },
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 33},
+ ],
+ 'length': 10,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/for_statement_test.dart b/pkg/analysis_server/test/services/snippets/dart/for_statement_test.dart
new file mode 100644
index 0000000..6b11ccc
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/for_statement_test.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/for_statement.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ForStatementTest);
+ });
+}
+
+@reflectiveTest
+class ForStatementTest extends DartSnippetProducerTest {
+ @override
+ final generator = ForStatement.new;
+
+ @override
+ String get label => ForStatement.label;
+
+ @override
+ String get prefix => ForStatement.prefix;
+
+ Future<void> test_for() async {
+ var code = r'''
+void f() {
+ for^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ for (var i = 0; i < count; i++) {
+
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 51);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 33},
+ ],
+ 'length': 5,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/function_declaration_test.dart b/pkg/analysis_server/test/services/snippets/dart/function_declaration_test.dart
new file mode 100644
index 0000000..7d5aefe
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/function_declaration_test.dart
@@ -0,0 +1,170 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/function_declaration.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(FunctionDeclarationTest);
+ });
+}
+
+@reflectiveTest
+class FunctionDeclarationTest extends DartSnippetProducerTest {
+ @override
+ final generator = FunctionDeclaration.new;
+
+ @override
+ String get label => FunctionDeclaration.label;
+
+ @override
+ String get prefix => FunctionDeclaration.prefix;
+
+ Future<void> test_classMethod() async {
+ var code = r'''
+class A {
+ ^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+class A {
+ void name(params) {
+
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 36);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 12},
+ ],
+ 'length': 4,
+ 'suggestions': []
+ },
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 17},
+ ],
+ 'length': 4,
+ 'suggestions': []
+ },
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 22},
+ ],
+ 'length': 6,
+ 'suggestions': []
+ },
+ ]);
+ }
+
+ Future<void> test_nested() async {
+ var code = r'''
+void a() {
+ ^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void a() {
+ void name(params) {
+
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 37);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 13},
+ ],
+ 'length': 4,
+ 'suggestions': []
+ },
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 18},
+ ],
+ 'length': 4,
+ 'suggestions': []
+ },
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 23},
+ ],
+ 'length': 6,
+ 'suggestions': []
+ },
+ ]);
+ }
+
+ Future<void> test_topLevel() async {
+ var code = r'''
+class A {}
+
+^
+
+class B {}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+class A {}
+
+void name(params) {
+
+}
+
+class B {}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 36);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 14},
+ ],
+ 'length': 4,
+ 'suggestions': []
+ },
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 19},
+ ],
+ 'length': 4,
+ 'suggestions': []
+ },
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 24},
+ ],
+ 'length': 6,
+ 'suggestions': []
+ },
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/if_else_statement_test.dart b/pkg/analysis_server/test/services/snippets/dart/if_else_statement_test.dart
new file mode 100644
index 0000000..e92e873
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/if_else_statement_test.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/if_else_statement.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(IfElseStatementTest);
+ });
+}
+
+@reflectiveTest
+class IfElseStatementTest extends DartSnippetProducerTest {
+ @override
+ final generator = IfElseStatement.new;
+
+ @override
+ String get label => IfElseStatement.label;
+
+ @override
+ String get prefix => IfElseStatement.prefix;
+
+ Future<void> test_ifElse() async {
+ var code = r'''
+void f() {
+ if^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ if (condition) {
+
+ } else {
+
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 34);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 17},
+ ],
+ 'length': 9,
+ 'suggestions': []
+ }
+ ]);
+ }
+
+ Future<void> test_ifElse_indentedInsideBlock() async {
+ var code = r'''
+void f() {
+ if (true) {
+ if^
+ }
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ if (true) {
+ if (condition) {
+
+ } else {
+
+ }
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 52);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 33},
+ ],
+ 'length': 9,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/if_statement_test.dart b/pkg/analysis_server/test/services/snippets/dart/if_statement_test.dart
new file mode 100644
index 0000000..bc83aa2
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/if_statement_test.dart
@@ -0,0 +1,96 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/if_statement.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(IfStatementTest);
+ });
+}
+
+@reflectiveTest
+class IfStatementTest extends DartSnippetProducerTest {
+ @override
+ final generator = IfStatement.new;
+
+ @override
+ String get label => IfStatement.label;
+
+ @override
+ String get prefix => IfStatement.prefix;
+
+ Future<void> test_if() async {
+ var code = r'''
+void f() {
+ if^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ if (condition) {
+
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 34);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 17},
+ ],
+ 'length': 9,
+ 'suggestions': []
+ }
+ ]);
+ }
+
+ Future<void> test_if_indentedInsideBlock() async {
+ var code = r'''
+void f() {
+ if (true) {
+ if^
+ }
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ if (true) {
+ if (condition) {
+
+ }
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 52);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 33},
+ ],
+ 'length': 9,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/main_function_test.dart b/pkg/analysis_server/test/services/snippets/dart/main_function_test.dart
new file mode 100644
index 0000000..82e1e12
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/main_function_test.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/main_function.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(MainFunctionTest);
+ });
+}
+
+@reflectiveTest
+class MainFunctionTest extends DartSnippetProducerTest {
+ @override
+ final generator = MainFunction.new;
+
+ @override
+ String get label => MainFunction.label;
+
+ @override
+ String get prefix => MainFunction.prefix;
+
+ Future<void> test_noParams_testFolder() => testInFile(
+ convertPath('$testPackageLibPath/test/foo_test.dart'),
+ expectArgsParameter: false,
+ );
+
+ Future<void> test_params_binFolder() => testInFile(
+ convertPath('$testPackageLibPath/bin/main.dart'),
+ expectArgsParameter: true,
+ );
+
+ Future<void> test_params_projectRoot() => testInFile(
+ convertPath('$testPackageRootPath/foo.dart'),
+ expectArgsParameter: true,
+ );
+
+ Future<void> test_params_toolFolder() => testInFile(
+ convertPath('$testPackageLibPath/tool/tool.dart'),
+ expectArgsParameter: true,
+ );
+
+ Future<void> test_typedPrefix() => testInFile(
+ testFile,
+ code: '$prefix^',
+ expectArgsParameter: true,
+ );
+
+ Future<void> testInFile(
+ String file, {
+ String code = '^',
+ required bool expectArgsParameter,
+ }) async {
+ testFile = file;
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ final expectedParams = expectArgsParameter ? 'List<String> args' : '';
+ expect(code, '''
+void main($expectedParams) {
+
+}''');
+ expect(snippet.change.selection!.file, file);
+ expect(snippet.change.selection!.offset, 16 + expectedParams.length);
+ expect(snippet.change.linkedEditGroups, isEmpty);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/switch_statement_test.dart b/pkg/analysis_server/test/services/snippets/dart/switch_statement_test.dart
new file mode 100644
index 0000000..c2585a0
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/switch_statement_test.dart
@@ -0,0 +1,120 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/switch_statement.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(SwitchStatementTest);
+ });
+}
+
+@reflectiveTest
+class SwitchStatementTest extends DartSnippetProducerTest {
+ @override
+ final generator = SwitchStatement.new;
+
+ @override
+ String get label => SwitchStatement.label;
+
+ @override
+ String get prefix => SwitchStatement.prefix;
+
+ Future<void> test_switch() async {
+ var code = r'''
+void f() {
+ sw^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ switch (expression) {
+ case value:
+
+ break;
+ default:
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 57);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ // expression
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 21},
+ ],
+ 'length': 10,
+ 'suggestions': []
+ },
+ // value
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 44},
+ ],
+ 'length': 5,
+ 'suggestions': []
+ },
+ ]);
+ }
+
+ Future<void> test_switch_indentedInsideBlock() async {
+ var code = r'''
+void f() {
+ if (true) {
+ sw^
+ }
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ if (true) {
+ switch (expression) {
+ case value:
+
+ break;
+ default:
+ }
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 77);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ // expression
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 37},
+ ],
+ 'length': 10,
+ 'suggestions': []
+ },
+ // value
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 62},
+ ],
+ 'length': 5,
+ 'suggestions': []
+ },
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/test_all.dart b/pkg/analysis_server/test/services/snippets/dart/test_all.dart
index 5686247..cc290a6 100644
--- a/pkg/analysis_server/test/services/snippets/dart/test_all.dart
+++ b/pkg/analysis_server/test/services/snippets/dart/test_all.dart
@@ -4,16 +4,41 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import 'dart_snippet_producers_test.dart' as dart_snippet_producers;
-import 'flutter_snippet_producers_test.dart' as flutter_snippet_producers;
-import 'snippet_manager_test.dart' as snippet_manager;
-import 'snippet_request_test.dart' as snippet_request;
+import 'class_declaration_test.dart' as class_declaration;
+import 'do_statement_test.dart' as do_statement;
+import 'flutter_stateful_widget_test.dart' as flutter_stateful_widget;
+import 'flutter_stateful_widget_with_animation_controller_test.dart'
+ as flutter_stateful_widget_with_animation_controller;
+import 'flutter_stateless_widget_test.dart' as flutter_stateless_widget;
+import 'for_in_statement_test.dart' as for_in_statement;
+import 'for_statement_test.dart' as for_statement;
+import 'function_declaration_test.dart' as function_declaration;
+import 'if_else_statement_test.dart' as if_else_statement;
+import 'if_statement_test.dart' as if_statement;
+import 'main_function_test.dart' as main_function;
+import 'switch_statement_test.dart' as switch_statement;
+import 'test_definition_test.dart' as test_definition;
+import 'test_group_definition_test.dart' as test_group_definition;
+import 'try_catch_statement_test.dart' as try_catch_statement;
+import 'while_statement_test.dart' as while_statement;
void main() {
defineReflectiveSuite(() {
- dart_snippet_producers.main();
- flutter_snippet_producers.main();
- snippet_manager.main();
- snippet_request.main();
+ class_declaration.main();
+ do_statement.main();
+ flutter_stateful_widget.main();
+ flutter_stateful_widget_with_animation_controller.main();
+ flutter_stateless_widget.main();
+ for_in_statement.main();
+ for_statement.main();
+ function_declaration.main();
+ if_else_statement.main();
+ if_statement.main();
+ main_function.main();
+ switch_statement.main();
+ test_definition.main();
+ test_group_definition.main();
+ try_catch_statement.main();
+ while_statement.main();
}, name: 'dart');
}
diff --git a/pkg/analysis_server/test/services/snippets/dart/test_definition_test.dart b/pkg/analysis_server/test/services/snippets/dart/test_definition_test.dart
new file mode 100644
index 0000000..1bb9a2d
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/test_definition_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/test_definition.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(TestDefinitionTest);
+ });
+}
+
+@reflectiveTest
+class TestDefinitionTest extends DartSnippetProducerTest {
+ @override
+ final generator = TestDefinition.new;
+
+ @override
+ String get label => TestDefinition.label;
+
+ @override
+ String get prefix => TestDefinition.prefix;
+
+ Future<void> test_inTestFile() async {
+ testFile = convertPath('$testPackageLibPath/test/foo_test.dart');
+ var code = r'''
+void f() {
+ test^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ test('test name', () {
+
+ });
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 40);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 19},
+ ],
+ 'length': 9,
+ 'suggestions': []
+ }
+ ]);
+ }
+
+ Future<void> test_notTestFile() async {
+ var code = r'''
+void f() {
+ test^
+}''';
+ await expectNotValidSnippet(code);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/test_group_definition_test.dart b/pkg/analysis_server/test/services/snippets/dart/test_group_definition_test.dart
new file mode 100644
index 0000000..1033e0d
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/test_group_definition_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/test_group_definition.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(TestGroupDefinitionTest);
+ });
+}
+
+@reflectiveTest
+class TestGroupDefinitionTest extends DartSnippetProducerTest {
+ @override
+ final generator = TestGroupDefinition.new;
+
+ @override
+ String get label => TestGroupDefinition.label;
+
+ @override
+ String get prefix => TestGroupDefinition.prefix;
+
+ Future<void> test_inTestFile() async {
+ testFile = convertPath('$testPackageLibPath/test/foo_test.dart');
+ var code = r'''
+void f() {
+ group^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ group('group name', () {
+
+ });
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 42);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 20},
+ ],
+ 'length': 10,
+ 'suggestions': []
+ }
+ ]);
+ }
+
+ Future<void> test_notTestFile() async {
+ var code = r'''
+void f() {
+ group^
+}''';
+ await expectNotValidSnippet(code);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/test_support.dart b/pkg/analysis_server/test/services/snippets/dart/test_support.dart
index 9f2d740..2d0a595 100644
--- a/pkg/analysis_server/test/services/snippets/dart/test_support.dart
+++ b/pkg/analysis_server/test/services/snippets/dart/test_support.dart
@@ -2,29 +2,99 @@
// 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:analyzer/source/source_range.dart';
+import 'package:analysis_server/src/services/snippets/dart_snippet_request.dart';
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_manager.dart';
import 'package:test/test.dart';
-int offsetFromMarker(String code) {
- final offset = withoutRangeMarkers(code).indexOf('^');
- expect(offset, isNot(-1));
- return offset;
+import '../../../abstract_single_unit.dart';
+import '../test_support.dart';
+
+export '../test_support.dart';
+
+abstract class DartSnippetProducerTest extends AbstractSingleUnitTest {
+ SnippetProducerGenerator get generator;
+ String get label;
+ String get prefix;
+
+ /// Override the package root because it usually contains /test/ and some
+ /// snippets behave differently for test files.
+ @override
+ String get testPackageRootPath => '$workspaceRootPath/my_package';
+
+ @override
+ bool get verifyNoTestUnitErrors => false;
+
+ Future<void> expectNotValidSnippet(
+ String code,
+ ) async {
+ await resolveTestCode(withoutMarkers(code));
+ final request = DartSnippetRequest(
+ unit: testAnalysisResult,
+ offset: offsetFromMarker(code),
+ );
+
+ final producer = generator(request);
+ expect(await producer.isValid(), isFalse);
+ }
+
+ Future<Snippet> expectValidSnippet(String code) async {
+ await resolveTestCode(withoutMarkers(code));
+ final request = DartSnippetRequest(
+ unit: testAnalysisResult,
+ offset: offsetFromMarker(code),
+ );
+
+ final producer = generator(request);
+ expect(await producer.isValid(), isTrue);
+ return producer.compute();
+ }
}
-SourceRange rangeFromMarkers(String code) {
- code = _withoutPositionMarker(code);
- final start = code.indexOf('[[');
- final end = code.indexOf(']]');
- expect(start, isNot(-1));
- expect(end, isNot(-1));
- final endAdjusted = end - 2; // Account for the [[ before this marker
- return SourceRange(start, endAdjusted - start);
+abstract class FlutterSnippetProducerTest extends DartSnippetProducerTest {
+ /// Checks snippets can produce edits where the imports and snippet will be
+ /// inserted at the same location.
+ ///
+ /// For example, when a document is completely empty besides the snippet
+ /// prefix, the imports will be inserted at offset 0 and the snippet will
+ /// replace from 0 to the end of the typed prefix.
+ Future<void> test_valid_importsAndEditsOverlap() async {
+ writeTestPackageConfig(flutter: true);
+
+ final snippet = await expectValidSnippet('$prefix^');
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+
+ // Main edits replace $prefix.length characters starting at $prefix
+ final mainEdit = snippet.change.edits[0].edits[0];
+ expect(mainEdit.offset, testCode.indexOf(prefix));
+ expect(mainEdit.length, prefix.length);
+
+ // Imports inserted at start of doc (0)
+ final importEdit = snippet.change.edits[0].edits[1];
+ expect(importEdit.offset, 0);
+ expect(importEdit.length, 0);
+ }
+
+ Future<void> test_valid_suffixReplacement() async {
+ writeTestPackageConfig(flutter: true);
+
+ final snippet = await expectValidSnippet('''
+class A {}
+
+$prefix^
+''');
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+
+ // Main edits replace $prefix.length characters starting at $prefix
+ final mainEdit = snippet.change.edits[0].edits[0];
+ expect(mainEdit.offset, testCode.indexOf(prefix));
+ expect(mainEdit.length, prefix.length);
+
+ // Imports inserted at start of doc (0)
+ final importEdit = snippet.change.edits[0].edits[1];
+ expect(importEdit.offset, 0);
+ expect(importEdit.length, 0);
+ }
}
-
-String withoutMarkers(String code) =>
- withoutRangeMarkers(_withoutPositionMarker(code));
-
-String withoutRangeMarkers(String code) =>
- code.replaceAll('[[', '').replaceAll(']]', '');
-
-String _withoutPositionMarker(String code) => code.replaceAll('^', '');
diff --git a/pkg/analysis_server/test/services/snippets/dart/try_catch_statement_test.dart b/pkg/analysis_server/test/services/snippets/dart/try_catch_statement_test.dart
new file mode 100644
index 0000000..3ecc95a
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/try_catch_statement_test.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/try_catch_statement.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(TryCatchStatementTest);
+ });
+}
+
+@reflectiveTest
+class TryCatchStatementTest extends DartSnippetProducerTest {
+ @override
+ final generator = TryCatchStatement.new;
+
+ @override
+ String get label => TryCatchStatement.label;
+
+ @override
+ String get prefix => TryCatchStatement.prefix;
+
+ Future<void> test_tryCatch() async {
+ var code = r'''
+void f() {
+ tr^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ try {
+
+ } catch (e) {
+
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 23);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 35},
+ ],
+ 'length': 1,
+ 'suggestions': []
+ }
+ ]);
+ }
+
+ Future<void> test_tryCatch_indentedInsideBlock() async {
+ var code = r'''
+void f() {
+ if (true) {
+ tr^
+ }
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ if (true) {
+ try {
+
+ } catch (e) {
+
+ }
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 41);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 55},
+ ],
+ 'length': 1,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/while_statement_test.dart b/pkg/analysis_server/test/services/snippets/dart/while_statement_test.dart
new file mode 100644
index 0000000..06e00c3
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/while_statement_test.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/snippets/dart/while_statement.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(WhileStatementTest);
+ });
+}
+
+@reflectiveTest
+class WhileStatementTest extends DartSnippetProducerTest {
+ @override
+ final generator = WhileStatement.new;
+
+ @override
+ String get label => WhileStatement.label;
+
+ @override
+ String get prefix => WhileStatement.prefix;
+
+ Future<void> test_while() async {
+ var code = r'''
+void f() {
+ while^
+}''';
+ final snippet = await expectValidSnippet(code);
+ expect(snippet.prefix, prefix);
+ expect(snippet.label, label);
+ expect(snippet.change.edits, hasLength(1));
+ code = withoutMarkers(code);
+ for (var edit in snippet.change.edits) {
+ code = SourceEdit.applySequence(code, edit.edits);
+ }
+ expect(code, '''
+void f() {
+ while (condition) {
+
+ }
+}''');
+ expect(snippet.change.selection!.file, testFile);
+ expect(snippet.change.selection!.offset, 37);
+ expect(snippet.change.linkedEditGroups.map((group) => group.toJson()), [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 20},
+ ],
+ 'length': 9,
+ 'suggestions': []
+ }
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/snippet_manager_test.dart b/pkg/analysis_server/test/services/snippets/snippet_manager_test.dart
similarity index 88%
rename from pkg/analysis_server/test/services/snippets/dart/snippet_manager_test.dart
rename to pkg/analysis_server/test/services/snippets/snippet_manager_test.dart
index eeff24a..ae98fe8 100644
--- a/pkg/analysis_server/test/services/snippets/dart/snippet_manager_test.dart
+++ b/pkg/analysis_server/test/services/snippets/snippet_manager_test.dart
@@ -2,12 +2,16 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:analysis_server/src/services/snippets/dart/snippet_manager.dart';
+import 'package:analysis_server/src/services/snippets/dart_snippet_request.dart';
+import 'package:analysis_server/src/services/snippets/snippet.dart';
+import 'package:analysis_server/src/services/snippets/snippet_context.dart';
+import 'package:analysis_server/src/services/snippets/snippet_manager.dart';
+import 'package:analysis_server/src/services/snippets/snippet_producer.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../../../abstract_single_unit.dart';
+import '../../abstract_single_unit.dart';
void main() {
defineReflectiveSuite(() {
diff --git a/pkg/analysis_server/test/services/snippets/dart/snippet_request_test.dart b/pkg/analysis_server/test/services/snippets/snippet_request_test.dart
similarity index 97%
rename from pkg/analysis_server/test/services/snippets/dart/snippet_request_test.dart
rename to pkg/analysis_server/test/services/snippets/snippet_request_test.dart
index fd6a240..87c5911 100644
--- a/pkg/analysis_server/test/services/snippets/dart/snippet_request_test.dart
+++ b/pkg/analysis_server/test/services/snippets/snippet_request_test.dart
@@ -2,12 +2,13 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:analysis_server/src/services/snippets/dart/snippet_manager.dart';
+import 'package:analysis_server/src/services/snippets/dart_snippet_request.dart';
+import 'package:analysis_server/src/services/snippets/snippet_context.dart';
import 'package:analyzer/src/test_utilities/platform.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../../../abstract_single_unit.dart';
+import '../../abstract_single_unit.dart';
import 'test_support.dart';
void main() {
diff --git a/pkg/analysis_server/test/services/snippets/test_all.dart b/pkg/analysis_server/test/services/snippets/test_all.dart
index 899f5ae..b66e99a 100644
--- a/pkg/analysis_server/test/services/snippets/test_all.dart
+++ b/pkg/analysis_server/test/services/snippets/test_all.dart
@@ -4,10 +4,14 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import 'dart/test_all.dart' as dart_all;
+import 'dart/test_all.dart' as dart;
+import 'snippet_manager_test.dart' as snippet_manager;
+import 'snippet_request_test.dart' as snippet_request;
void main() {
defineReflectiveSuite(() {
- dart_all.main();
+ dart.main();
+ snippet_manager.main();
+ snippet_request.main();
}, name: 'snippets');
}
diff --git a/pkg/analysis_server/test/services/snippets/test_support.dart b/pkg/analysis_server/test/services/snippets/test_support.dart
new file mode 100644
index 0000000..9f2d740
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/test_support.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2022, 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:analyzer/source/source_range.dart';
+import 'package:test/test.dart';
+
+int offsetFromMarker(String code) {
+ final offset = withoutRangeMarkers(code).indexOf('^');
+ expect(offset, isNot(-1));
+ return offset;
+}
+
+SourceRange rangeFromMarkers(String code) {
+ code = _withoutPositionMarker(code);
+ final start = code.indexOf('[[');
+ final end = code.indexOf(']]');
+ expect(start, isNot(-1));
+ expect(end, isNot(-1));
+ final endAdjusted = end - 2; // Account for the [[ before this marker
+ return SourceRange(start, endAdjusted - start);
+}
+
+String withoutMarkers(String code) =>
+ withoutRangeMarkers(_withoutPositionMarker(code));
+
+String withoutRangeMarkers(String code) =>
+ code.replaceAll('[[', '').replaceAll(']]', '');
+
+String _withoutPositionMarker(String code) => code.replaceAll('^', '');
diff --git a/pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart b/pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart
index 486abe6..c247892 100644
--- a/pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart
@@ -1459,6 +1459,21 @@
);
}
+ Future<void> test_prefixedTypes() async {
+ // Prefixed type names that are not tear-offs should never be included.
+ final contents = '''
+// ignore_for_file: unused_local_variable
+import 'dart:io' as io;
+
+void ^f(io.File f) {
+ io.Directory? d;
+}
+ ''';
+
+ final calls = await findOutgoingCalls(contents);
+ expect(calls, isEmpty);
+ }
+
Future<void> test_setter() async {
final contents = '''
import 'other.dart';
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 2555f6d..c4e8160 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -75,326 +75,146 @@
DirectiveUri get uri;
}
+/// The result of applying augmentations to a [ClassElement].
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class AugmentedClassElement implements AugmentedClassOrEnumElement {
+ /// Returns mixins applied by this class or in its augmentations.
+ ///
+ /// This is a union of mixins applied by the class declaration and all its
+ /// augmentations.
+ List<InterfaceType> get mixins;
+}
+
+/// The result of applying augmentations to a [ClassElement] or [EnumElement].
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class AugmentedClassOrEnumElement
+ implements AugmentedInterfaceDefiningElement {
+ /// Returns constructors declared in this element.
+ ///
+ /// [ConstructorAugmentationElement]s replace corresponding elements,
+ /// other [ConstructorElement]s are appended.
+ List<ConstructorElement> get constructors;
+
+ /// Returns the unnamed constructor from [constructors].
+ ConstructorElement? get unnamedConstructor;
+
+ /// Returns the constructor from [constructors] that has the given [name].
+ ConstructorElement? getNamedConstructor(String name);
+}
+
+/// The result of applying augmentations to a [EnumElement].
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class AugmentedEnumElement implements AugmentedClassOrEnumElement {
+ /// Returns mixins applied by this class or in its augmentations.
+ ///
+ /// This is a union of mixins applied by the class declaration and all its
+ /// augmentations.
+ List<InterfaceType> get mixins;
+}
+
+/// The result of applying augmentations to a [InterfaceDefiningElement].
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class AugmentedInterfaceDefiningElement {
+ /// Returns accessors (getters and setters) declared in this element.
+ ///
+ /// [PropertyAccessorAugmentationElement]s replace corresponding elements,
+ /// other [PropertyAccessorElement]s are appended.
+ List<PropertyAccessorElement> get accessors;
+
+ /// Returns fields declared in this element.
+ ///
+ /// [FieldAugmentationElement]s replace corresponding elements, other
+ /// [FieldElement]s are appended.
+ List<FieldElement> get fields;
+
+ /// Returns interfaces implemented by this element.
+ ///
+ /// This is a union of interfaces declared by the class declaration and
+ /// all its augmentations.
+ List<InterfaceType> get interfaces;
+
+ /// Returns metadata associated with this element.
+ ///
+ /// This is a union of annotations associated with the class declaration and
+ /// all its augmentations.
+ List<ElementAnnotation> get metadata;
+
+ /// Returns methods declared in this element.
+ ///
+ /// [MethodAugmentationElement]s replace corresponding elements, other
+ /// [MethodElement]s are appended.
+ List<MethodElement> get methods;
+
+ /// Returns the field from [fields] that has the given [name].
+ FieldElement? getField(String name);
+
+ /// Returns the getter from [accessors] that has the given [name].
+ PropertyAccessorElement? getGetter(String name);
+
+ /// Returns the method from [methods] that has the given [name].
+ MethodElement? getMethod(String name);
+
+ /// Returns the setter from [accessors] that has the given [name].
+ PropertyAccessorElement? getSetter(String name);
+}
+
+/// The result of applying augmentations to a [MixinElement].
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class AugmentedMixinElement extends AugmentedInterfaceDefiningElement {
+ /// Returns superclass constraints of this element.
+ ///
+ /// This is a union of constraints declared by the class declaration and
+ /// all its augmentations.
+ List<InterfaceType> get superclassConstraints;
+}
+
+/// A class augmentation, defined by a class augmentation declaration.
+///
+/// Clients may not extend, implement or mix-in this class.
+@experimental
+abstract class ClassAugmentationElement implements ClassOrAugmentationElement {
+ /// Returns the element that is augmented by this augmentation; or `null` if
+ /// there is no corresponding element to be augmented. The chain of
+ /// augmentations should normally end with a [ClassElement], but might end
+ /// with `null` immediately or after a few intermediate
+ /// [ClassAugmentationElement]s in case of invalid code when an augmentation
+ /// is declared without the corresponding class declaration.
+ ClassOrAugmentationElement? get augmentationTarget;
+}
+
/// An element that represents a class or a mixin. The class can be defined by
/// either a class declaration (with a class body), a mixin application (without
/// a class body), a mixin declaration, or an enum declaration.
///
/// Clients may not extend, implement or mix-in this class.
abstract class ClassElement
- implements TypeDefiningElement, TypeParameterizedElement {
- /// Return a list containing all of the accessors (getters and setters)
- /// declared in this class.
- List<PropertyAccessorElement> get accessors;
-
- /// Return a list containing all the supertypes defined for this class and its
- /// supertypes. This includes superclasses, mixins, interfaces and superclass
- /// constraints.
- List<InterfaceType> get allSupertypes;
-
- /// Return a list containing all of the constructors declared in this class.
- /// The list will be empty if there are no constructors defined for this
- /// class, as is the case when this element represents an enum or a mixin.
- List<ConstructorElement> get constructors;
-
- @override
- String get displayName;
-
- @Deprecated('Use enclosingElement2 instead')
- @override
- CompilationUnitElement get enclosingElement;
-
- @override
- CompilationUnitElement get enclosingElement2;
-
- /// Return a list containing all of the fields declared in this class.
- List<FieldElement> get fields;
-
- /// Return `true` if this class or its superclass declares a non-final
- /// instance field.
- bool get hasNonFinalField;
-
- /// Return `true` if this class declares a static member.
- bool get hasStaticMember;
-
- /// Return a list containing all of the interfaces that are implemented by
- /// this class.
- ///
- /// <b>Note:</b> Because the element model represents the state of the code,
- /// it is possible for it to be semantically invalid. In particular, it is not
- /// safe to assume that the inheritance structure of a class does not contain
- /// a cycle. Clients that traverse the inheritance structure must explicitly
- /// guard against infinite loops.
- List<InterfaceType> get interfaces;
-
- /// Return `true` if this class is abstract. A class is abstract if it has an
- /// explicit `abstract` modifier or if it is implicitly abstract, such as a
- /// class defined by a mixin declaration. Note, that this definition of
- /// <i>abstract</i> is different from <i>has unimplemented members</i>.
- bool get isAbstract;
-
- /// Return `true` if this class represents the class 'Enum' defined in the
- /// dart:core library.
- bool get isDartCoreEnum;
-
- /// Return `true` if this class represents the class 'Object' defined in the
- /// dart:core library.
- bool get isDartCoreObject;
-
- /// Return `true` if this class is defined by an enum declaration.
- bool get isEnum;
-
- /// Return `true` if this class is defined by a mixin declaration.
- bool get isMixin;
-
- /// Return `true` if this class is a mixin application. A class is a mixin
- /// application if it was declared using the syntax "class A = B with C;".
- bool get isMixinApplication;
-
- /// Return `true` if this class can validly be used as a mixin when defining
- /// another class. For classes defined by a mixin declaration, the result is
- /// always `true`. For classes defined by a class declaration or a mixin
- /// application, the behavior of this method is defined by the Dart Language
- /// Specification in section 9:
- /// <blockquote>
- /// It is a compile-time error if a declared or derived mixin refers to super.
- /// It is a compile-time error if a declared or derived mixin explicitly
- /// declares a constructor. It is a compile-time error if a mixin is derived
- /// from a class whose superclass is not Object.
- /// </blockquote>
- bool get isValidMixin;
-
- /// Return a list containing all of the methods declared in this class.
- List<MethodElement> get methods;
-
- /// Return a list containing all of the mixins that are applied to the class
- /// being extended in order to derive the superclass of this class.
- ///
- /// <b>Note:</b> Because the element model represents the state of the code,
- /// it is possible for it to be semantically invalid. In particular, it is not
- /// safe to assume that the inheritance structure of a class does not contain
- /// a cycle. Clients that traverse the inheritance structure must explicitly
- /// guard against infinite loops.
- List<InterfaceType> get mixins;
-
- @override
- String get name;
-
- /// Return a list containing all of the superclass constraints defined for
- /// this class. The list will be empty if this class does not represent a
- /// mixin declaration. If this class _does_ represent a mixin declaration but
- /// the declaration does not have an `on` clause, then the list will contain
- /// the type for the class `Object`.
- ///
- /// <b>Note:</b> Because the element model represents the state of the code,
- /// it is possible for it to be semantically invalid. In particular, it is not
- /// safe to assume that the inheritance structure of a class does not contain
- /// a cycle. Clients that traverse the inheritance structure must explicitly
- /// guard against infinite loops.
- List<InterfaceType> get superclassConstraints;
+ implements
+ ClassOrAugmentationElement,
+ InterfaceDefiningElement,
+ _TmpSharedClassElement {
+ /// Returns the result of applying augmentations to this class.
+ AugmentedClassElement get augmented;
/// Return the superclass of this class, or `null` if either the class
- /// represents the class 'Object' or if the class represents a mixin
- /// declaration. All other classes will have a non-`null` superclass. If the
- /// superclass was not explicitly declared then the implicit superclass
- /// 'Object' will be returned.
+ /// represents the class 'Object'. If the superclass was not explicitly
+ /// declared then the implicit superclass 'Object' will be returned.
///
/// <b>Note:</b> Because the element model represents the state of the code,
/// it is possible for it to be semantically invalid. In particular, it is not
/// safe to assume that the inheritance structure of a class does not contain
/// a cycle. Clients that traverse the inheritance structure must explicitly
/// guard against infinite loops.
+ @override
InterfaceType? get supertype;
-
- /// Return the type of `this` expression for this class.
- ///
- /// For a class like `class MyClass<T, U> {}` the returned type is equivalent
- /// to the type `MyClass<T, U>`. So, the type arguments are the types of the
- /// type parameters, and either `none` or `star` nullability suffix is used
- /// for the type arguments, and the returned type depending on the
- /// nullability status of the declaring library.
- InterfaceType get thisType;
-
- /// Return the unnamed constructor declared in this class, or `null` if either
- /// this class does not declare an unnamed constructor but does declare named
- /// constructors or if this class represents a mixin declaration. The returned
- /// constructor will be synthetic if this class does not declare any
- /// constructors, in which case it will represent the default constructor for
- /// the class.
- ConstructorElement? get unnamedConstructor;
-
- /// Return the field (synthetic or explicit) defined in this class that has
- /// the given [name], or `null` if this class does not define a field with the
- /// given name.
- FieldElement? getField(String name);
-
- /// Return the element representing the getter with the given [name] that is
- /// declared in this class, or `null` if this class does not declare a getter
- /// with the given name.
- PropertyAccessorElement? getGetter(String name);
-
- /// Return the element representing the method with the given [name] that is
- /// declared in this class, or `null` if this class does not declare a method
- /// with the given name.
- MethodElement? getMethod(String name);
-
- /// Return the named constructor declared in this class with the given [name],
- /// or `null` if this class does not declare a named constructor with the
- /// given name.
- ConstructorElement? getNamedConstructor(String name);
-
- /// Return the element representing the setter with the given [name] that is
- /// declared in this class, or `null` if this class does not declare a setter
- /// with the given name.
- PropertyAccessorElement? getSetter(String name);
-
- /// Create the [InterfaceType] for this class with the given [typeArguments]
- /// and [nullabilitySuffix].
- InterfaceType instantiate({
- required List<DartType> typeArguments,
- required NullabilitySuffix nullabilitySuffix,
- });
-
- /// Return the element representing the method that results from looking up
- /// the given [methodName] in this class with respect to the given [library],
- /// ignoring abstract methods, or `null` if the look up fails. The behavior of
- /// this method is defined by the Dart Language Specification in section
- /// 16.15.1:
- /// <blockquote>
- /// The result of looking up method <i>m</i> in class <i>C</i> with respect to
- /// library <i>L</i> is: If <i>C</i> declares an instance method named
- /// <i>m</i> that is accessible to <i>L</i>, then that method is the result of
- /// the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
- /// result of the lookup is the result of looking up method <i>m</i> in
- /// <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
- /// failed.
- /// </blockquote>
- MethodElement? lookUpConcreteMethod(
- String methodName, LibraryElement library);
-
- /// Return the element representing the getter that results from looking up
- /// the given [getterName] in this class with respect to the given [library],
- /// or `null` if the look up fails. The behavior of this method is defined by
- /// the Dart Language Specification in section 16.15.2:
- /// <blockquote>
- /// The result of looking up getter (respectively setter) <i>m</i> in class
- /// <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
- /// instance getter (respectively setter) named <i>m</i> that is accessible to
- /// <i>L</i>, then that getter (respectively setter) is the result of the
- /// lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
- /// of the lookup is the result of looking up getter (respectively setter)
- /// <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
- /// lookup has failed.
- /// </blockquote>
- PropertyAccessorElement? lookUpGetter(
- String getterName, LibraryElement library);
-
- /// Return the element representing the getter that results from looking up
- /// the given [getterName] in the superclass of this class with respect to the
- /// given [library], ignoring abstract getters, or `null` if the look up
- /// fails. The behavior of this method is defined by the Dart Language
- /// Specification in section 16.15.2:
- /// <blockquote>
- /// The result of looking up getter (respectively setter) <i>m</i> in class
- /// <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
- /// instance getter (respectively setter) named <i>m</i> that is accessible to
- /// <i>L</i>, then that getter (respectively setter) is the result of the
- /// lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
- /// of the lookup is the result of looking up getter (respectively setter)
- /// <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
- /// lookup has failed.
- /// </blockquote>
- PropertyAccessorElement? lookUpInheritedConcreteGetter(
- String getterName, LibraryElement library);
-
- /// Return the element representing the method that results from looking up
- /// the given [methodName] in the superclass of this class with respect to the
- /// given [library], ignoring abstract methods, or `null` if the look up
- /// fails. The behavior of this method is defined by the Dart Language
- /// Specification in section 16.15.1:
- /// <blockquote>
- /// The result of looking up method <i>m</i> in class <i>C</i> with respect to
- /// library <i>L</i> is: If <i>C</i> declares an instance method named
- /// <i>m</i> that is accessible to <i>L</i>, then that method is the result of
- /// the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
- /// result of the lookup is the result of looking up method <i>m</i> in
- /// <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
- /// failed.
- /// </blockquote>
- MethodElement? lookUpInheritedConcreteMethod(
- String methodName, LibraryElement library);
-
- /// Return the element representing the setter that results from looking up
- /// the given [setterName] in the superclass of this class with respect to the
- /// given [library], ignoring abstract setters, or `null` if the look up
- /// fails. The behavior of this method is defined by the Dart Language
- /// Specification in section 16.15.2:
- /// <blockquote>
- /// The result of looking up getter (respectively setter) <i>m</i> in class
- /// <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
- /// instance getter (respectively setter) named <i>m</i> that is accessible to
- /// <i>L</i>, then that getter (respectively setter) is the result of the
- /// lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
- /// of the lookup is the result of looking up getter (respectively setter)
- /// <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
- /// lookup has failed.
- /// </blockquote>
- PropertyAccessorElement? lookUpInheritedConcreteSetter(
- String setterName, LibraryElement library);
-
- /// Return the element representing the method that results from looking up
- /// the given [methodName] in the superclass of this class with respect to the
- /// given [library], or `null` if the look up fails. The behavior of this
- /// method is defined by the Dart Language Specification in section 16.15.1:
- /// <blockquote>
- /// The result of looking up method <i>m</i> in class <i>C</i> with respect to
- /// library <i>L</i> is: If <i>C</i> declares an instance method named
- /// <i>m</i> that is accessible to <i>L</i>, then that method is the result of
- /// the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
- /// result of the lookup is the result of looking up method <i>m</i> in
- /// <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
- /// failed.
- /// </blockquote>
- MethodElement? lookUpInheritedMethod(
- String methodName, LibraryElement library);
-
- /// Return the element representing the method that results from looking up
- /// the given [methodName] in this class with respect to the given [library],
- /// or `null` if the look up fails. The behavior of this method is defined by
- /// the Dart Language Specification in section 16.15.1:
- /// <blockquote>
- /// The result of looking up method <i>m</i> in class <i>C</i> with respect to
- /// library <i>L</i> is: If <i>C</i> declares an instance method named
- /// <i>m</i> that is accessible to <i>L</i>, then that method is the result of
- /// the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
- /// result of the lookup is the result of looking up method <i>m</i> in
- /// <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
- /// failed.
- /// </blockquote>
- MethodElement? lookUpMethod(String methodName, LibraryElement library);
-
- /// Return the element representing the setter that results from looking up
- /// the given [setterName] in this class with respect to the given [library],
- /// or `null` if the look up fails. The behavior of this method is defined by
- /// the Dart Language Specification in section 16.15.2:
- /// <blockquote>
- /// The result of looking up getter (respectively setter) <i>m</i> in class
- /// <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
- /// instance getter (respectively setter) named <i>m</i> that is accessible to
- /// <i>L</i>, then that getter (respectively setter) is the result of the
- /// lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
- /// of the lookup is the result of looking up getter (respectively setter)
- /// <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
- /// lookup has failed.
- /// </blockquote>
- PropertyAccessorElement? lookUpSetter(
- String setterName, LibraryElement library);
}
/// An element that is contained within a [ClassElement].
///
-/// When the 'extension-methods' experiment is enabled, these elements can also
-/// be contained within an extension element.
-///
/// Clients may not extend, implement or mix-in this class.
abstract class ClassMemberElement implements Element {
// TODO(brianwilkerson) Either remove this class or rename it to something
@@ -413,6 +233,18 @@
bool get isStatic;
}
+/// Shared interface between [ClassElement] and [ClassAugmentationElement].
+///
+/// Clients may not extend, implement or mix-in this class.
+@experimental
+abstract class ClassOrAugmentationElement
+ implements InterfaceDefiningOrAugmentationElement {
+ /// The immediate augmentation of this element, or `null` if there are no
+ /// augmentations. [ClassAugmentationElement.augmentationTarget] is the back
+ /// pointer that will point at this element.
+ ClassAugmentationElement? get augmentation;
+}
+
/// An element representing a compilation unit.
///
/// Clients may not extend, implement or mix-in this class.
@@ -474,12 +306,30 @@
ClassElement? getType(String name);
}
+/// An element representing a constructor augmentation.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class ConstructorAugmentationElement implements ConstructorElement {
+ /// Returns the element that is augmented by this augmentation. The chain of
+ /// augmentations should normally end with a [ConstructorElement] that is not
+ /// [ConstructorAugmentationElement], but might end with `null` immediately
+ /// or after a few intermediate [ConstructorAugmentationElement]s in case of
+ /// invalid code when an augmentation is declared without the corresponding
+ /// constructor declaration.
+ ConstructorElement? get augmentationTarget;
+}
+
/// An element representing a constructor or a factory method defined within a
/// class.
///
/// Clients may not extend, implement or mix-in this class.
abstract class ConstructorElement
implements ClassMemberElement, ExecutableElement, ConstantEvaluationTarget {
+ /// The immediate augmentation of this element, or `null` if there are no
+ /// augmentations. [ConstructorAugmentationElement.augmentationTarget] is
+ /// the back pointer that will point at this element.
+ ConstructorAugmentationElement? get augmentation;
+
@override
ConstructorElement get declaration;
@@ -1193,6 +1043,48 @@
R? visitTypeParameterElement(TypeParameterElement element);
}
+/// An enum augmentation, defined by a enum augmentation declaration.
+///
+/// Clients may not extend, implement or mix-in this class.
+@experimental
+abstract class EnumAugmentationElement implements EnumOrAugmentationElement {
+ /// Returns the element that is augmented by this augmentation; or `null` if
+ /// there is no corresponding element to be augmented. The chain of
+ /// augmentations should normally end with a [EnumElement], but might end
+ /// with `null` immediately or after a few intermediate
+ /// [EnumAugmentationElement]s in case of invalid code when an augmentation
+ /// is declared without the corresponding enum declaration.
+ EnumOrAugmentationElement? get augmentationTarget;
+}
+
+/// An element that represents an enum.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class EnumElement
+ implements
+ EnumOrAugmentationElement,
+ InterfaceDefiningElement,
+ _TmpSharedClassElement {
+ /// Returns the result of applying augmentations to this element.
+ AugmentedEnumElement get augmented;
+
+ /// Returns `Enum` from `dart:core`.
+ @override
+ InterfaceType? get supertype;
+}
+
+/// Shared interface between [EnumElement] and [EnumAugmentationElement].
+///
+/// Clients may not extend, implement or mix-in this class.
+@experimental
+abstract class EnumOrAugmentationElement
+ implements InterfaceDefiningOrAugmentationElement {
+ /// The immediate augmentation of this element, or `null` if there are no
+ /// augmentations. [EnumAugmentationElement.augmentationTarget] is the back
+ /// pointer that will point at this element.
+ EnumAugmentationElement? get augmentation;
+}
+
/// An element representing an executable object, including functions, methods,
/// constructors, getters, and setters.
///
@@ -1309,14 +1201,29 @@
PropertyAccessorElement? getSetter(String name);
}
-/// A field defined within a class.
+/// A field augmentation defined within a class.
///
-/// When the 'extension-methods' experiment is enabled, these elements can also
-/// be contained within an extension element.
+/// Clients may not extend, implement or mix-in this class.
+abstract class FieldAugmentationElement implements FieldElement {
+ /// Returns the element that is augmented by this augmentation. The chain of
+ /// augmentations should normally end with a [FieldElement] that is not
+ /// [FieldAugmentationElement], but might end with `null` immediately or
+ /// after a few intermediate [FieldAugmentationElement]s in case of invalid
+ /// code when an augmentation is declared without the corresponding field
+ /// declaration.
+ FieldElement? get augmentationTarget;
+}
+
+/// A field defined within a class.
///
/// Clients may not extend, implement or mix-in this class.
abstract class FieldElement
implements ClassMemberElement, PropertyInducingElement {
+ /// The immediate augmentation of this element, or `null` if there are no
+ /// augmentations. [FieldAugmentationElement.augmentationTarget] is the
+ /// back pointer that will point at this element.
+ FieldAugmentationElement? get augmentation;
+
@override
FieldElement get declaration;
@@ -1444,6 +1351,87 @@
PrefixElement get element;
}
+/// An element that defines an [InterfaceType].
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class InterfaceDefiningElement
+ implements InterfaceDefiningOrAugmentationElement, TypeDefiningElement {
+ /// Return a list containing all the supertypes defined for this element and
+ /// its supertypes. This includes superclasses, mixins, interfaces, and
+ /// superclass constraints.
+ List<InterfaceType> get allSupertypes;
+
+ /// Return the type of `this` expression for this element.
+ ///
+ /// For a class like `class MyClass<T, U> {}` the returned type is equivalent
+ /// to the type `MyClass<T, U>`. So, the type arguments are the types of the
+ /// type parameters, and either `none` or `star` is used for the nullability
+ /// suffix is used, depending on the nullability status of the declaring
+ /// library.
+ InterfaceType get thisType;
+
+ /// Create the [InterfaceType] for this element with the given [typeArguments]
+ /// and [nullabilitySuffix].
+ InterfaceType instantiate({
+ required List<DartType> typeArguments,
+ required NullabilitySuffix nullabilitySuffix,
+ });
+}
+
+/// Shared interface between [InterfaceDefiningElement] and augmentations.
+///
+/// Augmentations of [InterfaceDefiningElement] don't have their own type,
+/// so they cannot by instantiated into an [InterfaceType].
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class InterfaceDefiningOrAugmentationElement
+ implements TypeParameterizedElement {
+ /// Return a list containing all of the accessors (getters and setters)
+ /// declared in this class.
+ List<PropertyAccessorElement> get accessors;
+
+ /// Return a list containing all of the constructors declared in this class.
+ /// The list will be empty if there are no constructors defined for this
+ /// class, as is the case when this element represents an enum or a mixin.
+ List<ConstructorElement> get constructors;
+
+ @Deprecated('Use enclosingElement2 instead')
+ @override
+ CompilationUnitElement get enclosingElement;
+
+ @override
+ CompilationUnitElement get enclosingElement2;
+
+ /// Return a list containing all of the fields declared in this class.
+ List<FieldElement> get fields;
+
+ /// Return a list containing all of the interfaces that are implemented by
+ /// this class.
+ ///
+ /// <b>Note:</b> Because the element model represents the state of the code,
+ /// it is possible for it to be semantically invalid. In particular, it is not
+ /// safe to assume that the inheritance structure of a class does not contain
+ /// a cycle. Clients that traverse the inheritance structure must explicitly
+ /// guard against infinite loops.
+ List<InterfaceType> get interfaces;
+
+ /// Return a list containing all of the methods declared in this class.
+ List<MethodElement> get methods;
+
+ /// Return a list containing all of the mixins that are applied to the class
+ /// being extended in order to derive the superclass of this class.
+ ///
+ /// <b>Note:</b> Because the element model represents the state of the code,
+ /// it is possible for it to be semantically invalid. In particular, it is not
+ /// safe to assume that the inheritance structure of a class does not contain
+ /// a cycle. Clients that traverse the inheritance structure must explicitly
+ /// guard against infinite loops.
+ List<InterfaceType> get mixins;
+
+ @override
+ String get name;
+}
+
/// A label associated with a statement.
///
/// Clients may not extend, implement or mix-in this class.
@@ -1700,17 +1688,82 @@
String get name;
}
-/// An element that represents a method defined within a class.
+/// An element that represents a method augmentation defined within a class.
///
-/// When the 'extension-methods' experiment is enabled, these elements can also
-/// be contained within an extension element.
+/// Clients may not extend, implement or mix-in this class.
+abstract class MethodAugmentationElement implements MethodElement {
+ /// Returns the element that is augmented by this augmentation. The chain of
+ /// augmentations should normally end with a [MethodElement] that is not
+ /// [MethodAugmentationElement], but might end with `null` immediately or
+ /// after a few intermediate [MethodAugmentationElement]s in case of invalid
+ /// code when an augmentation is declared without the corresponding method
+ /// declaration.
+ MethodElement? get augmentationTarget;
+}
+
+/// An element that represents a method defined within a class.
///
/// Clients may not extend, implement or mix-in this class.
abstract class MethodElement implements ClassMemberElement, ExecutableElement {
+ /// The immediate augmentation of this element, or `null` if there are no
+ /// augmentations. [MethodAugmentationElement.augmentationTarget] is the
+ /// back pointer that will point at this element.
+ MethodAugmentationElement? get augmentation;
+
@override
MethodElement get declaration;
}
+/// A class augmentation, defined by a mixin augmentation declaration.
+///
+/// Clients may not extend, implement or mix-in this class.
+@experimental
+abstract class MixinAugmentationElement implements MixinOrAugmentationElement {
+ /// Returns the element that is augmented by this augmentation; or `null` if
+ /// there is no corresponding element to be augmented. The chain of
+ /// augmentations should normally end with a [MixinElement], but might end
+ /// with `null` immediately or after a few intermediate
+ /// [MixinAugmentationElement]s in case of invalid code when an augmentation
+ /// is declared without the corresponding class declaration.
+ MixinOrAugmentationElement? get augmentationTarget;
+}
+
+/// An element that represents a mixin.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class MixinElement
+ implements
+ MixinOrAugmentationElement,
+ InterfaceDefiningElement,
+ _TmpSharedClassElement {
+ /// Returns the result of applying augmentations to this element.
+ AugmentedMixinElement get augmented;
+
+ /// Returns the superclass constraints defined for this mixin. If the
+ /// declaration does not have an `on` clause, then the list will contain
+ /// the type for the class `Object`.
+ ///
+ /// <b>Note:</b> Because the element model represents the state of the code,
+ /// it is possible for it to be semantically invalid. In particular, it is not
+ /// safe to assume that the inheritance structure of a class does not contain
+ /// a cycle. Clients that traverse the inheritance structure must explicitly
+ /// guard against infinite loops.
+ @override
+ List<InterfaceType> get superclassConstraints;
+}
+
+/// Shared interface between [MixinElement] and [MixinAugmentationElement].
+///
+/// Clients may not extend, implement or mix-in this class.
+@experimental
+abstract class MixinOrAugmentationElement
+ implements InterfaceDefiningOrAugmentationElement {
+ /// The immediate augmentation of this element, or `null` if there are no
+ /// augmentations. [MixinAugmentationElement.augmentationTarget] is the back
+ /// pointer that will point at this element.
+ MixinAugmentationElement? get augmentation;
+}
+
/// A pseudo-element that represents multiple elements defined within a single
/// scope that have the same name. This situation is not allowed by the
/// language, so objects implementing this interface always represent an error.
@@ -1893,6 +1946,21 @@
String get name;
}
+/// Augmentation of a [PropertyAccessorElement].
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class PropertyAccessorAugmentationElement
+ implements PropertyAccessorElement {
+ /// Returns the element that is augmented by this augmentation. The chain of
+ /// augmentations should normally end with a [PropertyAccessorElement] that
+ /// is not [PropertyAccessorAugmentationElement], but might end with `null`
+ /// immediately or after a few intermediate
+ /// [PropertyAccessorAugmentationElement]s in case of invalid code when an
+ /// augmentation is declared without the corresponding property accessor
+ /// declaration.
+ PropertyAccessorElement? get augmentationTarget;
+}
+
/// A getter or a setter. Note that explicitly defined property accessors
/// implicitly define a synthetic field. Symmetrically, synthetic accessors are
/// implicitly created for explicitly defined fields. The following rules apply:
@@ -1907,6 +1975,11 @@
///
/// Clients may not extend, implement or mix-in this class.
abstract class PropertyAccessorElement implements ExecutableElement {
+ /// The immediate augmentation of this element, or `null` if there are no
+ /// augmentations. [PropertyAccessorAugmentationElement.augmentationTarget]
+ /// is the back pointer that will point at this element.
+ PropertyAccessorAugmentationElement? get augmentation;
+
/// Return the accessor representing the getter that corresponds to (has the
/// same name as) this setter, or `null` if this accessor is not a setter or
/// if there is no corresponding getter.
@@ -2206,3 +2279,255 @@
@override
Source get source;
}
+
+/// Properties that existed in [ClassElement], so we should keep them for
+/// backward compatibility for now. But we want them to be either moved, or
+/// removed.
+abstract class _TmpSharedClassElement {
+ /// Return `true` if this class or its superclass declares a non-final
+ /// instance field.
+ bool get hasNonFinalField;
+
+ /// Return `true` if this class declares a static member.
+ bool get hasStaticMember;
+
+ /// Return `true` if this class is abstract. A class is abstract if it has an
+ /// explicit `abstract` modifier or if it is implicitly abstract, such as a
+ /// class defined by a mixin declaration. Note, that this definition of
+ /// <i>abstract</i> is different from <i>has unimplemented members</i>.
+ /// TODO(scheglov) Deprecate and replace it.
+ bool get isAbstract;
+
+ /// Return `true` if this class represents the class 'Enum' defined in the
+ /// dart:core library.
+ bool get isDartCoreEnum;
+
+ /// Return `true` if this class represents the class 'Object' defined in the
+ /// dart:core library.
+ bool get isDartCoreObject;
+
+ /// Return `true` if this class is defined by an enum declaration.
+ bool get isEnum;
+
+ /// Return `true` if this class is defined by a mixin declaration.
+ bool get isMixin;
+
+ /// Return `true` if this class is a mixin application. A class is a mixin
+ /// application if it was declared using the syntax "class A = B with C;".
+ bool get isMixinApplication;
+
+ /// Return `true` if this class can validly be used as a mixin when defining
+ /// another class. For classes defined by a mixin declaration, the result is
+ /// always `true`. For classes defined by a class declaration or a mixin
+ /// application, the behavior of this method is defined by the Dart Language
+ /// Specification in section 9:
+ /// <blockquote>
+ /// It is a compile-time error if a declared or derived mixin refers to super.
+ /// It is a compile-time error if a declared or derived mixin explicitly
+ /// declares a constructor. It is a compile-time error if a mixin is derived
+ /// from a class whose superclass is not Object.
+ /// </blockquote>
+ /// TODO(scheglov) Deprecate and remove it.
+ bool get isValidMixin;
+
+ /// Return a list containing all of the superclass constraints defined for
+ /// this class. The list will be empty if this class does not represent a
+ /// mixin declaration. If this class _does_ represent a mixin declaration but
+ /// the declaration does not have an `on` clause, then the list will contain
+ /// the type for the class `Object`.
+ ///
+ /// <b>Note:</b> Because the element model represents the state of the code,
+ /// it is possible for it to be semantically invalid. In particular, it is not
+ /// safe to assume that the inheritance structure of a class does not contain
+ /// a cycle. Clients that traverse the inheritance structure must explicitly
+ /// guard against infinite loops.
+ /// TODO(scheglov) Deprecate and remove it.
+ List<InterfaceType> get superclassConstraints;
+
+ /// Return the superclass of this class, or `null` if either the class
+ /// represents the class 'Object' or if the class represents a mixin
+ /// declaration. All other classes will have a non-`null` superclass. If the
+ /// superclass was not explicitly declared then the implicit superclass
+ /// 'Object' will be returned.
+ ///
+ /// <b>Note:</b> Because the element model represents the state of the code,
+ /// it is possible for it to be semantically invalid. In particular, it is not
+ /// safe to assume that the inheritance structure of a class does not contain
+ /// a cycle. Clients that traverse the inheritance structure must explicitly
+ /// guard against infinite loops.
+ InterfaceType? get supertype;
+
+ /// Returns the unnamed constructor declared directly in this class. If the
+ /// class does not declare any constructors, a synthetic default constructor
+ /// will be returned.
+ /// TODO(scheglov) Deprecate and remove it.
+ ConstructorElement? get unnamedConstructor;
+
+ /// Returns the field (synthetic or explicit) defined directly in this
+ /// class or augmentation that has the given [name].
+ /// TODO(scheglov) Deprecate and remove it.
+ FieldElement? getField(String name);
+
+ /// Returns the getter (synthetic or explicit) defined directly in this
+ /// class or augmentation that has the given [name].
+ /// TODO(scheglov) Deprecate and remove it.
+ PropertyAccessorElement? getGetter(String name);
+
+ /// Returns the method defined directly in this class or augmentation that
+ /// has the given [name].
+ /// TODO(scheglov) Deprecate and remove it.
+ MethodElement? getMethod(String name);
+
+ /// Returns the constructor defined directly in this class or augmentation
+ /// that has the given [name].
+ /// TODO(scheglov) Deprecate and remove it.
+ ConstructorElement? getNamedConstructor(String name);
+
+ /// Returns the setter (synthetic or explicit) defined directly in this
+ /// class or augmentation that has the given [name].
+ /// TODO(scheglov) Deprecate and remove it.
+ PropertyAccessorElement? getSetter(String name);
+
+ /// Return the element representing the method that results from looking up
+ /// the given [methodName] in this class with respect to the given [library],
+ /// ignoring abstract methods, or `null` if the look up fails. The behavior of
+ /// this method is defined by the Dart Language Specification in section
+ /// 16.15.1:
+ /// <blockquote>
+ /// The result of looking up method <i>m</i> in class <i>C</i> with respect to
+ /// library <i>L</i> is: If <i>C</i> declares an instance method named
+ /// <i>m</i> that is accessible to <i>L</i>, then that method is the result of
+ /// the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
+ /// result of the lookup is the result of looking up method <i>m</i> in
+ /// <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
+ /// failed.
+ /// </blockquote>
+ /// TODO(scheglov) Deprecate and remove it.
+ MethodElement? lookUpConcreteMethod(
+ String methodName, LibraryElement library);
+
+ /// Return the element representing the getter that results from looking up
+ /// the given [getterName] in this class with respect to the given [library],
+ /// or `null` if the look up fails. The behavior of this method is defined by
+ /// the Dart Language Specification in section 16.15.2:
+ /// <blockquote>
+ /// The result of looking up getter (respectively setter) <i>m</i> in class
+ /// <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
+ /// instance getter (respectively setter) named <i>m</i> that is accessible to
+ /// <i>L</i>, then that getter (respectively setter) is the result of the
+ /// lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
+ /// of the lookup is the result of looking up getter (respectively setter)
+ /// <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
+ /// lookup has failed.
+ /// </blockquote>
+ /// TODO(scheglov) Deprecate and remove it.
+ PropertyAccessorElement? lookUpGetter(
+ String getterName, LibraryElement library);
+
+ /// Return the element representing the getter that results from looking up
+ /// the given [getterName] in the superclass of this class with respect to the
+ /// given [library], ignoring abstract getters, or `null` if the look up
+ /// fails. The behavior of this method is defined by the Dart Language
+ /// Specification in section 16.15.2:
+ /// <blockquote>
+ /// The result of looking up getter (respectively setter) <i>m</i> in class
+ /// <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
+ /// instance getter (respectively setter) named <i>m</i> that is accessible to
+ /// <i>L</i>, then that getter (respectively setter) is the result of the
+ /// lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
+ /// of the lookup is the result of looking up getter (respectively setter)
+ /// <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
+ /// lookup has failed.
+ /// </blockquote>
+ /// TODO(scheglov) Deprecate and remove it.
+ PropertyAccessorElement? lookUpInheritedConcreteGetter(
+ String getterName, LibraryElement library);
+
+ /// Return the element representing the method that results from looking up
+ /// the given [methodName] in the superclass of this class with respect to the
+ /// given [library], ignoring abstract methods, or `null` if the look up
+ /// fails. The behavior of this method is defined by the Dart Language
+ /// Specification in section 16.15.1:
+ /// <blockquote>
+ /// The result of looking up method <i>m</i> in class <i>C</i> with respect to
+ /// library <i>L</i> is: If <i>C</i> declares an instance method named
+ /// <i>m</i> that is accessible to <i>L</i>, then that method is the result of
+ /// the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
+ /// result of the lookup is the result of looking up method <i>m</i> in
+ /// <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
+ /// failed.
+ /// </blockquote>
+ /// TODO(scheglov) Deprecate and remove it.
+ MethodElement? lookUpInheritedConcreteMethod(
+ String methodName, LibraryElement library);
+
+ /// Return the element representing the setter that results from looking up
+ /// the given [setterName] in the superclass of this class with respect to the
+ /// given [library], ignoring abstract setters, or `null` if the look up
+ /// fails. The behavior of this method is defined by the Dart Language
+ /// Specification in section 16.15.2:
+ /// <blockquote>
+ /// The result of looking up getter (respectively setter) <i>m</i> in class
+ /// <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
+ /// instance getter (respectively setter) named <i>m</i> that is accessible to
+ /// <i>L</i>, then that getter (respectively setter) is the result of the
+ /// lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
+ /// of the lookup is the result of looking up getter (respectively setter)
+ /// <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
+ /// lookup has failed.
+ /// </blockquote>
+ /// TODO(scheglov) Deprecate and remove it.
+ PropertyAccessorElement? lookUpInheritedConcreteSetter(
+ String setterName, LibraryElement library);
+
+ /// Return the element representing the method that results from looking up
+ /// the given [methodName] in the superclass of this class with respect to the
+ /// given [library], or `null` if the look up fails. The behavior of this
+ /// method is defined by the Dart Language Specification in section 16.15.1:
+ /// <blockquote>
+ /// The result of looking up method <i>m</i> in class <i>C</i> with respect to
+ /// library <i>L</i> is: If <i>C</i> declares an instance method named
+ /// <i>m</i> that is accessible to <i>L</i>, then that method is the result of
+ /// the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
+ /// result of the lookup is the result of looking up method <i>m</i> in
+ /// <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
+ /// failed.
+ /// </blockquote>
+ /// TODO(scheglov) Deprecate and remove it.
+ MethodElement? lookUpInheritedMethod(
+ String methodName, LibraryElement library);
+
+ /// Return the element representing the method that results from looking up
+ /// the given [methodName] in this class with respect to the given [library],
+ /// or `null` if the look up fails. The behavior of this method is defined by
+ /// the Dart Language Specification in section 16.15.1:
+ /// <blockquote>
+ /// The result of looking up method <i>m</i> in class <i>C</i> with respect to
+ /// library <i>L</i> is: If <i>C</i> declares an instance method named
+ /// <i>m</i> that is accessible to <i>L</i>, then that method is the result of
+ /// the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
+ /// result of the lookup is the result of looking up method <i>m</i> in
+ /// <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
+ /// failed.
+ /// </blockquote>
+ /// TODO(scheglov) Deprecate and remove it.
+ MethodElement? lookUpMethod(String methodName, LibraryElement library);
+
+ /// Return the element representing the setter that results from looking up
+ /// the given [setterName] in this class with respect to the given [library],
+ /// or `null` if the look up fails. The behavior of this method is defined by
+ /// the Dart Language Specification in section 16.15.2:
+ /// <blockquote>
+ /// The result of looking up getter (respectively setter) <i>m</i> in class
+ /// <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
+ /// instance getter (respectively setter) named <i>m</i> that is accessible to
+ /// <i>L</i>, then that getter (respectively setter) is the result of the
+ /// lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
+ /// of the lookup is the result of looking up getter (respectively setter)
+ /// <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
+ /// lookup has failed.
+ /// </blockquote>
+ /// TODO(scheglov) Deprecate and remove it.
+ PropertyAccessorElement? lookUpSetter(
+ String setterName, LibraryElement library);
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index c36fadc..3818634 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -117,13 +117,6 @@
var node = NodeLocator(offset).searchWithin(parsedUnit);
- if (_hasEmptyCompletionContext(node)) {
- return AnalysisForCompletionResult(
- parsedUnit: parsedUnit,
- resolvedNodes: [],
- );
- }
-
var errorListener = RecordingErrorListener();
return performance.run('resolve', (performance) {
@@ -1011,81 +1004,6 @@
...dependenciesFinder.dependencies,
];
}
-
- static bool _hasEmptyCompletionContext(AstNode? node) {
- if (node is DoubleLiteral || node is IntegerLiteral) {
- return true;
- }
-
- if (node is SimpleIdentifier) {
- var parent = node.parent;
-
- if (parent is ConstructorDeclaration && parent.name == node) {
- return true;
- }
-
- if (parent is ConstructorFieldInitializer && parent.fieldName == node) {
- return true;
- }
-
- if (parent is FormalParameter && parent.identifier == node) {
- // We use elements to access fields.
- if (parent is FieldFormalParameter) {
- return false;
- }
- // We use elements to access the enclosing constructor.
- if (parent is SuperFormalParameter) {
- return false;
- }
- // We have a contributor that looks at the type, but it is syntactic.
- return true;
- }
-
- if (parent is FunctionDeclaration && parent.name == node) {
- return true;
- }
-
- if (parent is MethodDeclaration && parent.name == node) {
- return true;
- }
-
- // The name of a NamedType does not provide any context.
- // So, we don't need to resolve anything.
- if (parent is NamedType) {
- var parent3 = parent.parent?.parent;
- // `class A {foo^ int bar = 0;}` looks as `class A {foo int; bar = 0;}`.
- if (parent3 is FieldDeclaration) {
- return false;
- }
- // `{foo^ print(0);}` looks as `foo print; (0);`.
- if (parent3 is VariableDeclarationStatement &&
- parent3.semicolon.isSynthetic) {
- return false;
- }
- return true;
- }
-
- if (parent is TypeParameter && parent.name == node) {
- return true;
- }
-
- // We have a contributor that looks at the type, but it is syntactic.
- if (parent is VariableDeclaration && parent.name == node) {
- final parent2 = parent.parent;
- final parent3 = parent2?.parent;
- // `class A { foo^ }` looks like `class A { <noType> foo; }`.
- if (parent2 is VariableDeclarationList &&
- parent2.type == null &&
- parent3 is FieldDeclaration &&
- parent3.semicolon.isSynthetic) {
- return false;
- }
- return true;
- }
- }
-
- return false;
- }
}
/// Analysis result for single file.
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index af9564a..437d67b 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -610,6 +610,18 @@
}
@override
+ ClassAugmentationElement? get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
+ AugmentedClassElement get augmented {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
List<ConstructorElement> get constructors {
if (!identical(_constructors, _Sentinel.constructorElement)) {
return _constructors;
@@ -1363,6 +1375,12 @@
/// and [offset].
ConstructorElementImpl(super.name, super.offset);
+ @override
+ ConstructorAugmentationElement? get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
/// Return the constant initializers for this element, which will be empty if
/// there are no initializers, or `null` if there was an error in the source.
List<ConstructorInitializer> get constantInitializers {
@@ -2868,7 +2886,7 @@
}
/// An [AbstractClassElementImpl] which is an enum.
-class EnumElementImpl extends AbstractClassElementImpl {
+class EnumElementImpl extends AbstractClassElementImpl implements EnumElement {
ElementLinkedData? linkedData;
List<ConstructorElement> _constructors = _Sentinel.constructorElement;
@@ -2881,6 +2899,18 @@
return _accessors;
}
+ @override
+ Never get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
+ Never get augmented {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
List<FieldElement> get constants {
return fields.where((field) => field.isEnumConstant).toList();
}
@@ -3453,6 +3483,12 @@
FieldElementImpl(super.name, super.offset);
@override
+ FieldAugmentationElement? get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
FieldElement get declaration => this;
@override
@@ -4751,6 +4787,12 @@
MethodElementImpl(super.name, super.offset);
@override
+ MethodAugmentationElement? get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
MethodElement get declaration => prototype ?? this;
@override
@@ -4805,7 +4847,7 @@
}
/// A [ClassElementImpl] representing a mixin declaration.
-class MixinElementImpl extends ClassElementImpl {
+class MixinElementImpl extends ClassElementImpl implements MixinElement {
// TODO(brianwilkerson) Consider creating an abstract superclass of
// ClassElementImpl that contains the portions of the API that this class
// needs, and make this class extend the new class.
@@ -4822,6 +4864,18 @@
MixinElementImpl(super.name, super.offset);
@override
+ Never get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
+ Never get augmented {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
bool get isAbstract => true;
@override
@@ -5640,6 +5694,12 @@
}
@override
+ PropertyAccessorAugmentationElement? get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
PropertyAccessorElement? get correspondingGetter {
if (isGetter) {
return null;
diff --git a/pkg/analyzer/lib/src/dart/element/member.dart b/pkg/analyzer/lib/src/dart/element/member.dart
index 28785d5..242170d 100644
--- a/pkg/analyzer/lib/src/dart/element/member.dart
+++ b/pkg/analyzer/lib/src/dart/element/member.dart
@@ -32,6 +32,12 @@
const <TypeParameterElement>[]);
@override
+ ConstructorAugmentationElement? get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
ConstructorElement get declaration => super.declaration as ConstructorElement;
@override
@@ -331,6 +337,12 @@
);
@override
+ FieldAugmentationElement? get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
FieldElement get declaration => super.declaration as FieldElement;
@override
@@ -779,6 +791,12 @@
);
@override
+ MethodAugmentationElement? get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
MethodElement get declaration => super.declaration as MethodElement;
@Deprecated('Use enclosingElement2 instead')
@@ -985,6 +1003,12 @@
);
@override
+ PropertyAccessorAugmentationElement? get augmentation {
+ // TODO(scheglov) implement
+ throw UnimplementedError();
+ }
+
+ @override
PropertyAccessorElement? get correspondingGetter {
var baseGetter = declaration.correspondingGetter;
if (baseGetter == null) {
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index cd17032..ec868b6 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -190,13 +190,19 @@
}
} else if (element.isMustBeOverridden) {
if ((parent is MethodDeclaration && parent.isStatic) ||
- (parent is FieldDeclaration && parent.isStatic)) {
+ (parent is FieldDeclaration && parent.isStatic) ||
+ parent.parent is ExtensionDeclaration ||
+ parent.parent is EnumDeclaration) {
_errorReporter.reportErrorForNode(
HintCode.INVALID_ANNOTATION_TARGET,
node,
[node.name.name, 'instance members of classes and mixins'],
);
- } else if (parent.parent is ExtensionDeclaration ||
+ }
+ } else if (element.isMustCallSuper) {
+ if ((parent is MethodDeclaration && parent.isStatic) ||
+ (parent is FieldDeclaration && parent.isStatic) ||
+ parent.parent is ExtensionDeclaration ||
parent.parent is EnumDeclaration) {
_errorReporter.reportErrorForNode(
HintCode.INVALID_ANNOTATION_TARGET,
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_packages.dart b/pkg/analyzer/lib/src/test_utilities/mock_packages.dart
index 05d9022..d6c64be 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_packages.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_packages.dart
@@ -179,6 +179,12 @@
const _MustBeOverridden();
}
+@Target({
+ TargetKind.field,
+ TargetKind.getter,
+ TargetKind.method,
+ TargetKind.setter,
+})
class _MustCallSuper {
const _MustCallSuper();
}
diff --git a/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart b/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart
index a9deb63..5bf59c1 100644
--- a/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart
@@ -22,32 +22,6 @@
return driverFor(testFile);
}
- test_class__fieldDeclaration_type_namedType_name() async {
- var result = await _resolveTestCode(r'''
-class A {
- var f1 = 0;
- dou^ f2 = null;
- var f3 = 1;
-}
-''');
-
- result.assertResolvedNodes([
- 'dou f2 = null;',
- ]);
- }
-
- test_class__fieldDeclaration_type_namedType_typeArgument_name() async {
- var result = await _resolveTestCode(r'''
-class A {
- var f1 = 0;
- List<doub^>? f2 = null;
- var f3 = 1;
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_class_body_identifier_beforeFieldDeclaration() async {
var result = await _resolveTestCode(r'''
class A {
@@ -61,14 +35,6 @@
]);
}
- test_class_extends_name() async {
- var result = await _resolveTestCode(r'''
-class A extends foo^ {}
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_class_fieldDeclaration_initializer() async {
var result = await _resolveTestCode(r'''
class A {
@@ -83,12 +49,18 @@
]);
}
- test_class_implements_name() async {
+ test_class_fieldDeclaration_type_namedType_name() async {
var result = await _resolveTestCode(r'''
-class A implements foo^ {}
+class A {
+ var f1 = 0;
+ dou^ f2 = null;
+ var f3 = 1;
+}
''');
- result.assertResolvedNodes([]);
+ result.assertResolvedNodes([
+ 'dou f2 = null;',
+ ]);
}
test_class_methodDeclaration_body() async {
@@ -113,36 +85,6 @@
]);
}
- test_class_methodDeclaration_name() async {
- var result = await _resolveTestCode(r'''
-class A {
- void foo^() {
- print(0);
- }
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_class_methodDeclaration_returnType_name() async {
- var result = await _resolveTestCode(r'''
-class A {
- doub^ foo() {}
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_class_with_name() async {
- var result = await _resolveTestCode(r'''
-class A with foo^ {}
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_classDeclaration_body_identifier() async {
var result = await _resolveTestCode(r'''
class A {}
@@ -194,26 +136,6 @@
]);
}
- test_constructorDeclaration_fieldInitializer_name() async {
- var result = await _resolveTestCode(r'''
-class A {}
-
-class B {
- var f;
-
- void foo1() {}
-
- B(int a) : bar^ = 0 {
- print(0);
- }
-
- void foo2() {}
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_constructorDeclaration_fieldInitializer_value() async {
var result = await _resolveTestCode(r'''
class A {
@@ -231,18 +153,6 @@
]);
}
- test_constructorDeclaration_name() async {
- var result = await _resolveTestCode(r'''
-class A {
- A.foo^() {
- print(0);
- }
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_constructorDeclaration_superFormalParameter_name() async {
var result = await _resolveTestCode(r'''
class A {
@@ -260,14 +170,6 @@
]);
}
- test_doubleLiteral() async {
- var result = await _resolveTestCode(r'''
-var v = 1.2^;
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_extension_methodDeclaration_body() async {
var result = await _resolveTestCode(r'''
extension E on int {
@@ -288,38 +190,6 @@
]);
}
- test_extension_methodDeclaration_name() async {
- var result = await _resolveTestCode(r'''
-extension E on int {
- void foo^() {
- print(0);
- }
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_extension_methodDeclaration_returnType_name() async {
- var result = await _resolveTestCode(r'''
-extension E on int {
- doub^ foo() {}
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_extension_on_name() async {
- var result = await _resolveTestCode(r'''
-extension E on int^ {
- void foo() {}
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_functionDeclaration_body() async {
var result = await _resolveTestCode(r'''
void foo1() {}
@@ -356,24 +226,6 @@
]);
}
- test_functionDeclaration_name() async {
- var result = await _resolveTestCode(r'''
-void foo^() {
- print(0);
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_functionDeclaration_returnType_name() async {
- var result = await _resolveTestCode(r'''
-doub^ f() {}
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_importDirective_show_name() async {
var result = await _resolveTestCode(r'''
import 'dart:async';
@@ -398,42 +250,6 @@
]);
}
- test_integerLiteral() async {
- var result = await _resolveTestCode(r'''
-var v = 0^;
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_localVariableDeclaration_name() async {
- var result = await _resolveTestCode(r'''
-void f() {
- var foo^
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_localVariableDeclaration_type_name() async {
- var result = await _resolveTestCode(r'''
-void f() {
- doub^ a;
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_mixin_implements_name() async {
- var result = await _resolveTestCode(r'''
-mixin M implements foo^ {}
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_mixin_methodDeclaration_body() async {
var result = await _resolveTestCode(r'''
class A {}
@@ -456,36 +272,6 @@
]);
}
- test_mixin_methodDeclaration_name() async {
- var result = await _resolveTestCode(r'''
-mixin M {
- void foo^() {
- print(0);
- }
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_mixin_methodDeclaration_returnType_name() async {
- var result = await _resolveTestCode(r'''
-mixin M {
- doub^ foo() {}
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_mixin_on_name() async {
- var result = await _resolveTestCode(r'''
-mixin M on foo^ {}
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_processPendingChanges() async {
addTestFile('class A {}');
@@ -503,22 +289,6 @@
]);
}
- test_simpleFormalParameter_name() async {
- var result = await _resolveTestCode(r'''
-void f(doub^) {}
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_simpleFormalParameter_type_name() async {
- var result = await _resolveTestCode(r'''
-void f(doub^ a) {}
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_topLevelVariable_initializer() async {
var result = await _resolveTestCode(r'''
var v1 = 0;
@@ -531,36 +301,6 @@
]);
}
- test_topLevelVariable_name() async {
- var result = await _resolveTestCode(r'''
-var v1 = 0;
-var v2^
-var v3 = 0;
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_topLevelVariable_type_namedType_name() async {
- var result = await _resolveTestCode(r'''
-var v1 = 0;
-doub^ v2 = null;
-var v3 = 1;
-''');
-
- result.assertResolvedNodes([]);
- }
-
- test_topLevelVariable_type_namedType_typeArgument_name() async {
- var result = await _resolveTestCode(r'''
-var v1 = 0;
-List<doub^>? v2 = null;
-var v3 = 1;
-''');
-
- result.assertResolvedNodes([]);
- }
-
test_typedef_name_nothing() async {
var result = await _resolveTestCode(r'''
typedef F^
@@ -569,16 +309,6 @@
_assertWholeUnitResolved(result);
}
- test_typeParameter_name() async {
- var result = await _resolveTestCode(r'''
-void f<T^>() {
- print(0);
-}
-''');
-
- result.assertResolvedNodes([]);
- }
-
int _newFileWithOffset(String path, String content) {
var offset = content.indexOf('^');
expect(offset, isNot(equals(-1)), reason: 'missing ^');
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_annotation_target_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_annotation_target_test.dart
index dd7c738..9923146 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_annotation_target_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_annotation_target_test.dart
@@ -10,6 +10,7 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(InvalidAnnotationTarget_MustBeOverriddenTest);
+ defineReflectiveTests(InvalidAnnotationTarget_MustCallSuperTest);
defineReflectiveTests(InvalidAnnotationTargetTest);
});
}
@@ -179,6 +180,170 @@
}
@reflectiveTest
+class InvalidAnnotationTarget_MustCallSuperTest
+ extends PubPackageResolutionTest {
+ @override
+ void setUp() {
+ super.setUp();
+ writeTestPackageConfigWithMeta();
+ }
+
+ test_class_instance_field() async {
+ await assertNoErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+class A {
+ @mustCallSuper
+ int f = 0;
+}
+''');
+ }
+
+ test_class_instance_getter() async {
+ await assertNoErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+class A {
+ @mustCallSuper
+ int get f => 0;
+}
+''');
+ }
+
+ test_class_instance_method() async {
+ await assertNoErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+class A {
+ @mustCallSuper
+ void m() {}
+}
+''');
+ }
+
+ test_class_instance_setter() async {
+ await assertNoErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+class A {
+ @mustCallSuper
+ void set s(int value) {}
+}
+''');
+ }
+
+ test_class_static_field() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+class A {
+ @mustCallSuper
+ static int f = 0;
+}
+''', [
+ error(HintCode.INVALID_ANNOTATION_TARGET, 45, 14),
+ ]);
+ }
+
+ test_class_static_getter() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+class A {
+ @mustCallSuper
+ static int get f => 0;
+}
+''', [
+ error(HintCode.INVALID_ANNOTATION_TARGET, 45, 14),
+ ]);
+ }
+
+ test_class_static_method() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+class A {
+ @mustCallSuper
+ static void m() {}
+}
+''', [
+ error(HintCode.INVALID_ANNOTATION_TARGET, 45, 14),
+ ]);
+ }
+
+ test_class_static_setter() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+class A {
+ @mustCallSuper
+ static void set f(int value) {}
+}
+''', [
+ error(HintCode.INVALID_ANNOTATION_TARGET, 45, 14),
+ ]);
+ }
+
+ test_constructor() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+class C {
+ @mustCallSuper
+ C();
+}
+''', [
+ error(HintCode.INVALID_ANNOTATION_TARGET, 47, 13),
+ ]);
+ }
+
+ test_enum_member() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+enum E {
+ one, two;
+ @mustCallSuper
+ void m() {}
+}
+''', [
+ error(HintCode.INVALID_ANNOTATION_TARGET, 57, 14),
+ ]);
+ }
+
+ test_extension_member() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+extension E on String {
+ @mustCallSuper
+ void m() {}
+}
+''', [
+ error(HintCode.INVALID_ANNOTATION_TARGET, 60, 14),
+ ]);
+ }
+
+ test_mixin_instance_method() async {
+ await assertNoErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+mixin M {
+ @mustCallSuper
+ void m() {}
+}
+''');
+ }
+
+ test_topLevel() async {
+ await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+@mustCallSuper
+void m() {}
+''', [
+ error(HintCode.INVALID_ANNOTATION_TARGET, 35, 13),
+ ]);
+ }
+}
+
+@reflectiveTest
class InvalidAnnotationTargetTest extends PubPackageResolutionTest {
// todo(pq): add tests for topLevelVariables:
// https://dart-review.googlesource.com/c/sdk/+/200301
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index edba42a..43e340b 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -2,6 +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.
+import 'package:analyzer/dart/analysis/code_style_options.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
@@ -1351,6 +1352,9 @@
int timeStamp, this.libraryChangeBuilder)
: super(changeBuilder, resolvedUnit.path, timeStamp);
+ CodeStyleOptions get codeStyleOptions =>
+ resolvedUnit.session.analysisContext.analysisOptions.codeStyleOptions;
+
@override
bool get hasEdits => super.hasEdits || librariesToImport.isNotEmpty;
@@ -1794,7 +1798,7 @@
var whatPath = pathContext.fromUri(uri);
return getRelativePath(whatPath);
}
- var preferRelative = _isLintEnabled('prefer_relative_imports');
+ var preferRelative = codeStyleOptions.useRelativeUris;
if (forceRelative || (preferRelative && !forceAbsolute)) {
if (canBeRelativeImport(uri, resolvedUnit.uri)) {
var whatPath = resolvedUnit.session.uriConverter.uriToPath(uri);
@@ -1833,12 +1837,6 @@
return element.library == resolvedUnit.libraryElement;
}
- bool _isLintEnabled(String lintName) {
- final analysisOptions =
- resolvedUnit.session.analysisContext.analysisOptions;
- return analysisOptions.isLintEnabled(lintName);
- }
-
/// Create an edit to replace the return type of the innermost function
/// containing the given [node] with the type `Future`. The [typeProvider] is
/// used to check the current return type, because if it is already `Future`
diff --git a/pkg/compiler/lib/src/inferrer/abstract_value_strategy.dart b/pkg/compiler/lib/src/inferrer/abstract_value_strategy.dart
index 87d5b7d1..d98ec15 100644
--- a/pkg/compiler/lib/src/inferrer/abstract_value_strategy.dart
+++ b/pkg/compiler/lib/src/inferrer/abstract_value_strategy.dart
@@ -2,10 +2,8 @@
// 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.
-// @dart = 2.10
-
import '../universe/world_builder.dart' show SelectorConstraintsStrategy;
-import '../world.dart';
+import '../world_interfaces.dart';
import 'abstract_value_domain.dart';
/// Strategy for the abstraction of runtime values used by the global type
diff --git a/pkg/compiler/lib/src/ir/impact.dart b/pkg/compiler/lib/src/ir/impact.dart
index e1e7473..abe548f 100644
--- a/pkg/compiler/lib/src/ir/impact.dart
+++ b/pkg/compiler/lib/src/ir/impact.dart
@@ -162,12 +162,12 @@
void registerInstanceSet(
ir.DartType receiverType, ClassRelation relation, ir.Member target);
- void registerSuperInvocation(ir.Member target, int positionalArguments,
+ void registerSuperInvocation(ir.Member? target, int positionalArguments,
List<String> namedArguments, List<ir.DartType> typeArguments);
- void registerSuperGet(ir.Member target);
+ void registerSuperGet(ir.Member? target);
- void registerSuperSet(ir.Member target);
+ void registerSuperSet(ir.Member? target);
void registerSuperInitializer(
ir.Constructor source,
diff --git a/pkg/compiler/lib/src/ir/impact_data.dart b/pkg/compiler/lib/src/ir/impact_data.dart
index 2a65114..5704733 100644
--- a/pkg/compiler/lib/src/ir/impact_data.dart
+++ b/pkg/compiler/lib/src/ir/impact_data.dart
@@ -606,19 +606,19 @@
}
@override
- void registerSuperSet(ir.Member target) {
- (_data._superSets ??= []).add(target);
+ void registerSuperSet(ir.Member? target) {
+ (_data._superSets ??= []).add(target!);
}
@override
- void registerSuperGet(ir.Member target) {
- (_data._superGets ??= []).add(target);
+ void registerSuperGet(ir.Member? target) {
+ (_data._superGets ??= []).add(target!);
}
@override
- void registerSuperInvocation(ir.Member target, int positionalArguments,
+ void registerSuperInvocation(ir.Member? target, int positionalArguments,
List<String> namedArguments, List<ir.DartType> typeArguments) {
- (_data._superInvocations ??= []).add(_SuperInvocation(target,
+ (_data._superInvocations ??= []).add(_SuperInvocation(target!,
_CallStructure(positionalArguments, namedArguments, typeArguments)));
}
diff --git a/pkg/compiler/lib/src/js_model/closure.dart b/pkg/compiler/lib/src/js_model/closure.dart
index 5cd2856..4a05706 100644
--- a/pkg/compiler/lib/src/js_model/closure.dart
+++ b/pkg/compiler/lib/src/js_model/closure.dart
@@ -987,42 +987,6 @@
}
}
-class ClosureClassDefinition implements ClassDefinition {
- /// Tag used for identifying serialized [ClosureClassDefinition] objects in a
- /// debugging data stream.
- static const String tag = 'closure-class-definition';
-
- @override
- final SourceSpan location;
-
- ClosureClassDefinition(this.location);
-
- factory ClosureClassDefinition.readFromDataSource(DataSourceReader source) {
- source.begin(tag);
- SourceSpan location = source.readSourceSpan();
- source.end(tag);
- return ClosureClassDefinition(location);
- }
-
- @override
- void writeToDataSink(DataSinkWriter sink) {
- sink.writeEnum(ClassKind.closure);
- sink.begin(tag);
- sink.writeSourceSpan(location);
- sink.end(tag);
- }
-
- @override
- ClassKind get kind => ClassKind.closure;
-
- @override
- ir.Node get node =>
- throw UnsupportedError('ClosureClassDefinition.node for $location');
-
- @override
- String toString() => 'ClosureClassDefinition(kind:$kind,location:$location)';
-}
-
abstract class ClosureMemberData implements JMemberData {
@override
final MemberDefinition definition;
@@ -1037,7 +1001,7 @@
}
@override
- InterfaceType getMemberThisType(JsToElementMap elementMap) {
+ InterfaceType getMemberThisType(covariant JsToElementMap elementMap) {
return memberThisType;
}
}
@@ -1181,44 +1145,6 @@
ClassTypeVariableAccess.none;
}
-class RecordContainerDefinition implements ClassDefinition {
- /// Tag used for identifying serialized [RecordContainerDefinition] objects in
- /// a debugging data stream.
- static const String tag = 'record-definition';
-
- @override
- final SourceSpan location;
-
- RecordContainerDefinition(this.location);
-
- factory RecordContainerDefinition.readFromDataSource(
- DataSourceReader source) {
- source.begin(tag);
- SourceSpan location = source.readSourceSpan();
- source.end(tag);
- return RecordContainerDefinition(location);
- }
-
- @override
- void writeToDataSink(DataSinkWriter sink) {
- sink.writeEnum(ClassKind.record);
- sink.begin(tag);
- sink.writeSourceSpan(location);
- sink.end(tag);
- }
-
- @override
- ClassKind get kind => ClassKind.record;
-
- @override
- ir.Node get node =>
- throw UnsupportedError('RecordContainerDefinition.node for $location');
-
- @override
- String toString() =>
- 'RecordContainerDefinition(kind:$kind,location:$location)';
-}
-
abstract class ClosureRtiNeed {
bool classNeedsTypeArguments(ClassEntity cls);
diff --git a/pkg/compiler/lib/src/js_model/element_map.dart b/pkg/compiler/lib/src/js_model/element_map.dart
index 7350f29..75b6a58 100644
--- a/pkg/compiler/lib/src/js_model/element_map.dart
+++ b/pkg/compiler/lib/src/js_model/element_map.dart
@@ -19,7 +19,6 @@
import '../js_model/class_type_variable_access.dart';
import '../js_model/elements.dart' show JGeneratorBody;
import '../native/behavior.dart';
-import '../serialization/serialization.dart';
import '../universe/call_structure.dart';
import '../universe/selector.dart';
import '../world.dart';
@@ -36,6 +35,7 @@
JCommonElements get commonElements;
/// Access to the [DartTypes] object.
+ @override
DartTypes get types;
/// Returns the [DartType] corresponding to [type].
@@ -64,6 +64,7 @@
Selector getSelector(ir.Expression node);
/// Returns the [MemberEntity] corresponding to the member [node].
+ @override
MemberEntity getMember(ir.Member node);
/// Returns the [FunctionEntity] corresponding to the procedure [node].
@@ -102,6 +103,7 @@
// TODO(johnniwinther,sigmund): Remove the need for [memberContext]. This is
// only needed because effectively constant expressions are not replaced by
// constant expressions during resolution.
+ @override
ConstantValue getConstantValue(
ir.Member memberContext, ir.Expression expression,
{bool requireConstant = true, bool implicitNull = false});
@@ -111,6 +113,7 @@
///
/// These should only appear within the defaultValues object attached to
/// closures and tearoffs when emitting Function.apply.
+ @override
ConstantValue getRequiredSentinelConstantValue();
/// Return the [ImportEntity] corresponding to [node].
@@ -120,6 +123,7 @@
ClassDefinition getClassDefinition(covariant ClassEntity cls);
/// [ElementEnvironment] for library, class and member lookup.
+ @override
JElementEnvironment get elementEnvironment;
/// Returns the list of [DartType]s corresponding to [types].
@@ -242,81 +246,6 @@
return null;
}
-enum ClassKind {
- regular,
- closure,
- // TODO(efortuna, johnniwinther): Record is not a class, but is
- // masquerading as one currently for consistency with the old element model.
- record,
-}
-
-/// Definition information for a [ClassEntity].
-abstract class ClassDefinition {
- /// The kind of the defined class. This determines the semantics of [node].
- ClassKind get kind;
-
- /// The defining [ir.Node] for this class, if supported by its [kind].
- ir.Node get node;
-
- /// The canonical location of [cls]. This is used for sorting the classes
- /// in the emitted code.
- SourceSpan get location;
-
- /// Deserializes a [ClassDefinition] object from [source].
- factory ClassDefinition.readFromDataSource(DataSourceReader source) {
- ClassKind kind = source.readEnum(ClassKind.values);
- switch (kind) {
- case ClassKind.regular:
- return RegularClassDefinition.readFromDataSource(source);
- case ClassKind.closure:
- return ClosureClassDefinition.readFromDataSource(source);
- case ClassKind.record:
- return RecordContainerDefinition.readFromDataSource(source);
- }
- throw UnsupportedError("Unexpected ClassKind $kind");
- }
-
- /// Serializes this [ClassDefinition] to [sink].
- void writeToDataSink(DataSinkWriter sink);
-}
-
-/// A class directly defined by its [ir.Class] node.
-class RegularClassDefinition implements ClassDefinition {
- /// Tag used for identifying serialized [RegularClassDefinition] objects in a
- /// debugging data stream.
- static const String tag = 'regular-class-definition';
-
- @override
- final ir.Class node;
-
- RegularClassDefinition(this.node);
-
- factory RegularClassDefinition.readFromDataSource(DataSourceReader source) {
- source.begin(tag);
- ir.Class node = source.readClassNode();
- source.end(tag);
- return RegularClassDefinition(node);
- }
-
- @override
- void writeToDataSink(DataSinkWriter sink) {
- sink.writeEnum(kind);
- sink.begin(tag);
- sink.writeClassNode(node);
- sink.end(tag);
- }
-
- @override
- SourceSpan get location => computeSourceSpanFromTreeNode(node);
-
- @override
- ClassKind get kind => ClassKind.regular;
-
- @override
- String toString() => 'RegularClassDefinition(kind:$kind,'
- 'node:$node,location:$location)';
-}
-
/// Returns the initializer for [field].
///
/// If [field] is an instance field with a null literal initializer `null` is
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index 7836deb..f005c4d 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -55,10 +55,12 @@
import 'closure_migrated.dart' as closureMigrated;
import 'elements.dart';
import 'element_map.dart';
+import 'element_map_interfaces.dart' as interfaces show JsKernelToElementMap;
import 'env.dart';
import 'locals.dart';
-class JsKernelToElementMap implements JsToElementMap, IrToElementMap {
+class JsKernelToElementMap
+ implements interfaces.JsKernelToElementMap, JsToElementMap, IrToElementMap {
/// Tag used for identifying serialized [JsKernelToElementMap] objects in a
/// debugging data stream.
static const String tag = 'js-kernel-to-element-map';
diff --git a/pkg/compiler/lib/src/js_model/element_map_interfaces.dart b/pkg/compiler/lib/src/js_model/element_map_interfaces.dart
index 8016e27..7b548e09 100644
--- a/pkg/compiler/lib/src/js_model/element_map_interfaces.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_interfaces.dart
@@ -5,12 +5,24 @@
import 'package:compiler/src/js_model/element_map_migrated.dart';
import 'package:kernel/ast.dart' as ir;
+import '../common/elements.dart';
+import '../constants/values.dart';
import '../elements/entities.dart';
import '../elements/types.dart';
+import '../ir/element_map.dart';
// TODO(48820): Remove this interface when nnbd migration is done.
abstract class JsToElementMap {
+ JElementEnvironment get elementEnvironment;
+ DartTypes get types;
+ ConstantValue getConstantValue(
+ ir.Member memberContext, ir.Expression expression,
+ {bool requireConstant = true, bool implicitNull = false});
DartType getDartType(ir.DartType type);
+ MemberEntity getMember(ir.Member node);
MemberDefinition getMemberDefinition(MemberEntity member);
+ ConstantValue getRequiredSentinelConstantValue();
}
+
+abstract class JsKernelToElementMap implements JsToElementMap, IrToElementMap {}
diff --git a/pkg/compiler/lib/src/js_model/element_map_migrated.dart b/pkg/compiler/lib/src/js_model/element_map_migrated.dart
index fb5417f..1794774 100644
--- a/pkg/compiler/lib/src/js_model/element_map_migrated.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_migrated.dart
@@ -340,3 +340,151 @@
}
failedAt(function, "Unexpected function definition $definition.");
}
+
+enum ClassKind {
+ regular,
+ closure,
+ // TODO(efortuna, johnniwinther): Record is not a class, but is
+ // masquerading as one currently for consistency with the old element model.
+ record,
+}
+
+/// Definition information for a [ClassEntity].
+abstract class ClassDefinition {
+ /// The kind of the defined class. This determines the semantics of [node].
+ ClassKind get kind;
+
+ /// The defining [ir.Node] for this class, if supported by its [kind].
+ ir.Node get node;
+
+ /// The canonical location of [cls]. This is used for sorting the classes
+ /// in the emitted code.
+ SourceSpan get location;
+
+ /// Deserializes a [ClassDefinition] object from [source].
+ factory ClassDefinition.readFromDataSource(DataSourceReader source) {
+ ClassKind kind = source.readEnum(ClassKind.values);
+ switch (kind) {
+ case ClassKind.regular:
+ return RegularClassDefinition.readFromDataSource(source);
+ case ClassKind.closure:
+ return ClosureClassDefinition.readFromDataSource(source);
+ case ClassKind.record:
+ return RecordContainerDefinition.readFromDataSource(source);
+ }
+ }
+
+ /// Serializes this [ClassDefinition] to [sink].
+ void writeToDataSink(DataSinkWriter sink);
+}
+
+/// A class directly defined by its [ir.Class] node.
+class RegularClassDefinition implements ClassDefinition {
+ /// Tag used for identifying serialized [RegularClassDefinition] objects in a
+ /// debugging data stream.
+ static const String tag = 'regular-class-definition';
+
+ @override
+ final ir.Class node;
+
+ RegularClassDefinition(this.node);
+
+ factory RegularClassDefinition.readFromDataSource(DataSourceReader source) {
+ source.begin(tag);
+ ir.Class node = source.readClassNode();
+ source.end(tag);
+ return RegularClassDefinition(node);
+ }
+
+ @override
+ void writeToDataSink(DataSinkWriter sink) {
+ sink.writeEnum(kind);
+ sink.begin(tag);
+ sink.writeClassNode(node);
+ sink.end(tag);
+ }
+
+ @override
+ SourceSpan get location => computeSourceSpanFromTreeNode(node);
+
+ @override
+ ClassKind get kind => ClassKind.regular;
+
+ @override
+ String toString() => 'RegularClassDefinition(kind:$kind,'
+ 'node:$node,location:$location)';
+}
+
+class ClosureClassDefinition implements ClassDefinition {
+ /// Tag used for identifying serialized [ClosureClassDefinition] objects in a
+ /// debugging data stream.
+ static const String tag = 'closure-class-definition';
+
+ @override
+ final SourceSpan location;
+
+ ClosureClassDefinition(this.location);
+
+ factory ClosureClassDefinition.readFromDataSource(DataSourceReader source) {
+ source.begin(tag);
+ SourceSpan location = source.readSourceSpan();
+ source.end(tag);
+ return ClosureClassDefinition(location);
+ }
+
+ @override
+ void writeToDataSink(DataSinkWriter sink) {
+ sink.writeEnum(ClassKind.closure);
+ sink.begin(tag);
+ sink.writeSourceSpan(location);
+ sink.end(tag);
+ }
+
+ @override
+ ClassKind get kind => ClassKind.closure;
+
+ @override
+ ir.Node get node =>
+ throw UnsupportedError('ClosureClassDefinition.node for $location');
+
+ @override
+ String toString() => 'ClosureClassDefinition(kind:$kind,location:$location)';
+}
+
+class RecordContainerDefinition implements ClassDefinition {
+ /// Tag used for identifying serialized [RecordContainerDefinition] objects in
+ /// a debugging data stream.
+ static const String tag = 'record-definition';
+
+ @override
+ final SourceSpan location;
+
+ RecordContainerDefinition(this.location);
+
+ factory RecordContainerDefinition.readFromDataSource(
+ DataSourceReader source) {
+ source.begin(tag);
+ SourceSpan location = source.readSourceSpan();
+ source.end(tag);
+ return RecordContainerDefinition(location);
+ }
+
+ @override
+ void writeToDataSink(DataSinkWriter sink) {
+ sink.writeEnum(ClassKind.record);
+ sink.begin(tag);
+ sink.writeSourceSpan(location);
+ sink.end(tag);
+ }
+
+ @override
+ ClassKind get kind => ClassKind.record;
+
+ @override
+ ir.Node get node =>
+ throw UnsupportedError('RecordContainerDefinition.node for $location');
+
+ @override
+ String toString() =>
+ 'RecordContainerDefinition(kind:$kind,location:$location)';
+}
diff --git a/pkg/compiler/lib/src/js_model/env.dart b/pkg/compiler/lib/src/js_model/env.dart
index 0bf7e80..f597c52 100644
--- a/pkg/compiler/lib/src/js_model/env.dart
+++ b/pkg/compiler/lib/src/js_model/env.dart
@@ -19,9 +19,18 @@
import '../ordered_typeset.dart';
import '../serialization/deferrable.dart';
import '../serialization/serialization.dart';
-import 'closure.dart';
-import 'element_map.dart';
-import 'element_map_impl.dart';
+import 'closure.dart'
+ show
+ ClosureClassData,
+ RecordClassData,
+ ClosureFunctionData,
+ ClosureFieldData;
+import 'element_map_interfaces.dart' show JsToElementMap, JsKernelToElementMap;
+import 'element_map_migrated.dart'
+ show
+ ClassDefinition,
+ MemberDefinition,
+ forEachOrderedParameterByFunctionNode;
import 'elements.dart';
/// Environment for fast lookup of component libraries.
diff --git a/pkg/compiler/lib/src/kernel/element_map.dart b/pkg/compiler/lib/src/kernel/element_map.dart
index dc09e06..eb27551 100644
--- a/pkg/compiler/lib/src/kernel/element_map.dart
+++ b/pkg/compiler/lib/src/kernel/element_map.dart
@@ -8,10 +8,4 @@
export 'element_map_impl.dart';
/// Kinds of foreign functions.
-enum ForeignKind {
- JS,
- JS_BUILTIN,
- JS_EMBEDDED_GLOBAL,
- JS_INTERCEPTOR_CONSTANT,
- NONE,
-}
+export 'element_map_interfaces.dart' show ForeignKind;
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index 45f3683..c0e7df0 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -61,6 +61,7 @@
KernelElementEnvironment,
KernelToElementMapForClassHierarchy,
KernelToElementMapForImpactData,
+ KernelToElementMapForKernelImpact,
KernelToElementMapForNativeData,
KernelToElementMapForDeferredLoading;
import 'env.dart';
@@ -75,6 +76,7 @@
interfaces.KernelToElementMapForImpactData,
interfaces.KernelToElementMapForNativeData,
interfaces.KernelToElementMapForDeferredLoading,
+ interfaces.KernelToElementMapForKernelImpact,
IrToElementMap {
@override
final CompilerOptions options;
@@ -502,6 +504,7 @@
/// Kernel will say that C()'s super initializer resolves to Object(), but
/// this function will return an entity representing the unnamed mixin
/// application "Object+M"'s constructor.
+ @override
ConstructorEntity getSuperConstructor(
ir.Constructor sourceNode, ir.Member targetNode) {
ConstructorEntity source = getConstructor(sourceNode);
@@ -548,6 +551,7 @@
}
/// Returns the [InterfaceType] corresponding to [type].
+ @override
InterfaceType getInterfaceType(ir.InterfaceType type) =>
_typeConverter.visitType(type).withoutNullability;
@@ -867,6 +871,7 @@
ir.CoreTypes get coreTypes => _coreTypes ??= ir.CoreTypes(env.mainComponent);
/// Returns the type environment for the underlying kernel model.
+ @override
ir.TypeEnvironment get typeEnvironment =>
_typeEnvironment ??= ir.TypeEnvironment(coreTypes, classHierarchy);
@@ -894,6 +899,7 @@
}
/// Returns the [Name] corresponding to [name].
+ @override
Name getName(ir.Name name) {
return Name(name.text, name.isPrivate ? getLibrary(name.library) : null);
}
@@ -934,6 +940,7 @@
/// Returns the [Selector] corresponding to the invocation of [name] with
/// [arguments].
+ @override
Selector getInvocationSelector(ir.Name irName, int positionalArguments,
List<String> namedArguments, int typeArguments) {
Name name = getName(irName);
@@ -1042,6 +1049,7 @@
/// Computes the [NativeBehavior] for a call to the [JS] function.
/// TODO(johnniwinther): Cache this for later use.
+ @override
NativeBehavior getNativeBehaviorForJsCall(ir.StaticInvocation node) {
if (node.arguments.positional.length < 2 ||
node.arguments.named.isNotEmpty) {
@@ -1075,6 +1083,7 @@
/// TODO(johnniwinther): Cache this for later use.
/// Computes the [NativeBehavior] for a call to the [JS_BUILTIN]
/// function.
+ @override
NativeBehavior getNativeBehaviorForJsBuiltinCall(ir.StaticInvocation node) {
if (node.arguments.positional.length < 1) {
reporter.internalError(
@@ -1103,6 +1112,7 @@
/// Computes the [NativeBehavior] for a call to the
/// [JS_EMBEDDED_GLOBAL] function.
/// TODO(johnniwinther): Cache this for later use.
+ @override
NativeBehavior getNativeBehaviorForJsEmbeddedGlobalCall(
ir.StaticInvocation node) {
if (node.arguments.positional.length < 1) {
@@ -1207,6 +1217,7 @@
/// Returns the `noSuchMethod` [FunctionEntity] call from a
/// `super.noSuchMethod` invocation within [cls].
+ @override
FunctionEntity getSuperNoSuchMethod(ClassEntity cls) {
while (cls != null) {
cls = elementEnvironment.getSuperClass(cls);
@@ -1471,6 +1482,7 @@
}
/// NativeBasicData is need for computation of the default super class.
+ @override
NativeBasicData get nativeBasicData {
var data = _nativeBasicData;
if (data == null) {
@@ -1557,6 +1569,7 @@
}
/// Returns the [Local] corresponding to the local function [node].
+ @override
Local getLocalFunction(ir.LocalFunction node) {
KLocalFunction localFunction = localFunctionMap[node];
if (localFunction == null) {
@@ -1622,6 +1635,7 @@
}
/// Compute the kind of foreign helper function called by [node], if any.
+ @override
ForeignKind getForeignKind(ir.StaticInvocation node) {
if (commonElements.isForeignHelper(getMember(node.target))) {
switch (node.target.name.text) {
@@ -1640,6 +1654,7 @@
/// Computes the [InterfaceType] referenced by a call to the
/// [JS_INTERCEPTOR_CONSTANT] function, if any.
+ @override
InterfaceType getInterfaceTypeForJsInterceptorCall(ir.StaticInvocation node) {
if (node.arguments.positional.length != 1 ||
node.arguments.named.isNotEmpty) {
diff --git a/pkg/compiler/lib/src/kernel/element_map_interfaces.dart b/pkg/compiler/lib/src/kernel/element_map_interfaces.dart
index ac78b1f..12453f5 100644
--- a/pkg/compiler/lib/src/kernel/element_map_interfaces.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_interfaces.dart
@@ -9,26 +9,53 @@
import 'package:kernel/ast.dart' as ir
show
Class,
+ Constructor,
DartType,
+ Expression,
Field,
+ InterfaceType,
+ LibraryDependency,
+ LocalFunction,
Member,
+ Name,
Procedure,
ProcedureStubKind,
- LibraryDependency,
- Expression;
-import 'package:kernel/type_environment.dart' as ir show StaticTypeContext;
+ StaticInvocation;
+import 'package:kernel/type_environment.dart' as ir
+ show TypeEnvironment, StaticTypeContext;
import '../common.dart' show DiagnosticReporter;
import '../common/elements.dart' show CommonElements, ElementEnvironment;
import '../elements/entities.dart'
- show ClassEntity, ConstructorEntity, MemberEntity, ImportEntity;
+ show
+ ClassEntity,
+ ConstructorEntity,
+ FieldEntity,
+ FunctionEntity,
+ Local,
+ MemberEntity,
+ ImportEntity;
import '../constants/values.dart';
import '../elements/indexed.dart' show IndexedClass;
+import '../elements/names.dart' show Name;
import '../elements/types.dart' show DartType, DartTypes, InterfaceType;
import '../ir/constants.dart' show Dart2jsConstantEvaluator;
import '../native/behavior.dart';
+import '../js_backend/native_data.dart' show NativeBasicData;
import '../options.dart';
+import '../universe/selector.dart';
-abstract class KernelElementEnvironment implements ElementEnvironment {}
+enum ForeignKind {
+ JS,
+ JS_BUILTIN,
+ JS_EMBEDDED_GLOBAL,
+ JS_INTERCEPTOR_CONSTANT,
+ NONE,
+}
+
+abstract class KernelElementEnvironment implements ElementEnvironment {
+ Iterable<ConstantValue> getMemberMetadata(MemberEntity member,
+ {bool includeParameterMetadata = false});
+}
abstract class KernelToElementMapForNativeData {
KernelElementEnvironment get elementEnvironment;
@@ -75,6 +102,48 @@
ir.StaticTypeContext getStaticTypeContext(MemberEntity member);
}
+abstract class KernelToElementMapForKernelImpact {
+ CommonElements get commonElements;
+ KernelElementEnvironment get elementEnvironment;
+ NativeBasicData get nativeBasicData;
+ ir.TypeEnvironment get typeEnvironment;
+ InterfaceType createInterfaceType(
+ ir.Class cls, List<ir.DartType> typeArguments);
+ ClassEntity getClass(ir.Class node);
+ ConstantValue? getConstantValue(
+ ir.StaticTypeContext staticTypeContext, ir.Expression node,
+ {bool requireConstant = true,
+ bool implicitNull = false,
+ bool checkCasts = true});
+ ConstructorEntity getConstructor(ir.Member node);
+ ConstructorEntity getSuperConstructor(
+ ir.Constructor sourceNode, ir.Member targetNode);
+ DartType getDartType(ir.DartType type);
+ FieldEntity getField(ir.Field node);
+ ForeignKind getForeignKind(ir.StaticInvocation node);
+ FunctionEntity getMethod(ir.Procedure node);
+ FunctionEntity getSuperNoSuchMethod(ClassEntity cls);
+ InterfaceType getInterfaceType(ir.InterfaceType type);
+ ImportEntity? getImport(ir.LibraryDependency? node);
+ InterfaceType? getInterfaceTypeForJsInterceptorCall(ir.StaticInvocation node);
+ Local getLocalFunction(ir.LocalFunction node);
+ MemberEntity getMember(ir.Member node);
+ Name getName(ir.Name name);
+ NativeBehavior getNativeBehaviorForFieldLoad(ir.Field field,
+ Iterable<String> createsAnnotations, Iterable<String> returnsAnnotations,
+ {required bool isJsInterop});
+ NativeBehavior getNativeBehaviorForFieldStore(ir.Field field);
+ NativeBehavior getNativeBehaviorForJsBuiltinCall(ir.StaticInvocation node);
+ NativeBehavior getNativeBehaviorForJsCall(ir.StaticInvocation node);
+ NativeBehavior getNativeBehaviorForJsEmbeddedGlobalCall(
+ ir.StaticInvocation node);
+ NativeBehavior getNativeBehaviorForMethod(ir.Member member,
+ Iterable<String> createsAnnotations, Iterable<String> returnsAnnotations,
+ {required bool isJsInterop});
+ Selector getInvocationSelector(ir.Name irName, int positionalArguments,
+ List<String> namedArguments, int typeArguments);
+}
+
// Members which dart2js ignores.
bool memberIsIgnorable(ir.Member node, {ir.Class? cls}) {
if (node is! ir.Procedure) return false;
diff --git a/pkg/compiler/lib/src/kernel/kernel_impact.dart b/pkg/compiler/lib/src/kernel/kernel_impact.dart
index 7f95d06..c4c4f4c 100644
--- a/pkg/compiler/lib/src/kernel/kernel_impact.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_impact.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.
-// @dart = 2.10
-
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/type_environment.dart' as ir;
@@ -35,7 +33,9 @@
import '../universe/use.dart';
import '../universe/world_builder.dart';
import '../universe/world_impact.dart';
-import 'element_map.dart';
+import 'element_map_interfaces.dart';
+
+typedef KernelToElementMap = KernelToElementMapForKernelImpact;
/// [ImpactRegistry] that converts kernel based impact data to world impact
/// object based on the K model.
@@ -82,7 +82,7 @@
String typeToString(DartType type) =>
type.toStructuredText(dartTypes, _options);
- Object _computeReceiverConstraint(
+ Object? _computeReceiverConstraint(
ir.DartType receiverType, ClassRelation relation) {
if (receiverType is ir.InterfaceType) {
return StrongModeConstraint(commonElements, _nativeBasicData,
@@ -142,7 +142,7 @@
}
}
- List<DartType> _getTypeArguments(List<ir.DartType> types) {
+ List<DartType>? _getTypeArguments(List<ir.DartType> types) {
if (types.isEmpty) return null;
return types.map(elementMap.getDartType).toList();
}
@@ -156,12 +156,12 @@
void registerFieldNode(ir.Field field) {
if (field.isInstanceMember &&
_nativeBasicData
- .isNativeClass(elementMap.getClass(field.enclosingClass))) {
+ .isNativeClass(elementMap.getClass(field.enclosingClass!))) {
MemberEntity member = elementMap.getMember(field);
// TODO(johnniwinther): NativeDataBuilder already has the native behavior
// at this point. Use that instead.
bool isJsInterop = _nativeBasicData.isJsInteropMember(member);
- List<ConstantValue> metadata =
+ Iterable<ConstantValue> metadata =
elementMap.elementEnvironment.getMemberMetadata(member);
Iterable<String> createsAnnotations =
getCreatesAnnotations(dartTypes, reporter, commonElements, metadata);
@@ -181,7 +181,7 @@
// TODO(johnniwinther): NativeDataBuilder already has the native behavior
// at this point. Use that instead.
bool isJsInterop = _nativeBasicData.isJsInteropMember(member);
- List<ConstantValue> metadata =
+ Iterable<ConstantValue> metadata =
elementMap.elementEnvironment.getMemberMetadata(member);
Iterable<String> createsAnnotations =
getCreatesAnnotations(dartTypes, reporter, commonElements, metadata);
@@ -227,7 +227,7 @@
// TODO(johnniwinther): NativeDataBuilder already has the native behavior
// at this point. Use that instead.
bool isJsInterop = _nativeBasicData.isJsInteropMember(member);
- List<ConstantValue> metadata =
+ Iterable<ConstantValue> metadata =
elementMap.elementEnvironment.getMemberMetadata(member);
Iterable<String> createsAnnotations =
getCreatesAnnotations(dartTypes, reporter, commonElements, metadata);
@@ -271,7 +271,7 @@
@override
void registerListLiteral(ir.DartType elementType,
- {bool isConst, bool isEmpty}) {
+ {required bool isConst, required bool isEmpty}) {
// TODO(johnniwinther): Use the [isConstant] and [isEmpty] property when
// factory constructors are registered directly.
impactBuilder.registerTypeUse(TypeUse.instantiation(
@@ -280,7 +280,7 @@
@override
void registerSetLiteral(ir.DartType elementType,
- {bool isConst, bool isEmpty}) {
+ {required bool isConst, required bool isEmpty}) {
// TODO(johnniwinther): Use the [isEmpty] property when factory
// constructors are registered directly.
if (isConst) {
@@ -293,7 +293,7 @@
@override
void registerMapLiteral(ir.DartType keyType, ir.DartType valueType,
- {bool isConst, bool isEmpty}) {
+ {required bool isConst, required bool isEmpty}) {
// TODO(johnniwinther): Use the [isEmpty] property when factory
// constructors are registered directly.
if (isConst) {
@@ -312,19 +312,19 @@
int positionalArguments,
List<String> namedArguments,
List<ir.DartType> typeArguments,
- ir.LibraryDependency import,
- {bool isConst}) {
+ ir.LibraryDependency? import,
+ {required bool isConst}) {
ConstructorEntity constructor = elementMap.getConstructor(target);
CallStructure callStructure = CallStructure(
positionalArguments + namedArguments.length,
namedArguments,
typeArguments.length);
- ImportEntity deferredImport = elementMap.getImport(import);
+ ImportEntity? deferredImport = elementMap.getImport(import);
impactBuilder.registerStaticUse(isConst
? StaticUse.constConstructorInvoke(constructor, callStructure,
- elementMap.getDartType(type).withoutNullability, deferredImport)
+ elementMap.getInterfaceType(type), deferredImport)
: StaticUse.typedConstructorInvoke(constructor, callStructure,
- elementMap.getDartType(type).withoutNullability, deferredImport));
+ elementMap.getInterfaceType(type), deferredImport));
if (type.typeArguments.any((ir.DartType type) => type is! ir.DynamicType)) {
registerBackendImpact(_impacts.typeVariableBoundCheck);
}
@@ -340,8 +340,8 @@
@override
void registerConstInstantiation(ir.Class cls, List<ir.DartType> typeArguments,
- ir.LibraryDependency import) {
- ImportEntity deferredImport = elementMap.getImport(import);
+ ir.LibraryDependency? import) {
+ ImportEntity? deferredImport = elementMap.getImport(import);
InterfaceType type = elementMap.createInterfaceType(cls, typeArguments);
impactBuilder
.registerTypeUse(TypeUse.constInstantiation(type, deferredImport));
@@ -377,18 +377,18 @@
int positionalArguments,
List<String> namedArguments,
List<ir.DartType> typeArguments,
- ir.LibraryDependency import) {
+ ir.LibraryDependency? import) {
FunctionEntity target = elementMap.getMethod(procedure);
CallStructure callStructure = CallStructure(
positionalArguments + namedArguments.length,
namedArguments,
typeArguments.length);
- List<DartType> dartTypeArguments = _getTypeArguments(typeArguments);
+ List<DartType>? dartTypeArguments = _getTypeArguments(typeArguments);
if (commonElements.isExtractTypeArguments(target)) {
- _handleExtractTypeArguments(target, dartTypeArguments, callStructure);
+ _handleExtractTypeArguments(target, dartTypeArguments!, callStructure);
return;
} else {
- ImportEntity deferredImport = elementMap.getImport(import);
+ ImportEntity? deferredImport = elementMap.getImport(import);
impactBuilder.registerStaticUse(StaticUse.staticInvoke(
target, callStructure, dartTypeArguments, deferredImport));
}
@@ -409,7 +409,7 @@
elementMap.getNativeBehaviorForJsEmbeddedGlobalCall(node));
break;
case ForeignKind.JS_INTERCEPTOR_CONSTANT:
- InterfaceType type =
+ InterfaceType? type =
elementMap.getInterfaceTypeForJsInterceptorCall(node);
if (type != null) {
impactBuilder.registerTypeUse(TypeUse.instantiation(type));
@@ -449,29 +449,29 @@
@override
void registerStaticTearOff(
- ir.Procedure procedure, ir.LibraryDependency import) {
+ ir.Procedure procedure, ir.LibraryDependency? import) {
impactBuilder.registerStaticUse(StaticUse.staticTearOff(
elementMap.getMethod(procedure), elementMap.getImport(import)));
}
@override
- void registerStaticGet(ir.Member member, ir.LibraryDependency import) {
+ void registerStaticGet(ir.Member member, ir.LibraryDependency? import) {
impactBuilder.registerStaticUse(StaticUse.staticGet(
elementMap.getMember(member), elementMap.getImport(import)));
}
@override
- void registerStaticSet(ir.Member member, ir.LibraryDependency import) {
+ void registerStaticSet(ir.Member member, ir.LibraryDependency? import) {
impactBuilder.registerStaticUse(StaticUse.staticSet(
elementMap.getMember(member), elementMap.getImport(import)));
}
@override
- void registerSuperInvocation(ir.Member target, int positionalArguments,
+ void registerSuperInvocation(ir.Member? target, int positionalArguments,
List<String> namedArguments, List<ir.DartType> typeArguments) {
if (target != null) {
- FunctionEntity method = elementMap.getMember(target);
- List<DartType> dartTypeArguments = _getTypeArguments(typeArguments);
+ FunctionEntity method = elementMap.getMember(target) as FunctionEntity;
+ List<DartType>? dartTypeArguments = _getTypeArguments(typeArguments);
impactBuilder.registerStaticUse(StaticUse.superInvoke(
method,
CallStructure(positionalArguments + namedArguments.length,
@@ -481,18 +481,19 @@
// TODO(johnniwinther): Remove this when the CFE checks for missing
// concrete super targets.
impactBuilder.registerStaticUse(StaticUse.superInvoke(
- elementMap.getSuperNoSuchMethod(currentMember.enclosingClass),
+ elementMap.getSuperNoSuchMethod(currentMember.enclosingClass!),
CallStructure.ONE_ARG));
registerBackendImpact(_impacts.superNoSuchMethod);
}
}
@override
- void registerSuperGet(ir.Member target) {
+ void registerSuperGet(ir.Member? target) {
if (target != null) {
MemberEntity member = elementMap.getMember(target);
if (member.isFunction) {
- impactBuilder.registerStaticUse(StaticUse.superTearOff(member));
+ impactBuilder.registerStaticUse(
+ StaticUse.superTearOff(member as FunctionEntity));
} else {
impactBuilder.registerStaticUse(StaticUse.superGet(member));
}
@@ -500,26 +501,28 @@
// TODO(johnniwinther): Remove this when the CFE checks for missing
// concrete super targets.
impactBuilder.registerStaticUse(StaticUse.superInvoke(
- elementMap.getSuperNoSuchMethod(currentMember.enclosingClass),
+ elementMap.getSuperNoSuchMethod(currentMember.enclosingClass!),
CallStructure.ONE_ARG));
registerBackendImpact(_impacts.superNoSuchMethod);
}
}
@override
- void registerSuperSet(ir.Member target) {
+ void registerSuperSet(ir.Member? target) {
if (target != null) {
MemberEntity member = elementMap.getMember(target);
if (member.isField) {
- impactBuilder.registerStaticUse(StaticUse.superFieldSet(member));
+ impactBuilder
+ .registerStaticUse(StaticUse.superFieldSet(member as FieldEntity));
} else {
- impactBuilder.registerStaticUse(StaticUse.superSetterSet(member));
+ impactBuilder.registerStaticUse(
+ StaticUse.superSetterSet(member as FunctionEntity));
}
} else {
// TODO(johnniwinther): Remove this when the CFE checks for missing
// concrete super targets.
impactBuilder.registerStaticUse(StaticUse.superInvoke(
- elementMap.getSuperNoSuchMethod(currentMember.enclosingClass),
+ elementMap.getSuperNoSuchMethod(currentMember.enclosingClass!),
CallStructure.ONE_ARG));
registerBackendImpact(_impacts.superNoSuchMethod);
}
@@ -535,7 +538,7 @@
positionalArguments + namedArguments.length,
namedArguments,
typeArguments.length);
- List<DartType> dartTypeArguments = _getTypeArguments(typeArguments);
+ List<DartType>? dartTypeArguments = _getTypeArguments(typeArguments);
// Invocation of a local function. No need for dynamic use, but
// we need to track the type arguments.
impactBuilder.registerStaticUse(StaticUse.closureCall(
@@ -559,7 +562,7 @@
List<ir.DartType> typeArguments) {
Selector selector = elementMap.getInvocationSelector(
name, positionalArguments, namedArguments, typeArguments.length);
- List<DartType> dartTypeArguments = _getTypeArguments(typeArguments);
+ List<DartType>? dartTypeArguments = _getTypeArguments(typeArguments);
impactBuilder.registerDynamicUse(DynamicUse(selector,
_computeReceiverConstraint(receiverType, relation), dartTypeArguments));
}
@@ -574,7 +577,7 @@
positionalArguments + namedArguments.length,
namedArguments,
typeArguments.length);
- List<DartType> dartTypeArguments = _getTypeArguments(typeArguments);
+ List<DartType>? dartTypeArguments = _getTypeArguments(typeArguments);
impactBuilder.registerDynamicUse(DynamicUse(
callStructure.callSelector,
_computeReceiverConstraint(receiverType, ClassRelation.subtype),
@@ -589,7 +592,7 @@
int positionalArguments,
List<String> namedArguments,
List<ir.DartType> typeArguments) {
- List<DartType> dartTypeArguments = _getTypeArguments(typeArguments);
+ List<DartType>? dartTypeArguments = _getTypeArguments(typeArguments);
impactBuilder.registerDynamicUse(DynamicUse(
elementMap.getInvocationSelector(target.name, positionalArguments,
namedArguments, typeArguments.length),
@@ -635,9 +638,9 @@
@override
void registerRuntimeTypeUse(RuntimeTypeUseKind kind, ir.DartType receiverType,
- ir.DartType argumentType) {
+ ir.DartType? argumentType) {
DartType receiverDartType = elementMap.getDartType(receiverType);
- DartType argumentDartType =
+ DartType? argumentDartType =
argumentType == null ? null : elementMap.getDartType(argumentType);
// Enable runtime type support if we discover a getter called
@@ -649,7 +652,7 @@
}
@override
- void registerAssert({bool withMessage}) {
+ void registerAssert({required bool withMessage}) {
registerBackendImpact(withMessage
? _impacts.assertWithMessage
: _impacts.assertWithoutMessage);
@@ -660,7 +663,8 @@
ir.FunctionType expressionType, List<ir.DartType> typeArguments) {
// TODO(johnniwinther): Track which arities are used in instantiation.
final instantiation = GenericInstantiation(
- elementMap.getDartType(expressionType).withoutNullability,
+ elementMap.getDartType(expressionType).withoutNullability
+ as FunctionType,
typeArguments.map(elementMap.getDartType).toList());
registerBackendImpact(
_impacts.getGenericInstantiation(instantiation.typeArguments.length));
@@ -674,7 +678,7 @@
}
@override
- void registerLocalFunction(ir.TreeNode node) {
+ void registerLocalFunction(covariant ir.LocalFunction node) {
Local function = elementMap.getLocalFunction(node);
impactBuilder.registerStaticUse(StaticUse.closure(function));
registerBackendImpact(_impacts.closure);
@@ -727,7 +731,7 @@
@override
void registerSyncForIn(ir.DartType iterableType, ir.DartType iteratorType,
ClassRelation iteratorClassRelation) {
- Object receiverConstraint =
+ Object? receiverConstraint =
_computeReceiverConstraint(iteratorType, iteratorClassRelation);
registerBackendImpact(_impacts.syncForIn);
impactBuilder.registerDynamicUse(
@@ -741,7 +745,7 @@
@override
void registerAsyncForIn(ir.DartType iterableType, ir.DartType iteratorType,
ClassRelation iteratorClassRelation) {
- Object receiverConstraint =
+ Object? receiverConstraint =
_computeReceiverConstraint(iteratorType, iteratorClassRelation);
registerBackendImpact(_impacts.asyncForIn);
impactBuilder.registerDynamicUse(
@@ -770,8 +774,8 @@
}
@override
- void registerTypeLiteral(ir.DartType irType, ir.LibraryDependency import) {
- ImportEntity deferredImport = elementMap.getImport(import);
+ void registerTypeLiteral(ir.DartType irType, ir.LibraryDependency? import) {
+ ImportEntity? deferredImport = elementMap.getImport(import);
DartType type = elementMap.getDartType(irType);
impactBuilder.registerTypeUse(TypeUse.typeLiteral(type, deferredImport));
_customElementsResolutionAnalysis.registerTypeLiteral(type);
@@ -825,10 +829,10 @@
// Treat symbol constants as if Symbol doesn't override `==`.
return false;
}
- ClassEntity cls = type.element;
+ ClassEntity? cls = type.element;
while (cls != null) {
MemberEntity member =
- elementMap.elementEnvironment.lookupClassMember(cls, '==');
+ elementMap.elementEnvironment.lookupClassMember(cls, '==')!;
if (member.isAbstract) {
cls = elementMap.elementEnvironment.getSuperClass(cls);
} else {
@@ -842,7 +846,7 @@
for (ir.SwitchCase switchCase in node.cases) {
for (ir.Expression expression in switchCase.expressions) {
ConstantValue value =
- elementMap.getConstantValue(staticTypeContext, expression);
+ elementMap.getConstantValue(staticTypeContext, expression)!;
DartType type = value.getType(elementMap.commonElements);
if (type == commonElements.doubleType) {
reporter.reportErrorMessage(
@@ -854,7 +858,7 @@
MessageKind.SWITCH_CASE_FORBIDDEN, {'type': "Function"});
} else if (value is ObjectConstantValue &&
type != commonElements.typeLiteralType &&
- overridesEquals(type)) {
+ overridesEquals(type as InterfaceType)) {
reporter.reportErrorMessage(
computeSourceSpanFromTreeNode(expression),
MessageKind.SWITCH_CASE_VALUE_OVERRIDES_EQUALS,
diff --git a/pkg/compiler/lib/src/universe/use.dart b/pkg/compiler/lib/src/universe/use.dart
index b4ebace..fb60943 100644
--- a/pkg/compiler/lib/src/universe/use.dart
+++ b/pkg/compiler/lib/src/universe/use.dart
@@ -561,7 +561,7 @@
ConstructorEntity element,
CallStructure callStructure,
InterfaceType type,
- ImportEntity deferredImport) {
+ ImportEntity? deferredImport) {
assert(
(type as dynamic) != null, // TODO(48820): remove when sound
failedAt(element, "No type provided for constructor invocation."));
@@ -583,7 +583,7 @@
ConstructorEntity element,
CallStructure callStructure,
InterfaceType type,
- ImportEntity deferredImport) {
+ ImportEntity? deferredImport) {
assert(
(type as dynamic) != null, // TODO(48820): remove when sound
failedAt(element, "No type provided for constructor invocation."));
@@ -645,7 +645,7 @@
/// An invocation of a local function [element] with the provided
/// [callStructure] and [typeArguments].
factory StaticUse.closureCall(Local element, CallStructure callStructure,
- List<DartType> typeArguments) {
+ List<DartType>? typeArguments) {
StaticUse staticUse = StaticUse.internal(
element, StaticUseKind.CLOSURE_CALL,
callStructure: callStructure, typeArguments: typeArguments);
@@ -836,7 +836,7 @@
}
/// [type] used as a type literal, like `foo() => T;`.
- factory TypeUse.typeLiteral(DartType type, ImportEntity deferredImport) {
+ factory TypeUse.typeLiteral(DartType type, ImportEntity? deferredImport) {
return TypeUse.internal(type, TypeUseKind.TYPE_LITERAL, deferredImport);
}
@@ -847,7 +847,7 @@
/// [type] used in a constant instantiation, like `const T();`.
factory TypeUse.constInstantiation(
- InterfaceType type, ImportEntity deferredImport) {
+ InterfaceType type, ImportEntity? deferredImport) {
return TypeUse.internal(
type, TypeUseKind.CONST_INSTANTIATION, deferredImport);
}
diff --git a/pkg/dds/lib/src/dap/isolate_manager.dart b/pkg/dds/lib/src/dap/isolate_manager.dart
index 16eaf74..eca3678 100644
--- a/pkg/dds/lib/src/dap/isolate_manager.dart
+++ b/pkg/dds/lib/src/dap/isolate_manager.dart
@@ -542,7 +542,7 @@
final isolateId = isolate.id!;
final uriStrings = uris.map((uri) => uri.toString()).toList();
final res = await _adapter.vmService
- ?.lookupResolvedPackageUris(isolateId, uriStrings);
+ ?.lookupResolvedPackageUris(isolateId, uriStrings, local: true);
return res?.uris
?.cast<String?>()
@@ -648,6 +648,10 @@
Uri.parse(uri).toFilePath(),
);
+ if (vmUri == null) {
+ return;
+ }
+
final vmBp = await service.addBreakpointWithScriptUri(
isolateId, vmUri.toString(), bp.line,
column: bp.column);
@@ -826,7 +830,15 @@
/// (which they may have navigated to via the Analysis Server) we generate a
/// valid URI that the VM would create a breakpoint for.
Future<Uri?> resolvePathToUri(String filePath) async {
- // We don't currently need to call lookupPackageUris because the VM can
+ var google3Path = _convertPathToGoogle3Uri(filePath);
+ if (google3Path != null) {
+ var result = await _manager._adapter.vmService
+ ?.lookupPackageUris(isolate.id!, [google3Path.toString()]);
+ var uriStr = result?.uris?.first;
+ return uriStr != null ? Uri.parse(uriStr) : null;
+ }
+
+ // We don't need to call lookupPackageUris in non-google3 because the VM can
// handle incoming file:/// URIs for packages, and also the org-dartlang-sdk
// URIs directly for SDK sources (we do not need to convert to 'dart:'),
// however this method is Future-returning in case this changes in future
@@ -965,6 +977,21 @@
return null;
}
+ Uri? _convertPathToGoogle3Uri(String input) {
+ const search = '/google3/';
+ if (input.startsWith('/google') && input.contains(search)) {
+ var idx = input.indexOf(search);
+ var remainingPath = input.substring(idx + search.length);
+ return Uri(
+ scheme: 'google3',
+ host: '',
+ path: remainingPath,
+ );
+ }
+
+ return null;
+ }
+
/// Converts a file path inside the current SDK root into a URI in the form
/// org-dartlang-sdk:///sdk/lib/collection/hash_set.dart.
Uri? _convertPathToOrgDartlangSdk(String input) {
diff --git a/pkg/dds/lib/src/package_uri_converter.dart b/pkg/dds/lib/src/package_uri_converter.dart
index 0f9b64a..a8ef863 100644
--- a/pkg/dds/lib/src/package_uri_converter.dart
+++ b/pkg/dds/lib/src/package_uri_converter.dart
@@ -28,10 +28,13 @@
if (converter != null && useLocalResolver) {
final vmUris = result['uris'];
- final localUris = uris.map((x) => converter(x as String)).toList();
+ final localPaths = uris.map((x) => converter(x as String)).toList();
final resultUris = <String?>[
- for (var i = 0; i < vmUris.length; i++) localUris[i] ?? vmUris[i],
+ for (var i = 0; i < vmUris.length; i++)
+ localPaths[i] != null
+ ? Uri.file(localPaths[i]!).toString()
+ : vmUris[i],
];
return <String, dynamic>{
diff --git a/pkg/dds/test/uri_converter_test.dart b/pkg/dds/test/uri_converter_test.dart
index 2d82ea6..2364961 100644
--- a/pkg/dds/test/uri_converter_test.dart
+++ b/pkg/dds/test/uri_converter_test.dart
@@ -34,7 +34,7 @@
dds = await DartDevelopmentService.startDartDevelopmentService(
remoteVmServiceUri,
uriConverter: (uri) => uri == 'package:test/has_local.dart'
- ? 'file:///has_local.dart'
+ ? '/home/has_local.dart'
: null,
);
serviceUri = dds!.wsUri!;
@@ -68,7 +68,7 @@
.lookupResolvedPackageUris(isolate.id!, unresolvedUris, local: true);
expect(result.uris?[0], 'org-dartlang-sdk:///sdk/lib/io/io.dart');
- expect(result.uris?[1], 'file:///has_local.dart');
+ expect(result.uris?[1], 'file:///home/has_local.dart');
expect(result.uris?[2], null);
},
timeout: Timeout.none,
diff --git a/pkg/meta/lib/meta.dart b/pkg/meta/lib/meta.dart
index 7a1ce9f..fb15a7d 100644
--- a/pkg/meta/lib/meta.dart
+++ b/pkg/meta/lib/meta.dart
@@ -458,6 +458,12 @@
const _MustBeOverridden();
}
+@Target({
+ TargetKind.field,
+ TargetKind.getter,
+ TargetKind.method,
+ TargetKind.setter,
+})
class _MustCallSuper {
const _MustCallSuper();
}
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index bc4ebde..e7f9172 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -5722,6 +5722,7 @@
__ TryAllocate(cls, compiler->intrinsic_slow_path_label(),
compiler::Assembler::kFarJump, result, temp);
} else {
+ RELEASE_ASSERT(instruction->CanTriggerGC());
auto slow_path = new BoxAllocationSlowPath(instruction, cls, result);
compiler->AddSlowPathCode(slow_path);
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 2aa863f..af9f8b1 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -445,7 +445,7 @@
M(DebugStepCheck, _) \
M(RecordCoverage, kNoGC) \
M(LoadIndexed, kNoGC) \
- M(LoadCodeUnits, kNoGC) \
+ M(LoadCodeUnits, _) \
M(StoreIndexed, kNoGC) \
M(StoreField, _) \
M(LoadStaticField, _) \
@@ -5989,6 +5989,10 @@
virtual bool HasUnknownSideEffects() const { return false; }
+ virtual bool CanTriggerGC() const {
+ return !can_pack_into_smi() && (representation() == kTagged);
+ }
+
private:
const intptr_t class_id_;
const TokenPosition token_pos_;
@@ -6869,6 +6873,10 @@
virtual bool AllowsCSE() const { return slot_.is_immutable(); }
+ virtual bool CanTriggerGC() const {
+ return calls_initializer() || IsPotentialUnboxedDartFieldLoad();
+ }
+
virtual bool AttributesEqual(const Instruction& other) const;
PRINT_OPERANDS_TO_SUPPORT
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index a12f853..44f2547 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -1133,30 +1133,17 @@
FLAG_branch_coverage);
}
-// Generated code must match the host architecture and ABI.
-#if defined(TARGET_ARCH_ARM)
-#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
- buffer.AddString(" arm-ios");
-#else
- buffer.AddString(" arm-eabi");
-#endif
- buffer.AddString(TargetCPUFeatures::hardfp_supported() ? " hardfp"
- : " softfp");
-#elif defined(TARGET_ARCH_ARM64)
-#if defined(DART_TARGET_OS_FUCHSIA)
- // See signal handler cheat in Assembler::EnterFrame.
- buffer.AddString(" arm64-fuchsia");
-#else
- buffer.AddString(" arm64-sysv");
-#endif
-#elif defined(TARGET_ARCH_IA32)
+ // Generated code must match the host architecture and ABI. We check the
+ // strong condition of matching on operating system so that
+ // Platform.isAndroid etc can be compile-time constants.
+#if defined(TARGET_ARCH_IA32)
buffer.AddString(" ia32");
#elif defined(TARGET_ARCH_X64)
-#if defined(DART_TARGET_OS_WINDOWS)
- buffer.AddString(" x64-win");
-#else
- buffer.AddString(" x64-sysv");
-#endif
+ buffer.AddString(" x64");
+#elif defined(TARGET_ARCH_ARM)
+ buffer.AddString(" arm");
+#elif defined(TARGET_ARCH_ARM64)
+ buffer.AddString(" arm64");
#elif defined(TARGET_ARCH_RISCV32)
buffer.AddString(" riscv32");
#elif defined(TARGET_ARCH_RISCV64)
@@ -1164,6 +1151,25 @@
#else
#error What architecture?
#endif
+
+#if defined(DART_TARGET_OS_ANDROID)
+ buffer.AddString(" android");
+#elif defined(DART_TARGET_OS_FUCHSIA)
+ buffer.AddString(" fuchsia");
+#elif defined(DART_TARGET_OS_MACOS)
+#if defined(DART_TARGET_OS_MACOS_IOS)
+ buffer.AddString(" ios");
+#else
+ buffer.AddString(" macos");
+#endif
+#elif defined(DART_TARGET_OS_LINUX)
+ buffer.AddString(" linux");
+#elif defined(DART_TARGET_OS_WINDOWS)
+ buffer.AddString(" windows");
+#else
+#error What operating system?
+#endif
+
#if defined(DART_COMPRESSED_POINTERS)
buffer.AddString(" compressed-pointers");
#else
diff --git a/tools/VERSION b/tools/VERSION
index 4190dd2..1aac611 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 19
PATCH 0
-PRERELEASE 44
+PRERELEASE 45
PRERELEASE_PATCH 0
\ No newline at end of file