Version 2.17.0-125.0.dev
Merge commit '0921dceaef7f205bd24dc214abd2509ec95e0cdf' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/client_configuration.dart b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
index 83b987b..c7497dd 100644
--- a/pkg/analysis_server/lib/src/lsp/client_configuration.dart
+++ b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
@@ -165,6 +165,18 @@
_fallback?.enableSdkFormatter ??
true;
+ /// Whether to include Snippets in code completion results.
+ bool get enableSnippets =>
+ // TODO(dantup): Change this setting to `enableSnippets`
+ // and default to `true`
+ // and remove `initializeWithSnippetSupportAndPreviewFlag` from tests
+ // once all snippets are implemented and VS Code has shipped a
+ // version that maps `enableServerSnippets` to `enableSnippets` in
+ // middleware to avoid dupes.
+ _settings['previewEnableSnippets'] as bool? ??
+ _fallback?.enableSnippets ??
+ false;
+
/// The line length used when formatting documents.
///
/// If null, the formatters default will be used.
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 6338b96..a91f9fd 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -21,7 +21,9 @@
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:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/ast/ast.dart' as ast;
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/services/available_declarations.dart';
@@ -178,6 +180,31 @@
String _createImportedSymbolKey(String name, Uri declaringUri) =>
'$name/$declaringUri';
+ Future<List<CompletionItem>> _getDartSnippetItems({
+ required LspClientCapabilities clientCapabilities,
+ required ResolvedUnitResult unit,
+ required int offset,
+ required LineInfo lineInfo,
+ }) async {
+ final request = DartSnippetRequest(
+ unit: unit,
+ offset: offset,
+ );
+ final snippetManager = DartSnippetManager();
+ final snippets = await snippetManager.computeSnippets(request);
+
+ return snippets
+ .map((snippet) => snippetToCompletionItem(
+ server,
+ clientCapabilities,
+ unit.path,
+ lineInfo,
+ toPosition(lineInfo.getLocation(offset)),
+ snippet,
+ ))
+ .toList();
+ }
+
Future<ErrorOr<CompletionList>> _getPluginResults(
LspClientCapabilities capabilities,
LineInfo lineInfo,
@@ -420,6 +447,18 @@
});
}
+ // Add in any snippets.
+ final snippetsEnabled =
+ server.clientConfiguration.forResource(unit.path).enableSnippets;
+ if (capabilities.completionSnippets && snippetsEnabled) {
+ results.addAll(await _getDartSnippetItems(
+ clientCapabilities: capabilities,
+ unit: unit,
+ offset: offset,
+ lineInfo: unit.lineInfo,
+ ));
+ }
+
// Perform fuzzy matching based on the identifier in front of the caret to
// reduce the size of the payload.
final fuzzyPattern = completionRequest.targetPrefix;
@@ -435,6 +474,8 @@
CompletionList(isIncomplete: false, items: matchingResults));
} on AbortCompletion {
return success(CompletionList(isIncomplete: false, items: []));
+ } on InconsistentAnalysisException {
+ return success(CompletionList(isIncomplete: false, items: []));
}
},
);
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index 817937e..fec47e8 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -22,6 +22,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:analyzer/dart/analysis/results.dart' as server;
import 'package:analyzer/error/error.dart' as server;
import 'package:analyzer/source/line_info.dart' as server;
@@ -890,6 +891,35 @@
);
}
+/// Creates a SnippetTextEdit for a set of edits using Linked Edit Groups.
+///
+/// Edit groups offsets are based on the entire content being modified after all
+/// edits, so [editOffset] must to take into account both the offset of the edit
+/// _and_ any delta from edits prior to this one in the file.
+///
+/// [selectionOffset] is also absolute and assumes [edit.replacement] will be
+/// inserted at [editOffset].
+lsp.SnippetTextEdit snippetTextEditFromEditGroups(
+ String filePath,
+ server.LineInfo lineInfo,
+ server.SourceEdit edit, {
+ required List<server.LinkedEditGroup> editGroups,
+ required int editOffset,
+ required int? selectionOffset,
+}) {
+ return lsp.SnippetTextEdit(
+ insertTextFormat: lsp.InsertTextFormat.Snippet,
+ range: toRange(lineInfo, edit.offset, edit.length),
+ newText: buildSnippetStringForEditGroups(
+ edit.replacement,
+ filePath: filePath,
+ editGroups: editGroups,
+ editOffset: editOffset,
+ selectionOffset: selectionOffset,
+ ),
+ );
+}
+
/// Creates a SnippetTextEdit for an edit with a selection placeholder.
///
/// [selectionOffset] is relative to (and therefore must be within) the edit.
@@ -909,6 +939,76 @@
);
}
+lsp.CompletionItem snippetToCompletionItem(
+ lsp.LspAnalysisServer server,
+ LspClientCapabilities capabilities,
+ String file,
+ LineInfo lineInfo,
+ Position position,
+ Snippet snippet,
+) {
+ assert(capabilities.completionSnippets);
+
+ final formats = capabilities.completionDocumentationFormats;
+ final documentation = snippet.documentation;
+ final supportsAsIsInsertMode =
+ capabilities.completionInsertTextModes.contains(InsertTextMode.asIs);
+ final changes = snippet.change;
+
+ // We must only get one change for this file to be able to apply snippets.
+ final thisFilesChange = changes.edits.singleWhere((e) => e.file == file);
+ final otherFilesChanges = changes.edits.where((e) => e.file != file).toList();
+
+ // If this completion involves editing other files, we'll need to build
+ // a command that the client will call to apply those edits later, because
+ // LSP Completions can only provide simple edits for the current file.
+ Command? command;
+ if (otherFilesChanges.isNotEmpty) {
+ final workspaceEdit = createPlainWorkspaceEdit(server, otherFilesChanges);
+ command = Command(
+ title: 'Add import',
+ command: Commands.sendWorkspaceEdit,
+ arguments: [workspaceEdit]);
+ }
+
+ /// Convert the changes to TextEdits using snippet tokens for linked edit
+ /// groups.
+ final mainFileEdits = toSnippetTextEdits(
+ file,
+ thisFilesChange,
+ changes.linkedEditGroups,
+ lineInfo,
+ selectionOffset:
+ changes.selection?.file == file ? changes.selection?.offset : null,
+ );
+
+ // For LSP, we need to provide the main edit and other edits separately. The
+ // main edit must include the location that completion was invoked. If we
+ // fail to find exactly one, this is an error.
+ final mainEdit = mainFileEdits
+ .singleWhere((edit) => edit.range.start.line == position.line);
+ final nonMainEdits = mainFileEdits.where((edit) => edit != mainEdit).toList();
+
+ return lsp.CompletionItem(
+ label: snippet.label,
+ filterText: snippet.prefix,
+ kind: lsp.CompletionItemKind.Snippet,
+ command: command,
+ documentation: documentation != null
+ ? asStringOrMarkupContent(formats, documentation)
+ : null,
+ // Force snippets to be sorted at the bottom of the list.
+ // TODO(dantup): Consider if we can rank these better. Client-side
+ // snippets have always been forced to the bottom partly because they
+ // show up in more places than wanted.
+ sortText: 'zzz${snippet.prefix}',
+ insertTextFormat: lsp.InsertTextFormat.Snippet,
+ insertTextMode: supportsAsIsInsertMode ? InsertTextMode.asIs : null,
+ textEdit: Either2<TextEdit, InsertReplaceEdit>.t1(mainEdit),
+ additionalTextEdits: nonMainEdits,
+ );
+}
+
lsp.CompletionItemKind? suggestionKindToCompletionItemKind(
Set<lsp.CompletionItemKind> supportedCompletionKinds,
server.CompletionSuggestionKind kind,
@@ -1372,6 +1472,36 @@
);
}
+List<lsp.SnippetTextEdit> toSnippetTextEdits(
+ String filePath,
+ server.SourceFileEdit change,
+ List<server.LinkedEditGroup> editGroups,
+ LineInfo lineInfo, {
+ required int? selectionOffset,
+}) {
+ final snippetEdits = <lsp.SnippetTextEdit>[];
+
+ // Edit groups offsets are based on after the edits are applied, so we
+ // must track the offset delta to ensure we track the offset within the
+ // edits. This also requires the edits are sorted earliest-to-latest.
+ var offsetDelta = 0;
+ change.edits.sortBy<num>((edit) => edit.offset);
+ for (final edit in change.edits) {
+ snippetEdits.add(snippetTextEditFromEditGroups(
+ filePath,
+ lineInfo,
+ edit,
+ editGroups: editGroups,
+ editOffset: edit.offset + offsetDelta,
+ selectionOffset: selectionOffset,
+ ));
+
+ offsetDelta += edit.replacement.length - edit.length;
+ }
+
+ return snippetEdits;
+}
+
ErrorOr<server.SourceRange> toSourceRange(
server.LineInfo lineInfo, Range range) {
// If there is a range, convert to offsets because that's what
diff --git a/pkg/analysis_server/lib/src/lsp/snippets.dart b/pkg/analysis_server/lib/src/lsp/snippets.dart
index 6f59d8e..e88dcaa 100644
--- a/pkg/analysis_server/lib/src/lsp/snippets.dart
+++ b/pkg/analysis_server/lib/src/lsp/snippets.dart
@@ -4,32 +4,113 @@
import 'dart:math' as math;
+import 'package:analysis_server/src/protocol_server.dart' as server
+ hide AnalysisError;
import 'package:collection/collection.dart';
+/// Builds an LSP snippet string using the supplied edit groups.
+///
+/// [editGroups] are provided as absolute positions, where the edit will be
+/// made starting at [editOffset].
+///
+/// [selectionOffset] is also absolute and assumes [text] will be
+/// inserted at [editOffset].
+String buildSnippetStringForEditGroups(
+ String text, {
+ required String filePath,
+ required List<server.LinkedEditGroup> editGroups,
+ required int editOffset,
+ int? selectionOffset,
+}) =>
+ _buildSnippetString(
+ text,
+ filePath: filePath,
+ editGroups: editGroups,
+ editGroupsOffset: editOffset,
+ selectionOffset:
+ selectionOffset != null ? selectionOffset - editOffset : null,
+ );
+
/// Builds an LSP snippet string with supplied ranges as tab stops.
///
/// [tabStopOffsetLengthPairs] are relative to the supplied text.
String buildSnippetStringWithTabStops(
String text,
List<int>? tabStopOffsetLengthPairs,
-) {
+) =>
+ _buildSnippetString(
+ text,
+ filePath: null,
+ tabStopOffsetLengthPairs: tabStopOffsetLengthPairs,
+ );
+
+/// Builds an LSP snippet string with supplied ranges as tab stops.
+///
+/// [tabStopOffsetLengthPairs] are relative to the supplied text.
+///
+/// [selectionOffset]/[selectionLength] form a tab stop that is always "number 0"
+/// which is the final tab stop.
+///
+/// [editGroups] are provided as absolute positions, where [text] is known to
+/// start at [editGroupsOffset] in the final document.
+String _buildSnippetString(
+ String text, {
+ required String? filePath,
+ List<int>? tabStopOffsetLengthPairs,
+ int? selectionOffset,
+ int? selectionLength,
+ List<server.LinkedEditGroup>? editGroups,
+ int editGroupsOffset = 0,
+}) {
tabStopOffsetLengthPairs ??= const [];
+ editGroups ??= const [];
assert(tabStopOffsetLengthPairs.length % 2 == 0);
- // Convert selection/tab stops/edit groups all into a common format
- // (`SnippetPlaceholder`) so they can be handled in a single pass through
+ /// Helper to create a [SnippetPlaceholder] for each position in a linked
+ /// edit group.
+ Iterable<SnippetPlaceholder> convertEditGroup(
+ int index,
+ server.LinkedEditGroup editGroup,
+ ) {
+ final validPositions = editGroup.positions.where((p) => p.file == filePath);
+ // Create a placeholder for each position in the group.
+ return validPositions.map(
+ (position) => SnippetPlaceholder(
+ // Make the position relative to the supplied text.
+ position.offset - editGroupsOffset,
+ editGroup.length,
+ suggestions: editGroup.suggestions
+ .map((suggestion) => suggestion.value)
+ .toList(),
+ // Use the index as an ID to keep all related positions together (so
+ // the remain "linked").
+ linkedGroupId: index,
+ ),
+ );
+ }
+
+ // Convert selection/tab stops/edit groups all into the same format
+ // (`_SnippetPlaceholder`) so they can be handled in a single pass through
// the text.
final placeholders = [
+ // Selection.
+ if (selectionOffset != null)
+ SnippetPlaceholder(selectionOffset, selectionLength ?? 0, isFinal: true),
+
// Tab stops.
for (var i = 0; i < tabStopOffsetLengthPairs.length - 1; i += 2)
SnippetPlaceholder(
tabStopOffsetLengthPairs[i],
tabStopOffsetLengthPairs[i + 1],
- // If there's only a single tab stop, mark
+ // If there's only a single tab stop (and no selection/editGroups), mark
// it as the final stop so it exit "snippet mode" when tabbed to.
- isFinal: tabStopOffsetLengthPairs.length == 2,
+ isFinal: selectionOffset == null &&
+ editGroups.isEmpty &&
+ tabStopOffsetLengthPairs.length == 2,
),
- // TODO(dantup): Add edit group/selection support.
+
+ // Linked edit groups.
+ ...editGroups.expandIndexed(convertEditGroup),
];
// Remove any groups outside of the range (it's possible the edit groups apply
diff --git a/pkg/analysis_server/lib/src/services/correction/assist.dart b/pkg/analysis_server/lib/src/services/correction/assist.dart
index 3f8be5c..0e0b1c9 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist.dart
@@ -56,6 +56,11 @@
30,
'Assign value to new local variable',
);
+ static const CONVERT_CLASS_TO_ENUM = AssistKind(
+ 'dart.assist.convert.classToEnum',
+ 30,
+ 'Convert class to an enum',
+ );
static const CONVERT_CLASS_TO_MIXIN = AssistKind(
'dart.assist.convert.classToMixin',
30,
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
index 3cbb3f8..70835d5 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -12,6 +12,7 @@
import 'package:analysis_server/src/services/correction/dart/add_type_annotation.dart';
import 'package:analysis_server/src/services/correction/dart/assign_to_local_variable.dart';
import 'package:analysis_server/src/services/correction/dart/convert_add_all_to_spread.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_class_to_enum.dart';
import 'package:analysis_server/src/services/correction/dart/convert_class_to_mixin.dart';
import 'package:analysis_server/src/services/correction/dart/convert_conditional_expression_to_if_element.dart';
import 'package:analysis_server/src/services/correction/dart/convert_documentation_into_block.dart';
@@ -91,6 +92,7 @@
AddTypeAnnotation.newInstanceBulkFixable,
AssignToLocalVariable.newInstance,
ConvertAddAllToSpread.newInstance,
+ ConvertClassToEnum.newInstance,
ConvertClassToMixin.newInstance,
ConvertConditionalExpressionToIfElement.newInstance,
ConvertDocumentationIntoBlock.newInstance,
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_class_to_enum.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_class_to_enum.dart
new file mode 100644
index 0000000..978a6f5
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_class_to_enum.dart
@@ -0,0 +1,768 @@
+// 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/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analysis_server/src/utilities/extensions/range_factory.dart';
+import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/constant/value.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.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:analyzer_plugin/utilities/range_factory.dart';
+import 'package:collection/collection.dart';
+
+class ConvertClassToEnum extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.CONVERT_CLASS_TO_ENUM;
+
+ @override
+ Future<void> compute(ChangeBuilder builder) async {
+ if (!libraryElement.featureSet.isEnabled(Feature.enhanced_enums)) {
+ // If the library doesn't support enhanced_enums then the class can't be
+ // converted.
+ return;
+ }
+ if (libraryElement.units.length > 1) {
+ // If the library has any part files, then the class can't be converted
+ // because we don't currently have a performant way to access the ASTs for
+ // the parts to check for invocations of the constructors or subclasses of
+ // the class.
+ return;
+ }
+ var node = this.node;
+ if (node is! SimpleIdentifier) {
+ return;
+ }
+ var parent = node.parent;
+ if (parent is ClassDeclaration && parent.name == node) {
+ var description = _EnumDescription.fromClass(parent);
+ if (description != null) {
+ await builder.addDartFileEdit(file, (builder) {
+ description.applyChanges(builder, utils);
+ });
+ }
+ }
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
+ static ConvertClassToEnum newInstance() => ConvertClassToEnum();
+}
+
+/// A superclass for the [_EnumVisitor] and [_NonEnumVisitor].
+class _BaseVisitor extends RecursiveAstVisitor<void> {
+ /// The element representing the enum declaration that's being visited.
+ final ClassElement classElement;
+
+ _BaseVisitor(this.classElement);
+
+ /// Return `true` if the given [node] is an invocation of a constructor from
+ /// the class being converted.
+ bool invokesEnumConstructor(InstanceCreationExpression node) {
+ var constructorElement = node.constructorName.staticElement;
+ return constructorElement?.enclosingElement == classElement;
+ }
+}
+
+/// An exception thrown by the visitors if a condition is found that prevents
+/// the class from being converted.
+class _CannotConvertException implements Exception {
+ final String message;
+
+ _CannotConvertException(this.message);
+}
+
+/// A representation of a static field in the class being converted that will be
+/// replaced by an enum constant.
+class _ConstantField extends _Field {
+ /// The element representing the constructor used to initialize the field.
+ ConstructorElement constructorElement;
+
+ /// The invocation of the constructor.
+ final InstanceCreationExpression instanceCreation;
+
+ /// The value of the index field.
+ final int indexValue;
+
+ _ConstantField(
+ FieldElement element,
+ VariableDeclaration declaration,
+ VariableDeclarationList declarationList,
+ FieldDeclaration fieldDeclaration,
+ this.instanceCreation,
+ this.constructorElement,
+ this.indexValue)
+ : super(element, declaration, declarationList, fieldDeclaration);
+}
+
+/// Information about a single constructor in the class being converted.
+class _Constructor {
+ /// The declaration of the constructor.
+ final ConstructorDeclaration declaration;
+
+ /// The element representing the constructor.
+ final ConstructorElement element;
+
+ _Constructor(this.declaration, this.element);
+}
+
+/// Information about the constructors in the class being converted.
+class _Constructors {
+ /// A map from elements to constructors.
+ final Map<ConstructorElement, _Constructor> byElement = {};
+
+ _Constructors();
+
+ /// Return the constructors in this collection.
+ Iterable<_Constructor> get constructors => byElement.values;
+
+ /// Add the given [constructor] to this collection.
+ void add(_Constructor constructor) {
+ byElement[constructor.element] = constructor;
+ }
+
+ /// Return the constructor with the given [element].
+ _Constructor? forElement(ConstructorElement element) {
+ return byElement[element];
+ }
+}
+
+/// A description of how to convert the class to an enum.
+class _EnumDescription {
+ /// The class declaration being converted.
+ final ClassDeclaration classDeclaration;
+
+ /// A map from constructor declarations to information about the parameter
+ /// corresponding to the index field. The map is `null` if there is no index
+ /// field.
+ final Map<_Constructor, _Parameter>? constructorMap;
+
+ /// A list of the declarations to be converted into enum constants.
+ final _Fields fields;
+
+ /// A list of the indexes of members that need to be deleted.
+ final List<int> membersToDelete;
+
+ _EnumDescription({
+ required this.classDeclaration,
+ required this.constructorMap,
+ required this.fields,
+ required this.membersToDelete,
+ });
+
+ /// Return the offset immediately following the opening brace for the class
+ /// body.
+ int get bodyOffset => classDeclaration.leftBracket.end;
+
+ /// Use the [builder] and correction [utils] to apply the change necessary to
+ /// convert the class to an enum.
+ void applyChanges(DartFileEditBuilder builder, CorrectionUtils utils) {
+ // Replace the keyword.
+ builder.addSimpleReplacement(
+ range.token(classDeclaration.classKeyword), 'enum');
+
+ // Remove the extends clause if there is one.
+ final extendsClause = classDeclaration.extendsClause;
+ if (extendsClause != null) {
+ var followingToken = extendsClause.endToken.next!;
+ builder.addDeletion(range.startStart(extendsClause, followingToken));
+ }
+
+ // Compute the declarations of the enum constants and delete the fields
+ // being converted.
+ var members = classDeclaration.members;
+ var indent = utils.getIndent(1);
+ var constantsBuffer = StringBuffer();
+ var fieldsToConvert = fields.fieldsToConvert;
+ fieldsToConvert
+ .sort((first, second) => first.indexValue.compareTo(second.indexValue));
+ for (var field in fieldsToConvert) {
+ // Compute the declaration of the corresponding enum constant.
+ if (constantsBuffer.isNotEmpty) {
+ constantsBuffer.write(',\n$indent');
+ }
+ constantsBuffer.write(field.name);
+ var invocation = field.instanceCreation;
+ var constructorNameNode = invocation.constructorName;
+ var invokedConstructorElement = field.constructorElement;
+ var invokedConstructor = constructorMap?.keys.firstWhere(
+ (constructor) => constructor.element == invokedConstructorElement);
+ var parameterData = constructorMap?[invokedConstructor];
+ var typeArguments = constructorNameNode.type.typeArguments;
+ if (typeArguments != null) {
+ constantsBuffer.write(utils.getNodeText(typeArguments));
+ }
+ var constructorName = constructorNameNode.name?.name;
+ if (constructorName != null) {
+ constantsBuffer.write('.$constructorName');
+ }
+ var argumentList = invocation.argumentList;
+ var arguments = argumentList.arguments;
+ var argumentCount = arguments.length - (parameterData == null ? 0 : 1);
+ if (argumentCount == 0) {
+ if (typeArguments != null || constructorName != null) {
+ constantsBuffer.write('()');
+ }
+ } else if (parameterData == null) {
+ constantsBuffer.write(utils.getNodeText(argumentList));
+ } else {
+ constantsBuffer.write('(');
+ var index = parameterData.index;
+ var last = arguments.length - 1;
+ if (index == 0) {
+ var offset = arguments[1].offset;
+ var length = arguments[last].end - offset;
+ constantsBuffer.write(utils.getText(offset, length));
+ } else if (index == last) {
+ var offset = arguments[0].offset;
+ int length;
+ if (arguments[last].endToken.next?.type == TokenType.COMMA) {
+ length = arguments[last].offset - offset;
+ } else {
+ length = arguments[last - 1].end - offset;
+ }
+ constantsBuffer.write(utils.getText(offset, length));
+ } else {
+ var offset = arguments[0].offset;
+ var length = arguments[index].offset - offset;
+ constantsBuffer.write(utils.getText(offset, length));
+
+ offset = arguments[index + 1].offset;
+ length = argumentList.endToken.offset - offset;
+ constantsBuffer.write(utils.getText(offset, length));
+ }
+ constantsBuffer.write(')');
+ }
+
+ // Delete the static field that was converted to an enum constant.
+ _deleteField(builder, field, members);
+ }
+
+ // Remove the index field.
+ var indexField = fields.indexField;
+ if (indexField != null) {
+ _deleteField(builder, indexField, members);
+ }
+
+ // Update the constructors.
+ var removedConstructor = _removeUnnamedConstructor();
+ _transformConstructors(builder, removedConstructor);
+
+ // Special case replacing all of the members.
+ if (membersToDelete.length == members.length) {
+ builder.addSimpleReplacement(range.startEnd(members.first, members.last),
+ constantsBuffer.toString());
+ return;
+ }
+
+ // Insert the declarations of the enum constants.
+ var semicolon = ';';
+ var prefix = '${utils.endOfLine}$indent';
+ var suffix = '$semicolon${utils.endOfLine}';
+ builder.addSimpleInsertion(bodyOffset, '$prefix$constantsBuffer$suffix');
+
+ // Delete any members that are no longer needed.
+ membersToDelete.sort();
+ for (var range in range.nodesInList(members, membersToDelete)) {
+ builder.addDeletion(range);
+ }
+ }
+
+ /// Use the [builder] to delete the [field].
+ void _deleteField(DartFileEditBuilder builder, _Field field,
+ NodeList<ClassMember> members) {
+ var variableList = field.declarationList;
+ if (variableList.variables.length == 1) {
+ membersToDelete.add(members.indexOf(field.fieldDeclaration));
+ } else {
+ builder.addDeletion(
+ range.nodeInList(variableList.variables, field.declaration));
+ }
+ }
+
+ /// If the unnamed constructor is the only constructor, and if it has no
+ /// parameters other than potentially the index field, then remove it.
+ ConstructorDeclaration? _removeUnnamedConstructor() {
+ var members = classDeclaration.members;
+ var constructors = members.whereType<ConstructorDeclaration>().toList();
+ if (constructors.length != 1) {
+ return null;
+ }
+ var constructor = constructors[0];
+ var name = constructor.name?.name;
+ if (name != null && name != 'new') {
+ return null;
+ }
+ var parameters = constructor.parameters.parameters;
+ // If there's only one constructor, then there can only be one entry in the
+ // constructor map.
+ var parameterData = constructorMap?.entries.first.value;
+ // `parameterData` should only be `null` if there is no index field.
+ var updatedParameterCount =
+ parameters.length - (parameterData == null ? 0 : 1);
+ if (updatedParameterCount != 0) {
+ return null;
+ }
+ membersToDelete.add(members.indexOf(constructor));
+ return constructor;
+ }
+
+ /// Transform the used constructors by removing the parameter corresponding to
+ /// the index field.
+ void _transformConstructors(
+ DartFileEditBuilder builder, ConstructorDeclaration? removedConstructor) {
+ final constructorMap = this.constructorMap;
+ if (constructorMap == null) {
+ return;
+ }
+ for (var constructor in constructorMap.keys) {
+ if (constructor.declaration != removedConstructor) {
+ var parameterData = constructorMap[constructor];
+ if (parameterData != null) {
+ var parameters = constructor.declaration.parameters.parameters;
+ builder.addDeletion(
+ range.nodeInList(parameters, parameters[parameterData.index]));
+ }
+ }
+ }
+ }
+
+ /// If the given [node] can be converted into an enum, then return a
+ /// description of the conversion work to be done. Otherwise, return `null`.
+ static _EnumDescription? fromClass(ClassDeclaration node) {
+ // The class must be a concrete class.
+ var classElement = node.declaredElement;
+ if (classElement == null || classElement.isAbstract) {
+ return null;
+ }
+
+ // The class must be a subclass of Object, whether implicitly or explicitly.
+ var extendsClause = node.extendsClause;
+ if (extendsClause != null &&
+ extendsClause.superclass.type?.isDartCoreObject == false) {
+ return null;
+ }
+
+ // The class must either be private or must only have private constructors.
+ var constructors = _validateConstructors(node, classElement);
+ if (constructors == null) {
+ return null;
+ }
+
+ // The class must not override either `==` or `hashCode`.
+ if (!_validateMethods(node)) {
+ return null;
+ }
+
+ // There must be at least one static field that can be converted into an
+ // enum constant.
+ //
+ // The instance fields must all be final.
+ var fields = _validateFields(node, classElement);
+ if (fields == null || fields.fieldsToConvert.isEmpty) {
+ return null;
+ }
+
+ var visitor = _EnumVisitor(classElement, fields.fieldsToConvert);
+ try {
+ node.accept(visitor);
+ } on _CannotConvertException {
+ return null;
+ }
+
+ // Within the defining library,
+ // - there can't be any subclasses of the class to be converted,
+ // - there can't be any invocations of any constructor from that class.
+ try {
+ node.root.accept(_NonEnumVisitor(classElement));
+ } on _CannotConvertException {
+ return null;
+ }
+
+ var usedConstructors = _computeUsedConstructors(constructors, fields);
+ var constructorMap = _indexFieldData(usedConstructors, fields);
+ if (fields.indexField != null && constructorMap == null) {
+ return null;
+ }
+
+ var membersToDelete = <int>[];
+ return _EnumDescription(
+ classDeclaration: node,
+ constructorMap: constructorMap,
+ fields: fields,
+ membersToDelete: membersToDelete,
+ );
+ }
+
+ /// Return the subset of [constructors] that are invoked by the [fields] to be
+ /// converted.
+ static _Constructors _computeUsedConstructors(
+ _Constructors constructors, _Fields fields) {
+ var usedElements = <ConstructorElement>{};
+ for (var field in fields.fieldsToConvert) {
+ usedElements.add(field.constructorElement);
+ }
+ var usedConstructors = _Constructors();
+ for (var element in usedElements) {
+ var constructor = constructors.forElement(element);
+ if (constructor != null) {
+ usedConstructors.add(constructor);
+ }
+ }
+ return usedConstructors;
+ }
+
+ /// If the index field can be removed, return a map describing the changes
+ /// that need to be made to both the constructors and the invocations of those
+ /// constructors. Otherwise, return `null`.
+ static Map<_Constructor, _Parameter>? _indexFieldData(
+ _Constructors usedConstructors, _Fields fields) {
+ var indexField = fields.indexField;
+ if (indexField == null) {
+ return null;
+ }
+ // Ensure that the index field has a corresponding field formal initializer
+ // in each of the used constructors.
+ var constructorMap = <_Constructor, _Parameter>{};
+ for (var constructor in usedConstructors.constructors) {
+ var parameterData = _indexParameter(constructor, indexField);
+ if (parameterData == null) {
+ return null;
+ }
+ constructorMap[constructor] = parameterData;
+ }
+
+ var fieldsToConvert = fields.fieldsToConvert;
+ var values = <int>{};
+ for (var field in fieldsToConvert) {
+ var constructorElement = field.constructorElement;
+ var constructor = usedConstructors.forElement(constructorElement);
+ if (constructor == null) {
+ // We should never reach this point.
+ return null;
+ }
+ var parameterData = constructorMap[constructor];
+ if (parameterData == null) {
+ // We should never reach this point.
+ return null;
+ }
+ var arguments = field.instanceCreation.argumentList.arguments;
+ var argument = parameterData.getArgument(arguments);
+ if (argument is! IntegerLiteral) {
+ return null;
+ }
+ var value = argument.value;
+ if (value == null) {
+ return null;
+ }
+ if (!values.add(value)) {
+ // Duplicate value.
+ return null;
+ }
+ }
+ var sortedValues = values.toList()..sort();
+ if (sortedValues.length == fieldsToConvert.length &&
+ sortedValues.first == 0 &&
+ sortedValues.last == fieldsToConvert.length - 1) {
+ return constructorMap;
+ }
+ return null;
+ }
+
+ static _Parameter? _indexParameter(
+ _Constructor constructor, _Field? indexField) {
+ if (indexField == null) {
+ return null;
+ }
+ var parameters = constructor.declaration.parameters.parameters;
+ var indexFieldElement = indexField.element;
+ for (var i = 0; i < parameters.length; i++) {
+ var element = parameters[i].declaredElement;
+ if (element is FieldFormalParameterElement) {
+ if (element.field == indexFieldElement) {
+ if (element.isPositional) {
+ return _Parameter(i, element);
+ } else {
+ return _Parameter(i, element);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /// Return a representation of all of the constructors declared by the
+ /// [classDeclaration], or `null` if the class can't be converted.
+ ///
+ /// The [classElement] must be the element declared by the [classDeclaration].
+ static _Constructors? _validateConstructors(
+ ClassDeclaration classDeclaration, ClassElement classElement) {
+ var constructors = _Constructors();
+ for (var member in classDeclaration.members) {
+ if (member is ConstructorDeclaration) {
+ var constructor = member.declaredElement;
+ if (constructor is ConstructorElement) {
+ if (!classElement.isPrivate && !constructor.isPrivate) {
+ // Public constructor in public enum.
+ return null;
+ } else if (constructor.isFactory) {
+ // Factory constructor.
+ return null;
+ } else if (!constructor.isConst) {
+ // Non-const constructor.
+ return null;
+ }
+ constructors.add(_Constructor(member, constructor));
+ } else {
+ // Not resolved.
+ return null;
+ }
+ }
+ }
+ return constructors;
+ }
+
+ /// Return a representation of all of the constructors declared by the
+ /// [classDeclaration], or `null` if the class can't be converted.
+ ///
+ /// The [classElement] must be the element declared by the [classDeclaration].
+ static _Fields? _validateFields(
+ ClassDeclaration classDeclaration, ClassElement classElement) {
+ var potentialFieldsToConvert = <DartObject, List<_ConstantField>>{};
+ _Field? indexField;
+
+ for (var member in classDeclaration.members) {
+ if (member is FieldDeclaration) {
+ var fieldList = member.fields;
+ var fields = fieldList.variables;
+ if (member.isStatic) {
+ for (var field in fields) {
+ var fieldElement = field.declaredElement;
+ if (fieldElement is FieldElement) {
+ var fieldType = fieldElement.type;
+ // The field can be converted to be an enum constant if it
+ // - is a const field,
+ // - has a type equal to the type of the class, and
+ // - is initialized by an instance creation expression defined in this
+ // class.
+ if (fieldElement.isConst &&
+ fieldType is InterfaceType &&
+ fieldType.element == classElement) {
+ var initializer = field.initializer;
+ if (initializer is InstanceCreationExpression) {
+ var constructorElement =
+ initializer.constructorName.staticElement;
+ if (constructorElement != null &&
+ constructorElement.enclosingElement == classElement) {
+ var fieldValue = fieldElement.computeConstantValue();
+ if (fieldValue != null) {
+ if (fieldList.variables.length != 1) {
+ // Too many constants in the field declaration.
+ return null;
+ }
+ potentialFieldsToConvert
+ .putIfAbsent(fieldValue, () => [])
+ .add(_ConstantField(
+ fieldElement,
+ field,
+ fieldList,
+ member,
+ initializer,
+ constructorElement,
+ fieldValue.getField('index')?.toIntValue() ??
+ -1));
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ for (var field in fields) {
+ if (!field.isFinal) {
+ // Non-final instance field.
+ return null;
+ }
+ var fieldElement = field.declaredElement;
+ if (fieldElement is FieldElement) {
+ var fieldType = fieldElement.type;
+ if (fieldElement.name == 'index' && fieldType.isDartCoreInt) {
+ indexField = _Field(fieldElement, field, fieldList, member);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ var fieldsToConvert = <_ConstantField>[];
+ for (var list in potentialFieldsToConvert.values) {
+ if (list.length == 1) {
+ fieldsToConvert.add(list[0]);
+ } else {
+ // TODO(brianwilkerson) We could potentially handle the case where
+ // there's only one non-deprecated field in the list. We'd need to
+ // change the return type for this method so that we could return two
+ // lists: the list of fields to convert and the list of fields whose
+ // initializer needs to be updated to refer to the constant.
+ return null;
+ }
+ }
+ return _Fields(fieldsToConvert, indexField);
+ }
+
+ /// Return `true` if the [classDeclaration] does not contain any methods that
+ /// prevent it from being converted.
+ static bool _validateMethods(ClassDeclaration classDeclaration) {
+ for (var member in classDeclaration.members) {
+ if (member is MethodDeclaration) {
+ if (member.name.name == '==' || member.name.name == 'hashCode') {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
+
+/// A visitor used to visit the class being converted. This visitor throws an
+/// exception if a constructor for the class is invoked anywhere other than the
+/// top-level expression of an initializer for one of the fields being converted.
+class _EnumVisitor extends _BaseVisitor {
+ /// The declarations of the fields that are to be converted.
+ final List<VariableDeclaration> fieldsToConvert;
+
+ /// A flag indicating whether we are currently visiting the children of a
+ /// field declaration that will be converted to be a constant.
+ bool inConstantDeclaration = false;
+
+ /// Initialize a newly created visitor to visit the class declaration
+ /// corresponding to the given [classElement].
+ _EnumVisitor(ClassElement classElement, List<_ConstantField> fieldsToConvert)
+ : fieldsToConvert =
+ fieldsToConvert.map((field) => field.declaration).toList(),
+ super(classElement);
+
+ @override
+ void visitInstanceCreationExpression(InstanceCreationExpression node) {
+ if (!inConstantDeclaration) {
+ if (invokesEnumConstructor(node)) {
+ throw _CannotConvertException(
+ 'Constructor used outside constant initializer');
+ }
+ }
+ inConstantDeclaration = false;
+ super.visitInstanceCreationExpression(node);
+ }
+
+ @override
+ void visitVariableDeclaration(VariableDeclaration node) {
+ if (fieldsToConvert.contains(node)) {
+ inConstantDeclaration = true;
+ }
+ super.visitVariableDeclaration(node);
+ inConstantDeclaration = false;
+ }
+}
+
+/// A representation of a field of interest in the class being converted.
+class _Field {
+ /// The element representing the field.
+ final FieldElement element;
+
+ /// The declaration of the field.
+ final VariableDeclaration declaration;
+
+ /// The list containing the [declaration]
+ final VariableDeclarationList declarationList;
+
+ /// The field declaration containing the [declarationList].
+ final FieldDeclaration fieldDeclaration;
+
+ _Field(this.element, this.declaration, this.declarationList,
+ this.fieldDeclaration);
+
+ /// Return the name of the field.
+ String get name => declaration.name.name;
+}
+
+/// A representation of all the fields of interest in the class being converted.
+class _Fields {
+ /// The fields to be converted into enum constants.
+ List<_ConstantField> fieldsToConvert;
+
+ /// The index field, or `null` if there is no index field.
+ _Field? indexField;
+
+ _Fields(this.fieldsToConvert, this.indexField);
+}
+
+/// A visitor that visits everything in the library other than the class being
+/// converted. This visitor throws an exception if the class can't be converted
+/// because
+/// - there is a subclass of the class, or
+/// - there is an invocation of one of the constructors of the class.
+class _NonEnumVisitor extends _BaseVisitor {
+ /// Initialize a newly created visitor to visit everything except the class
+ /// declaration corresponding to the given [classElement].
+ _NonEnumVisitor(ClassElement classElement) : super(classElement);
+
+ @override
+ void visitClassDeclaration(ClassDeclaration node) {
+ var element = node.declaredElement;
+ if (element == null) {
+ throw _CannotConvertException('Unresolved');
+ }
+ if (element != classElement) {
+ if (element.supertype?.element == classElement) {
+ throw _CannotConvertException('Class is extended');
+ } else if (element.interfaces
+ .map((e) => e.element)
+ .contains(classElement)) {
+ throw _CannotConvertException('Class is implemented');
+ } else if (element.mixins.map((e) => e.element).contains(classElement)) {
+ // This case won't occur unless there's an error in the source code, but
+ // it's easier to check for the condition than it is to check for the
+ // diagnostic.
+ throw _CannotConvertException('Class is mixed in');
+ }
+ super.visitClassDeclaration(node);
+ }
+ }
+
+ @override
+ void visitInstanceCreationExpression(InstanceCreationExpression node) {
+ if (invokesEnumConstructor(node)) {
+ throw _CannotConvertException(
+ 'Constructor used outside class being converted');
+ }
+ super.visitInstanceCreationExpression(node);
+ }
+}
+
+/// An object used to access information about a specific parameter, including
+/// its index in the parameter list as well as any associated argument in an
+/// argument list.
+class _Parameter {
+ /// The index of this parameter in the enclosing constructor's parameter list.
+ final int index;
+
+ /// The element associated with the parameter.
+ final ParameterElement element;
+
+ _Parameter(this.index, this.element);
+
+ /// Return the expression representing the argument associated with this
+ /// parameter, or `null` if there is no such argument.
+ Expression? getArgument(NodeList<Expression> arguments) {
+ return arguments.firstWhereOrNull(
+ (argument) => argument.staticParameterElement == element);
+ }
+}
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
new file mode 100644
index 0000000..6222f82
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/flutter_snippet_producers.dart
@@ -0,0 +1,160 @@
+// 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/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/analysis/session_helper.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:meta/meta.dart';
+
+abstract class FlutterSnippetProducer extends SnippetProducer {
+ final flutter = Flutter.instance;
+ final AnalysisSessionHelper sessionHelper;
+
+ late ClassElement? classWidget;
+
+ FlutterSnippetProducer(DartSnippetRequest request)
+ : sessionHelper = AnalysisSessionHelper(request.analysisSession),
+ super(request);
+
+ @override
+ @mustCallSuper
+ Future<bool> isValid() async {
+ classWidget = await _getClass('Widget');
+
+ return classWidget != null;
+ }
+
+ Future<ClassElement?> _getClass(String name) =>
+ sessionHelper.getClass(flutter.widgetsUri, name);
+
+ DartType _getType(
+ ClassElement classElement, [
+ NullabilitySuffix nullabilitySuffix = NullabilitySuffix.none,
+ ]) =>
+ classElement.instantiate(
+ typeArguments: const [],
+ nullabilitySuffix: nullabilitySuffix,
+ );
+}
+
+/// Contributes snippets useful in Flutter applications, such as creating
+/// Flutter widget classes.
+class FlutterStatelessWidgetSnippetProducer extends FlutterSnippetProducer {
+ static const prefix = 'stless';
+ static const label = 'Flutter Stateless Widget';
+
+ late ClassElement? classStatelessWidget;
+ late ClassElement? classBuildContext;
+ late ClassElement? classKey;
+
+ FlutterStatelessWidgetSnippetProducer(DartSnippetRequest request)
+ : super(request);
+
+ @override
+ Future<Snippet> compute() async {
+ final builder = ChangeBuilder(session: request.analysisSession);
+
+ // Checked by isValid().
+ final classStatelessWidget = this.classStatelessWidget!;
+ final classWidget = this.classWidget!;
+ final classBuildContext = this.classBuildContext!;
+ final classKey = this.classKey!;
+
+ // Only include `?` for nulable types like Key? if in a null-safe library.
+ final nullableSuffix = request.unit.libraryElement.isNonNullableByDefault
+ ? NullabilitySuffix.question
+ : NullabilitySuffix.none;
+
+ final className = 'MyWidget';
+ await builder.addDartFileEdit(request.filePath, (builder) {
+ builder.addReplacement(request.replacementRange, (builder) {
+ builder.writeClassDeclaration(
+ className,
+ nameGroupName: 'name',
+ superclass: _getType(classStatelessWidget),
+ membersWriter: () {
+ // Add the constructor.
+ builder.write(' ');
+ builder.writeConstructorDeclaration(
+ className,
+ classNameGroupName: 'name',
+ isConst: true,
+ parameterWriter: () {
+ builder.write('{');
+ builder.writeParameter(
+ 'key',
+ type: _getType(classKey, nullableSuffix),
+ );
+ builder.write('}');
+ },
+ initializerWriter: () => builder.write('super(key: key)'),
+ );
+ builder.writeln();
+ builder.writeln();
+
+ // 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(' ');
+ builder.selectHere();
+ builder.writeln('');
+ builder.writeln(' }');
+ },
+ );
+ },
+ );
+ });
+ });
+
+ return Snippet(
+ prefix,
+ label,
+ 'Insert a StatelessWidget',
+ builder.sourceChange,
+ );
+ }
+
+ @override
+ Future<bool> isValid() async {
+ if (!await super.isValid()) {
+ return false;
+ }
+
+ classStatelessWidget = await _getClass('StatelessWidget');
+ if (classStatelessWidget == null) {
+ return false;
+ }
+
+ classBuildContext = await _getClass('BuildContext');
+ if (classBuildContext == null) {
+ return false;
+ }
+
+ classKey = await _getClass('Key');
+ if (classKey == null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ static FlutterStatelessWidgetSnippetProducer newInstance(
+ DartSnippetRequest request) =>
+ FlutterStatelessWidgetSnippetProducer(request);
+}
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
new file mode 100644
index 0000000..6895873
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/snippets/dart/snippet_manager.dart
@@ -0,0 +1,186 @@
+// 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/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: [
+ FlutterStatelessWidgetSnippetProducer.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();
+}
diff --git a/pkg/analysis_server/lib/src/utilities/extensions/range_factory.dart b/pkg/analysis_server/lib/src/utilities/extensions/range_factory.dart
index 95c5384..eedc3c7 100644
--- a/pkg/analysis_server/lib/src/utilities/extensions/range_factory.dart
+++ b/pkg/analysis_server/lib/src/utilities/extensions/range_factory.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/scanner/token.dart';
+import 'package:analysis_server/src/utilities/index_range.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/source/source_range.dart';
@@ -67,6 +68,30 @@
}
}
+ /// Return a list of the ranges that cover all of the elements in the [list]
+ /// whose index is in the list of [indexes].
+ List<SourceRange> nodesInList<T extends AstNode>(
+ NodeList<T> list, List<int> indexes) {
+ var ranges = <SourceRange>[];
+ var indexRanges = IndexRange.contiguousSubRanges(indexes);
+ if (indexRanges.length == 1) {
+ var indexRange = indexRanges[0];
+ if (indexRange.lower == 0 && indexRange.upper == list.length - 1) {
+ ranges.add(startEnd(list[indexRange.lower], list[indexRange.upper]));
+ return ranges;
+ }
+ }
+ for (var indexRange in indexRanges) {
+ if (indexRange.lower == 0) {
+ ranges.add(
+ startStart(list[indexRange.lower], list[indexRange.upper + 1]));
+ } else {
+ ranges.add(endEnd(list[indexRange.lower - 1], list[indexRange.upper]));
+ }
+ }
+ return ranges;
+ }
+
/// Return a source range that covers the given [node] with any leading and
/// trailing comments.
///
diff --git a/pkg/analysis_server/lib/src/utilities/index_range.dart b/pkg/analysis_server/lib/src/utilities/index_range.dart
new file mode 100644
index 0000000..648ec02
--- /dev/null
+++ b/pkg/analysis_server/lib/src/utilities/index_range.dart
@@ -0,0 +1,42 @@
+// 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.
+
+/// A range of indexes within a list.
+class IndexRange {
+ /// The index of the first element in the range.
+ final int lower;
+
+ /// The index of the last element in the range. This will be the same as the
+ /// [lower] if there is a single element in the range.
+ final int upper;
+
+ /// Initialize a newly created range.
+ IndexRange(this.lower, this.upper);
+
+ /// Return the number of indices in this range.
+ int get count => upper - lower + 1;
+
+ @override
+ String toString() => '[$lower..$upper]';
+
+ static List<IndexRange> contiguousSubRanges(List<int> indexes) {
+ var ranges = <IndexRange>[];
+ if (indexes.isEmpty) {
+ return ranges;
+ }
+ var lower = indexes[0];
+ var previous = lower;
+ for (var index = 1; index < indexes.length; index++) {
+ var current = indexes[index];
+ if (current == previous + 1) {
+ previous = current;
+ } else {
+ ranges.add(IndexRange(lower, previous));
+ lower = previous = current;
+ }
+ }
+ ranges.add(IndexRange(lower, previous));
+ return ranges;
+ }
+}
diff --git a/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart b/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
index 925b13b..9a541dc 100644
--- a/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
@@ -341,7 +341,7 @@
}
Future<void> test_outsideRoot() async {
- final otherFilePath = '/home/otherProject/foo.dart';
+ final otherFilePath = convertPath('/home/otherProject/foo.dart');
final otherFileUri = Uri.file(otherFilePath);
newFile(otherFilePath, content: 'bad code to create error');
await initialize(
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index 1f749a6..5588ee9 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -5,6 +5,7 @@
import 'package:analysis_server/lsp_protocol/protocol_generated.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/flutter_snippet_producers.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';
@@ -19,6 +20,9 @@
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(CompletionTest);
+ defineReflectiveTests(DartSnippetCompletionTest);
+ defineReflectiveTests(FlutterSnippetCompletionTest);
+ defineReflectiveTests(FlutterSnippetCompletionWithNullSafetyTest);
defineReflectiveTests(CompletionTestWithNullSafetyTest);
});
}
@@ -2363,3 +2367,246 @@
expect(completion.detail, '(int? a, [int b = 1]) → String?');
}
}
+
+@reflectiveTest
+class DartSnippetCompletionTest extends SnippetCompletionTest {
+ Future<void> test_snippets_disabled() async {
+ final content = '^';
+
+ // Advertise support (this is done by the editor), but with the user
+ // preference disabled.
+ await provideConfig(
+ () => initialize(
+ textDocumentCapabilities: withCompletionItemSnippetSupport(
+ emptyTextDocumentClientCapabilities),
+ workspaceCapabilities:
+ withConfigurationSupport(emptyWorkspaceClientCapabilities),
+ ),
+ {'enableSnippets': true},
+ );
+
+ await expectNoSnippet(
+ content,
+ FlutterStatelessWidgetSnippetProducer.prefix,
+ );
+ }
+
+ Future<void>
+ test_snippets_flutterStateless_notAvailable_notFlutterProject() async {
+ final content = '''
+class A {}
+
+stle^
+
+class B {}
+''';
+
+ await initializeWithSnippetSupportAndPreviewFlag();
+ await expectNoSnippet(
+ content,
+ FlutterStatelessWidgetSnippetProducer.prefix,
+ );
+ }
+
+ Future<void> test_snippets_notSupported() async {
+ final content = '^';
+
+ // If we don't send support for Snippet CompletionItem kinds, we don't
+ // expect any snippets at all.
+ await initialize();
+ await openFile(mainFileUri, withoutMarkers(content));
+ final res = await getCompletion(mainFileUri, positionFromMarker(content));
+ expect(res.any((c) => c.kind == CompletionItemKind.Snippet), isFalse);
+ }
+}
+
+@reflectiveTest
+class FlutterSnippetCompletionTest extends SnippetCompletionTest {
+ /// Nullability suffix expected in this test class.
+ ///
+ /// Used to allow all tests to be run in both modes without having to
+ /// duplicate all tests ([FlutterSnippetCompletionWithNullSafetyTest]
+ /// overrides this).
+ String get questionSuffix => '';
+
+ @override
+ void setUp() {
+ super.setUp();
+ writePackageConfig(
+ projectFolderPath,
+ flutter: true,
+ );
+ }
+
+ Future<void> test_snippets_flutterStateless() async {
+ final content = '''
+import 'package:flutter/widgets.dart';
+
+class A {}
+
+stle^
+
+class B {}
+''';
+
+ await initializeWithSnippetSupportAndPreviewFlag();
+ final updated = await expectAndApplySnippet(
+ content,
+ prefix: FlutterStatelessWidgetSnippetProducer.prefix,
+ label: FlutterStatelessWidgetSnippetProducer.label,
+ );
+
+ expect(updated, '''
+import 'package:flutter/widgets.dart';
+
+class A {}
+
+class \${1:MyWidget} extends StatelessWidget {
+ const \${1:MyWidget}({Key$questionSuffix key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ \$0
+ }
+}
+
+class B {}
+''');
+ }
+
+ Future<void> test_snippets_flutterStateless_addsImports() async {
+ final content = '''
+class A {}
+
+stle^
+
+class B {}
+''';
+
+ await initializeWithSnippetSupportAndPreviewFlag();
+ final updated = await expectAndApplySnippet(
+ content,
+ prefix: FlutterStatelessWidgetSnippetProducer.prefix,
+ label: FlutterStatelessWidgetSnippetProducer.label,
+ );
+
+ expect(updated, '''
+import 'package:flutter/src/foundation/key.dart';
+import 'package:flutter/src/widgets/framework.dart';
+
+class A {}
+
+class \${1:MyWidget} extends StatelessWidget {
+ const \${1:MyWidget}({Key$questionSuffix key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ \$0
+ }
+}
+
+class B {}
+''');
+ }
+
+ Future<void> test_snippets_flutterStateless_notAvailable_notTopLevel() async {
+ final content = '''
+class A {
+
+ stle^
+
+}
+''';
+
+ await initializeWithSnippetSupportAndPreviewFlag();
+ await expectNoSnippet(
+ content,
+ FlutterStatelessWidgetSnippetProducer.prefix,
+ );
+ }
+}
+
+@reflectiveTest
+class FlutterSnippetCompletionWithNullSafetyTest
+ extends FlutterSnippetCompletionTest {
+ @override
+ String get questionSuffix => '?';
+
+ @override
+ String get testPackageLanguageVersion => latestLanguageVersion;
+}
+
+abstract class SnippetCompletionTest extends AbstractLspAnalysisServerTest
+ with CompletionTestMixin {
+ /// Expect that there is a snippet for [prefix] at [position] with the label
+ /// [label] and return the results of applying it to [content].
+ Future<String> expectAndApplySnippet(
+ String content, {
+ required String prefix,
+ required String label,
+ }) async {
+ final snippet = await expectSnippet(
+ content,
+ prefix: prefix,
+ label: label,
+ );
+
+ // Also apply the edit and check that it went in the right place with the
+ // correct formatting. Edit groups will just appear in the raw textmate
+ // snippet syntax here, as we don't do any special handling of them (and
+ // assume what's coded here is correct, and that the client will correctly
+ // interpret them).
+ final updated = applyTextEdits(
+ withoutMarkers(content),
+ [toTextEdit(snippet.textEdit!)]
+ .followedBy(snippet.additionalTextEdits!)
+ .toList(),
+ );
+ return updated;
+ }
+
+ /// Expect that there is no snippet for [prefix] at the position of `^` within
+ /// [content].
+ Future<void> expectNoSnippet(
+ String content,
+ String prefix,
+ ) async {
+ await openFile(mainFileUri, withoutMarkers(content));
+ final res = await getCompletion(mainFileUri, positionFromMarker(content));
+ final hasSnippet = res.any((c) => c.filterText == prefix);
+ expect(hasSnippet, isFalse);
+ }
+
+ /// Expect that there is a snippet for [prefix] with the label [label] at
+ /// [position] in [content].
+ Future<CompletionItem> expectSnippet(
+ String content, {
+ required String prefix,
+ required String label,
+ }) async {
+ await openFile(mainFileUri, withoutMarkers(content));
+ final res = await getCompletion(mainFileUri, positionFromMarker(content));
+ final item = res.singleWhere(
+ (c) => c.filterText == prefix && c.label == label,
+ );
+ expect(item.insertTextFormat, InsertTextFormat.Snippet);
+ expect(item.insertText, isNull);
+ expect(item.textEdit, isNotNull);
+ return item;
+ }
+
+ Future<void> initializeWithSnippetSupport() => initialize(
+ textDocumentCapabilities: withCompletionItemSnippetSupport(
+ emptyTextDocumentClientCapabilities),
+ );
+
+ Future<void> initializeWithSnippetSupportAndPreviewFlag() => provideConfig(
+ () => initialize(
+ textDocumentCapabilities: withCompletionItemSnippetSupport(
+ emptyTextDocumentClientCapabilities),
+ workspaceCapabilities:
+ withConfigurationSupport(emptyWorkspaceClientCapabilities),
+ ),
+ {'previewEnableSnippets': true},
+ );
+}
diff --git a/pkg/analysis_server/test/lsp/snippets_test.dart b/pkg/analysis_server/test/lsp/snippets_test.dart
index ba25212..a126c9b 100644
--- a/pkg/analysis_server/test/lsp/snippets_test.dart
+++ b/pkg/analysis_server/test/lsp/snippets_test.dart
@@ -4,6 +4,7 @@
import 'package:analysis_server/src/lsp/snippets.dart' as lsp;
import 'package:analysis_server/src/lsp/snippets.dart';
+import 'package:analysis_server/src/protocol_server.dart' as server;
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -129,6 +130,146 @@
@reflectiveTest
class SnippetsTest {
+ /// Paths aren't used for anything except filtering edit groups positions
+ /// so the specific values are not important.
+ final mainPath = '/home/test.dart';
+ final otherPath = '/home/other.dart';
+
+ Future<void> test_editGroups_choices() async {
+ var result = lsp.buildSnippetStringForEditGroups(
+ r'''
+var a = 1;
+''',
+ filePath: mainPath,
+ editOffset: 0,
+ editGroups: [
+ server.LinkedEditGroup(
+ [_pos(4)],
+ 1,
+ [
+ _suggestion('aaa'),
+ _suggestion(r'bbb${},|'), // test for escaping
+ _suggestion('ccc'),
+ ],
+ ),
+ ],
+ );
+ expect(result, equals(r'''
+var ${1|a,aaa,bbb\${\}\,\|,ccc|} = 1;
+'''));
+ }
+
+ Future<void> test_editGroups_emptyGroup() async {
+ var result = lsp.buildSnippetStringForEditGroups(
+ r'''
+class {
+ ();
+}
+''',
+ filePath: mainPath,
+ editOffset: 0,
+ editGroups: [
+ server.LinkedEditGroup(
+ [
+ _pos(6),
+ _pos(11),
+ ],
+ 0,
+ [],
+ ),
+ ],
+ );
+ expect(result, equals(r'''
+class $1 {
+ $1();
+}
+'''));
+ }
+
+ Future<void> test_editGroups_positionsInOtherFiles() async {
+ var result = lsp.buildSnippetStringForEditGroups(
+ r'''
+class A {
+ A();
+}
+''',
+ filePath: mainPath,
+ editOffset: 0,
+ editGroups: [
+ server.LinkedEditGroup(
+ [
+ _pos(6),
+ _pos(10, otherPath), // Should not be included.
+ _pos(12),
+ ],
+ 1,
+ [],
+ ),
+ ],
+ );
+ expect(result, equals(r'''
+class ${1:A} {
+ ${1:A}();
+}
+'''));
+ }
+
+ Future<void> test_editGroups_simpleGroup() async {
+ var result = lsp.buildSnippetStringForEditGroups(
+ r'''
+class A {
+ A();
+}
+''',
+ filePath: mainPath,
+ editOffset: 0,
+ editGroups: [
+ server.LinkedEditGroup(
+ [
+ _pos(6),
+ _pos(12),
+ ],
+ 1,
+ [],
+ ),
+ ],
+ );
+ expect(result, equals(r'''
+class ${1:A} {
+ ${1:A}();
+}
+'''));
+ }
+
+ Future<void> test_editGroups_withOffset() async {
+ var result = lsp.buildSnippetStringForEditGroups(
+ r'''
+class A {
+ A();
+}
+''',
+ filePath: mainPath,
+ // This means the edit will be inserted at offset 100, so all linked edit
+ // offsets will be 100 more than in the supplied text.
+ editOffset: 100,
+ editGroups: [
+ server.LinkedEditGroup(
+ [
+ _pos(100 + 6),
+ _pos(100 + 12),
+ ],
+ 1,
+ [],
+ ),
+ ],
+ );
+ expect(result, equals(r'''
+class ${1:A} {
+ ${1:A}();
+}
+'''));
+ }
+
Future<void> test_tabStops_contains() async {
var result = lsp.buildSnippetStringWithTabStops('a, b, c', [3, 1]);
expect(result, equals(r'a, ${0:b}, c'));
@@ -165,4 +306,13 @@
var result = lsp.buildSnippetStringWithTabStops('a, b', [0, 1]);
expect(result, equals(r'${0:a}, b'));
}
+
+ server.Position _pos(int offset, [String? path]) =>
+ server.Position(path ?? mainPath, offset);
+
+ server.LinkedEditSuggestion _suggestion(String text) =>
+ server.LinkedEditSuggestion(
+ text,
+ server.LinkedEditSuggestionKind.TYPE, // We don't use type.
+ );
}
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
new file mode 100644
index 0000000..1b9449c
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/flutter_snippet_producers_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/services/snippets/dart/flutter_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(FlutterStatelessWidgetSnippetProducerTest);
+ });
+}
+
+class FlutterSnippetProducerTest extends AbstractSingleUnitTest {
+ Future<void> expectNotValidSnippet(
+ SnippetProducerGenerator generator,
+ 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(
+ SnippetProducerGenerator generator, 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 FlutterStatelessWidgetSnippetProducerTest
+ extends FlutterSnippetProducerTest {
+ Future<void> test_notValid_notFlutterProject() async {
+ writeTestPackageConfig();
+
+ await expectNotValidSnippet(
+ FlutterStatelessWidgetSnippetProducer.newInstance,
+ '^',
+ );
+ }
+
+ Future<void> test_notValid_notTopLevel() async {
+ writeTestPackageConfig();
+
+ await expectNotValidSnippet(
+ FlutterStatelessWidgetSnippetProducer.newInstance,
+ 'class A { ^ }',
+ );
+ }
+
+ Future<void> test_valid() async {
+ writeTestPackageConfig(flutter: true);
+
+ final snippet = await expectValidSnippet(
+ FlutterStatelessWidgetSnippetProducer.newInstance,
+ '^',
+ );
+ expect(snippet.prefix, 'stless');
+ expect(snippet.label, 'Flutter Stateless Widget');
+ expect(snippet.change.toJson(), {
+ 'message': '',
+ 'edits': [
+ {
+ 'file': testFile,
+ 'fileStamp': 0,
+ 'edits': [
+ {
+ 'offset': 0,
+ 'length': 0,
+ 'replacement':
+ 'import \'package:flutter/src/foundation/key.dart\';\n'
+ 'import \'package:flutter/src/widgets/framework.dart\';\n'
+ },
+ {
+ 'offset': 0,
+ 'length': 0,
+ 'replacement': 'class MyWidget extends StatelessWidget {\n'
+ ' const MyWidget({Key? key}) : super(key: key);\n'
+ '\n'
+ ' @override\n'
+ ' Widget build(BuildContext context) {\n'
+ ' \n'
+ ' }\n'
+ '}'
+ }
+ ]
+ }
+ ],
+ 'linkedEditGroups': [
+ {
+ 'positions': [
+ {'file': testFile, 'offset': 109},
+ {'file': testFile, 'offset': 152}
+ ],
+ 'length': 8,
+ 'suggestions': []
+ }
+ ],
+ 'selection': {'file': testFile, 'offset': 248}
+ });
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/snippet_manager_test.dart b/pkg/analysis_server/test/services/snippets/dart/snippet_manager_test.dart
new file mode 100644
index 0000000..622d84f
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/snippet_manager_test.dart
@@ -0,0 +1,116 @@
+// 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/snippet_manager.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';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(SnippetManagerTest);
+ });
+}
+
+@reflectiveTest
+class SnippetManagerTest extends AbstractSingleUnitTest {
+ Future<void> test_notValidProducers() async {
+ await resolveTestCode('');
+ final request = DartSnippetRequest(
+ unit: testAnalysisResult,
+ offset: 0,
+ );
+
+ final manager = _TestDartSnippetManager({
+ SnippetContext.atTopLevel: [_NotValidSnippetProducer.newInstance],
+ });
+ final results = await manager.computeSnippets(request);
+ expect(results, isEmpty);
+ }
+
+ Future<void> test_onlyCreatesForContext() async {
+ await resolveTestCode('');
+ final request = DartSnippetRequest(
+ unit: testAnalysisResult,
+ offset: 0,
+ );
+
+ final manager = _TestDartSnippetManager({
+ SnippetContext.atTopLevel: [_ValidSnippetProducer.newInstance],
+ SnippetContext.inClass: [
+ (context) => throw 'Tried to create producer for wrong context',
+ ]
+ });
+ final results = await manager.computeSnippets(request);
+ expect(results, hasLength(1));
+ }
+
+ Future<void> test_validProducers() async {
+ await resolveTestCode('');
+ final request = DartSnippetRequest(
+ unit: testAnalysisResult,
+ offset: 0,
+ );
+
+ final manager = _TestDartSnippetManager({
+ SnippetContext.atTopLevel: [_ValidSnippetProducer.newInstance],
+ });
+ final results = await manager.computeSnippets(request);
+ expect(results, hasLength(1));
+ final snippet = results.single;
+ expect(snippet.prefix, 'mysnip');
+ expect(snippet.label, 'My Test Snippet');
+ }
+}
+
+/// A snippet producer that always returns `false` from [isValid] and throws
+/// if [compute] is called.
+class _NotValidSnippetProducer extends SnippetProducer {
+ _NotValidSnippetProducer(DartSnippetRequest request) : super(request);
+
+ @override
+ Future<Snippet> compute() {
+ throw UnsupportedError(
+ 'compute should not be called for a producer '
+ 'that returned false from isValid',
+ );
+ }
+
+ @override
+ Future<bool> isValid() async => false;
+
+ static _NotValidSnippetProducer newInstance(DartSnippetRequest request) =>
+ _NotValidSnippetProducer(request);
+}
+
+class _TestDartSnippetManager extends DartSnippetManager {
+ @override
+ final Map<SnippetContext, List<SnippetProducerGenerator>> producerGenerators;
+
+ _TestDartSnippetManager(this.producerGenerators);
+}
+
+/// A snippet producer that always returns `true` from [isValid] and a simple
+/// snippet from [compute].
+class _ValidSnippetProducer extends SnippetProducer {
+ _ValidSnippetProducer(DartSnippetRequest request) : super(request);
+
+ @override
+ Future<Snippet> compute() async {
+ return Snippet(
+ 'mysnip',
+ 'My Test Snippet',
+ 'This is a test snippet',
+ SourceChange('message'),
+ );
+ }
+
+ @override
+ Future<bool> isValid() async => true;
+
+ static _ValidSnippetProducer newInstance(DartSnippetRequest request) =>
+ _ValidSnippetProducer(request);
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/snippet_request_test.dart b/pkg/analysis_server/test/services/snippets/dart/snippet_request_test.dart
new file mode 100644
index 0000000..fd6a240
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/snippet_request_test.dart
@@ -0,0 +1,495 @@
+// 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/snippet_manager.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 'test_support.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(SnippetRequestTest);
+ });
+}
+
+@reflectiveTest
+class SnippetRequestTest extends AbstractSingleUnitTest {
+ SnippetRequestTest() {
+ verifyNoTestUnitErrors = false;
+ }
+
+ Future<void> test_expression_constructor() async {
+ await testRequest(r'''
+final a = new [[^]]
+''', SnippetContext.inExpressionOrStatement);
+ }
+
+ Future<void> test_expression_constructorName() async {
+ await testRequest(r'''
+class A {
+ A.foo();
+}
+final a = new A.[[fo^]]
+''', SnippetContext.inExpressionOrStatement);
+ }
+
+ Future<void> test_inAnnotation() async {
+ await testRequest(r'''
+@[[depre^]]
+class A {}
+''', SnippetContext.inExpressionOrStatement);
+ }
+
+ Future<void> test_inBlock_forBody() async {
+ await testRequest(r'''
+foo() {
+ for (var i = 0; i < 10; i++) {
+ [[^]]
+ }
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inClass_atEnd() async {
+ await testRequest(r'''
+class A {
+ foo() {}
+
+ [[^]]
+}
+''', SnippetContext.inClass);
+ }
+
+ Future<void> test_inClass_atEnd_partialIdentifier() async {
+ await testRequest(r'''
+class A {
+ foo() {}
+
+ [[mysnip^]]
+}
+''', SnippetContext.inClass);
+ }
+
+ Future<void> test_inClass_atStart() async {
+ await testRequest(r'''
+class A {
+ [[^]]
+
+ foo() {}
+}
+''', SnippetContext.inClass);
+ }
+
+ Future<void> test_inClass_atStart_partialIdentifier() async {
+ await testRequest(r'''
+class A {
+ [[mysnip^]]
+
+ foo() {}
+}
+''', SnippetContext.inClass);
+ }
+
+ Future<void> test_inClass_betweenMembers() async {
+ await testRequest(r'''
+class A {
+ foo() {}
+
+ [[^]]
+
+ bar() {}
+}
+''', SnippetContext.inClass);
+ }
+
+ Future<void> test_inClass_betweenMembers_partialIdentifier() async {
+ await testRequest(r'''
+class A {
+ foo() {}
+
+ [[mysnip^]]
+
+ bar() {}
+}
+''', SnippetContext.inClass);
+ }
+
+ Future<void> test_inClass_empty() async {
+ await testRequest(r'''
+class A {
+ [[^]]
+}
+''', SnippetContext.inClass);
+ }
+
+ Future<void> test_inClass_empty_partialIdentifier() async {
+ await testRequest(r'''
+class A {
+ [[mysnip^]]
+}
+''', SnippetContext.inClass);
+ }
+
+ Future<void> test_inComment_dartDoc() async {
+ await testRequest(r'''
+/// [[^]]
+class A {}
+''', SnippetContext.inComment);
+ }
+
+ Future<void> test_inComment_dartDoc_reference_member() async {
+ await testRequest(r'''
+class A {
+ /// [ [[A^]] ]
+ foo() {}
+}
+''', SnippetContext.inComment);
+ }
+
+ Future<void> test_inComment_dartDoc_reference_topLevel() async {
+ await testRequest(r'''
+/// [ [[A^]] ]
+class A {}
+''', SnippetContext.inComment);
+ }
+
+ Future<void> test_inComment_multiline_member() async {
+ await testRequest(r'''
+class A {
+ /*
+ * [[^]]
+ */
+ foo() {}
+}
+''', SnippetContext.inComment);
+ }
+
+ Future<void> test_inComment_multiline_topLevel() async {
+ await testRequest(r'''
+/*
+ * [[^]]
+ */
+class A {}
+''', SnippetContext.inComment);
+ }
+
+ Future<void> test_inComment_singleLine_member() async {
+ await testRequest(r'''
+class A {
+ // [[^]]
+ foo () {}
+}
+''', SnippetContext.inComment);
+ }
+
+ Future<void> test_inComment_singleLine_topLevel() async {
+ await testRequest(r'''
+// [[^]]
+class A {}
+''', SnippetContext.inComment);
+ }
+
+ Future<void> test_inExpression_functionCall() async {
+ await testRequest(r'''
+foo() {
+ print([[^]]
+}
+''', SnippetContext.inExpressionOrStatement);
+ }
+
+ Future<void> test_inExtension() async {
+ await testRequest(r'''
+extension on Object {
+ [[^]]
+}
+''', SnippetContext.inClass);
+ }
+
+ Future<void> test_inFunction_atEnd() async {
+ await testRequest(r'''
+foo() {
+ var a = 1;
+ [[^]]
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inFunction_atEnd_partialIdentifier() async {
+ await testRequest(r'''
+foo() {
+ var a = 1;
+ [[mysnip^]]
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inFunction_atStart() async {
+ await testRequest(r'''
+foo() {
+ [[^]]
+ var a = 1;
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inFunction_atStart_partialIdentifier() async {
+ await testRequest(r'''
+foo() {
+ [[mysnip^]]
+ var a = 1;
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inFunction_betweenStatements() async {
+ await testRequest(r'''
+foo() {
+ var a = 1;
+ [[^]]
+ var b = 1;
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inFunction_betweenStatements_partialIdentifier() async {
+ await testRequest(r'''
+foo() {
+ var a = 1;
+ [[mysnip^]]
+ var b = 1;
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inFunction_empty() async {
+ await testRequest(r'''
+foo() {
+ [[^]]
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inFunction_empty_partialIdentifier() async {
+ await testRequest(r'''
+foo() {
+ [[mysnip^]]
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inMethod_atEnd() async {
+ await testRequest(r'''
+class A {
+ foo() {
+ var a = 1;
+ [[^]]
+ }
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inMethod_atEnd_partialIdentifier() async {
+ await testRequest(r'''
+class A {
+ foo() {
+ var a = 1;
+ [[mysnip^]]
+ }
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inMethod_atStart() async {
+ await testRequest(r'''
+class A {
+ foo() {
+ [[^]]
+ var a = 1;
+ }
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inMethod_atStart_partialIdentifier() async {
+ await testRequest(r'''
+class A {
+ foo() {
+ [[mysnip^]]
+ var a = 1;
+ }
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inMethod_betweenStatements() async {
+ await testRequest(r'''
+class A {
+ foo() {
+ var a = 1;
+ [[^]]
+ var b = 1;
+ }
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inMethod_betweenStatements_partialIdentifier() async {
+ await testRequest(r'''
+class A {
+ foo() {
+ var a = 1;
+ [[mysnip^]]
+ var b = 1;
+ }
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inMethod_empty() async {
+ await testRequest(r'''
+class A {
+ foo() {
+ [[^]]
+ }
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inMethod_empty_partialIdentifier() async {
+ await testRequest(r'''
+class A {
+ foo() {
+ [[mysnip^]]
+ }
+}
+''', SnippetContext.inBlock);
+ }
+
+ Future<void> test_inMixin() async {
+ await testRequest(r'''
+mixin A {
+ [[^]]
+}
+''', SnippetContext.inClass);
+ }
+
+ Future<void> test_inStatement_forCondition() async {
+ await testRequest(r'''
+foo() {
+ for (var i = [[^]]
+}
+''', SnippetContext.inExpressionOrStatement);
+ }
+
+ Future<void> test_inStatement_variableDeclaration() async {
+ await testRequest(r'''
+foo() {
+ var a = [[^]]
+}
+''', SnippetContext.inExpressionOrStatement);
+ }
+
+ Future<void> test_inString() async {
+ await testRequest(r'''
+const a = '[[^]]';
+''', SnippetContext.inString);
+ }
+
+ Future<void> test_inString_raw() async {
+ await testRequest(r'''
+const a = r'[[^]]';
+''', SnippetContext.inString);
+ }
+
+ Future<void> test_inString_unterminated() async {
+ await testRequest(r'''
+const a = r'[[^]]
+''', SnippetContext.inString);
+ }
+
+ Future<void> test_topLevel_atEnd() async {
+ await testRequest(r'''
+class A {}
+
+[[^]]
+''', SnippetContext.atTopLevel);
+ }
+
+ Future<void> test_topLevel_atEnd_partialIdentifier() async {
+ await testRequest(r'''
+class A {}
+
+[[mysnip^]]
+''', SnippetContext.atTopLevel);
+ }
+
+ Future<void> test_topLevel_atStart() async {
+ await testRequest(r'''
+[[^]]
+
+class A {}
+''', SnippetContext.atTopLevel);
+ }
+
+ Future<void> test_topLevel_atStart_partialIdentifier() async {
+ await testRequest(r'''
+[[mysnip^]]
+
+class A {}
+''', SnippetContext.atTopLevel);
+ }
+
+ Future<void> test_topLevel_betweenClasses() async {
+ await testRequest(r'''
+class A {}
+
+[[^]]
+
+class B {}
+''', SnippetContext.atTopLevel);
+ }
+
+ Future<void> test_topLevel_betweenClasses_partialIdentifier() async {
+ await testRequest(r'''
+class A {}
+
+[[mysnip^]]
+
+class B {}
+''', SnippetContext.atTopLevel);
+ }
+
+ Future<void> test_topLevel_empty() async {
+ await testRequest('[[^]]', SnippetContext.atTopLevel);
+ }
+
+ Future<void> test_topLevel_empty_partialIdentifier() async {
+ await testRequest('[[mysnip^]]', SnippetContext.atTopLevel);
+ }
+
+ /// Checks that [code] produces a context of [expectedContext] where the
+ /// character '^' in [code] represents the supplied offset and the range
+ /// surrounded `[[` by brackets `]]` is the expected replacement range.
+ ///
+ /// `^`, `[[` and `]]` will be removed from the code before resolving.
+ Future<void> testRequest(String code, SnippetContext expectedContext) async {
+ code = normalizeNewlinesForPlatform(code);
+ final offset = offsetFromMarker(code);
+ final expectedReplacementRange = rangeFromMarkers(code);
+ await resolveTestCode(withoutMarkers(code));
+
+ final request = DartSnippetRequest(
+ unit: testAnalysisResult,
+ offset: offset,
+ );
+
+ expect(request.filePath, testFile);
+ expect(request.offset, offset);
+ expect(request.context, expectedContext);
+ expect(request.replacementRange, expectedReplacementRange);
+ }
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/test_all.dart b/pkg/analysis_server/test/services/snippets/dart/test_all.dart
new file mode 100644
index 0000000..907e05a
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/test_all.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '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;
+
+void main() {
+ defineReflectiveSuite(() {
+ flutter_snippet_producers.main();
+ snippet_manager.main();
+ snippet_request.main();
+ }, name: 'dart');
+}
diff --git a/pkg/analysis_server/test/services/snippets/dart/test_support.dart b/pkg/analysis_server/test/services/snippets/dart/test_support.dart
new file mode 100644
index 0000000..9f2d740
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/dart/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/services/snippets/test_all.dart b/pkg/analysis_server/test/services/snippets/test_all.dart
new file mode 100644
index 0000000..899f5ae
--- /dev/null
+++ b/pkg/analysis_server/test/services/snippets/test_all.dart
@@ -0,0 +1,13 @@
+// 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:test_reflective_loader/test_reflective_loader.dart';
+
+import 'dart/test_all.dart' as dart_all;
+
+void main() {
+ defineReflectiveSuite(() {
+ dart_all.main();
+ }, name: 'snippets');
+}
diff --git a/pkg/analysis_server/test/services/test_all.dart b/pkg/analysis_server/test/services/test_all.dart
index 954023d..a6feeba 100644
--- a/pkg/analysis_server/test/services/test_all.dart
+++ b/pkg/analysis_server/test/services/test_all.dart
@@ -9,6 +9,7 @@
import 'linter/test_all.dart' as linter_all;
import 'refactoring/test_all.dart' as refactoring_all;
import 'search/test_all.dart' as search_all;
+import 'snippets/test_all.dart' as snippets_all;
void main() {
defineReflectiveSuite(() {
@@ -17,5 +18,6 @@
linter_all.main();
refactoring_all.main();
search_all.main();
+ snippets_all.main();
});
}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_class_to_enum_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_class_to_enum_test.dart
new file mode 100644
index 0000000..931cc73
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_class_to_enum_test.dart
@@ -0,0 +1,516 @@
+// 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/assist.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'assist_processor.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ConvertClassToEnumTest);
+ });
+}
+
+@reflectiveTest
+class ConvertClassToEnumTest extends AssistProcessorTest {
+ @override
+ AssistKind get kind => DartAssistKind.CONVERT_CLASS_TO_ENUM;
+
+ Future<void> test_extends_object_privateClass() async {
+ await resolveTestCode('''
+class _E extends Object {
+ static const _E c = _E();
+
+ const _E();
+}
+''');
+ await assertHasAssistAt('E extends', '''
+enum _E {
+ c
+}
+''');
+ }
+
+ Future<void> test_extends_object_publicClass() async {
+ await resolveTestCode('''
+class E extends Object {
+ static const E c = E._();
+
+ const E._();
+}
+''');
+ await assertHasAssistAt('E extends', '''
+enum E {
+ c._();
+
+ const E._();
+}
+''');
+ }
+
+ Future<void> test_index_namedIndex_first_privateClass() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c0 = _E(0, 'a');
+ static const _E c1 = _E(1, 'b');
+
+ final int index;
+
+ final String code;
+
+ const _E(this.index, this.code);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum _E {
+ c0('a'),
+ c1('b');
+
+ final String code;
+
+ const _E(this.code);
+}
+''');
+ }
+
+ Future<void> test_index_namedIndex_last_privateClass() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c0 = _E('a', 0);
+ static const _E c1 = _E('b', 1);
+
+ final String code;
+
+ final int index;
+
+ const _E(this.code, this.index);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum _E {
+ c0('a'),
+ c1('b');
+
+ final String code;
+
+ const _E(this.code);
+}
+''');
+ }
+
+ Future<void> test_index_namedIndex_middle_privateClass() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c0 = _E('a', 0, 'b');
+ static const _E c1 = _E('c', 1, 'd');
+
+ final String first;
+
+ final int index;
+
+ final String last;
+
+ const _E(this.first, this.index, this.last);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum _E {
+ c0('a', 'b'),
+ c1('c', 'd');
+
+ final String first;
+
+ final String last;
+
+ const _E(this.first, this.last);
+}
+''');
+ }
+
+ Future<void> test_index_namedIndex_only_outOfOrder() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c0 = _E(1);
+ static const _E c1 = _E(0);
+
+ final int index;
+
+ const _E(this.index);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum _E {
+ c1,
+ c0
+}
+''');
+ }
+
+ Future<void> test_index_namedIndex_only_privateClass() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c0 = _E(0);
+ static const _E c1 = _E(1);
+
+ final int index;
+
+ const _E(this.index);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum _E {
+ c0,
+ c1
+}
+''');
+ }
+
+ Future<void> test_index_namedIndex_only_publicClass() async {
+ await resolveTestCode('''
+class E {
+ static const E c0 = E._(0);
+ static const E c1 = E._(1);
+
+ final int index;
+
+ const E._(this.index);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum E {
+ c0._(),
+ c1._();
+
+ const E._();
+}
+''');
+ }
+
+ Future<void> test_index_notNamedIndex_privateClass() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c0 = _E(0);
+ static const _E c1 = _E(1);
+
+ final int value;
+
+ const _E(this.value);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum _E {
+ c0(0),
+ c1(1);
+
+ final int value;
+
+ const _E(this.value);
+}
+''');
+ }
+
+ Future<void> test_index_notNamedIndex_publicClass() async {
+ await resolveTestCode('''
+class E {
+ static const E c0 = E._(0);
+ static const E c1 = E._(1);
+
+ final int value;
+
+ const E._(this.value);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum E {
+ c0._(0),
+ c1._(1);
+
+ final int value;
+
+ const E._(this.value);
+}
+''');
+ }
+
+ Future<void> test_invalid_abstractClass() async {
+ await resolveTestCode('''
+abstract class E {}
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_invalid_constructorUsedInConstructor() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c = _E();
+
+ // ignore: unused_element
+ const _E({_E e = const _E()});
+}
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_invalid_constructorUsedOutsideClass() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c = _E();
+
+ const _E();
+}
+_E get e => _E();
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_invalid_extended() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c = _E();
+
+ const _E();
+}
+class F extends _E {}
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_invalid_extends_notObject() async {
+ await resolveTestCode('''
+class E extends C {
+ static const E c = E._();
+
+ const E._();
+}
+class C {
+ const C();
+}
+''');
+ await assertNoAssistAt('E extends');
+ }
+
+ Future<void> test_invalid_hasPart() async {
+ // Change this test if the assist becomes able to look for references to the
+ // class and its constructors in part files.
+ newFile('$testPackageLibPath/a.dart', content: '''
+part of 'test.dart';
+''');
+ await resolveTestCode('''
+part 'a.dart';
+
+class E {
+ static const E c = E._();
+
+ const E._();
+}
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_invalid_implemented() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c = _E();
+
+ const _E();
+}
+class F implements _E {}
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_invalid_indexFieldNotSequential() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c0 = _E(0);
+ static const _E c1 = _E(3);
+
+ final int index;
+
+ const _E(this.index);
+}
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_invalid_multipleConstantsInSameFieldDeclaration() async {
+ // Change this test if support is added to cover cases where multiple
+ // constants are defined in a single field declaration.
+ await resolveTestCode('''
+class _E {
+ static const _E c0 = _E('a'), c1 = _E('b');
+
+ final String s;
+
+ const _E(this.s);
+}
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_invalid_nonConstConstructor() async {
+ await resolveTestCode('''
+class _E {
+ static _E c = _E();
+
+ _E();
+}
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_invalid_overrides_equal() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c = _E();
+
+ const _E();
+
+ @override
+ int get hashCode => 0;
+}
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_invalid_overrides_hashCode() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c = _E();
+
+ const _E();
+
+ @override
+ bool operator ==(Object other) => true;
+}
+''');
+ await assertNoAssistAt('E {');
+ }
+
+ Future<void> test_minimal_privateClass() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c = _E();
+
+ const _E();
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum _E {
+ c
+}
+''');
+ }
+
+ Future<void> test_minimal_publicClass() async {
+ await resolveTestCode('''
+class E {
+ static const E c = E._();
+
+ const E._();
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum E {
+ c._();
+
+ const E._();
+}
+''');
+ }
+
+ Future<void> test_noIndex_int_privateClass() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c0 = _E(2);
+ static const _E c1 = _E(4);
+
+ final int count;
+
+ const _E(this.count);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum _E {
+ c0(2),
+ c1(4);
+
+ final int count;
+
+ const _E(this.count);
+}
+''');
+ }
+
+ Future<void> test_noIndex_int_publicClass() async {
+ await resolveTestCode('''
+class E {
+ static const E c0 = E._(2);
+ static const E c1 = E._(4);
+
+ final int count;
+
+ const E._(this.count);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum E {
+ c0._(2),
+ c1._(4);
+
+ final int count;
+
+ const E._(this.count);
+}
+''');
+ }
+
+ Future<void> test_noIndex_notInt_privateClass() async {
+ await resolveTestCode('''
+class _E {
+ static const _E c0 = _E('c0');
+ static const _E c1 = _E('c1');
+
+ final String name;
+
+ const _E(this.name);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum _E {
+ c0('c0'),
+ c1('c1');
+
+ final String name;
+
+ const _E(this.name);
+}
+''');
+ }
+
+ Future<void> test_noIndex_notInt_publicClass() async {
+ await resolveTestCode('''
+class E {
+ static const E c0 = E._('c0');
+ static const E c1 = E._('c1');
+
+ final String name;
+
+ const E._(this.name);
+}
+''');
+ await assertHasAssistAt('E {', '''
+enum E {
+ c0._('c0'),
+ c1._('c1');
+
+ final String name;
+
+ const E._(this.name);
+}
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/test_all.dart b/pkg/analysis_server/test/src/services/correction/assist/test_all.dart
index e61b6f7..2c05865 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/test_all.dart
@@ -9,6 +9,7 @@
import 'add_return_type_test.dart' as add_return_type;
import 'add_type_annotation_test.dart' as add_type_annotation;
import 'assign_to_local_variable_test.dart' as assign_to_local_variable;
+import 'convert_class_to_enum_test.dart' as convert_class_to_enum;
import 'convert_class_to_mixin_test.dart' as convert_class_to_mixin;
import 'convert_documentation_into_block_test.dart'
as convert_documentation_into_block;
@@ -97,6 +98,7 @@
add_return_type.main();
add_type_annotation.main();
assign_to_local_variable.main();
+ convert_class_to_enum.main();
convert_class_to_mixin.main();
convert_documentation_into_block.main();
convert_documentation_into_line.main();
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 29f3b33..9e07497 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -44,16 +44,17 @@
Token get beginToken => _strings.beginToken!;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..addAll(_strings);
-
- @override
Token get endToken => _strings.endToken!;
@override
NodeListImpl<StringLiteral> get strings => _strings;
@override
+ ChildEntities get _childEntities {
+ return ChildEntities()..addNodeList('strings', strings);
+ }
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitAdjacentStrings(this);
@override
@@ -126,17 +127,11 @@
]..sort(AstNode.LEXICAL_ORDER);
}
- /// Return a holder of child entities that subclasses can add to.
+ @override
ChildEntities get _childEntities {
- ChildEntities result = ChildEntities();
- if (_commentIsBeforeAnnotations()) {
- result
- ..add(_comment)
- ..addAll(_metadata);
- } else {
- result.addAll(sortedCommentAndAnnotations);
- }
- return result;
+ return ChildEntities()
+ ..addNode('documentationComment', documentationComment)
+ ..addNodeList('metadata', metadata);
}
@override
@@ -237,15 +232,6 @@
Token get beginToken => atSign;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(atSign)
- ..add(_name)
- ..add(_typeArguments)
- ..add(period)
- ..add(_constructorName)
- ..add(_arguments);
-
- @override
SimpleIdentifierImpl? get constructorName => _constructorName;
set constructorName(SimpleIdentifier? name) {
@@ -296,6 +282,17 @@
}
@override
+ ChildEntities get _childEntities {
+ return ChildEntities()
+ ..addToken('atSign', atSign)
+ ..addNode('name', name)
+ ..addNode('typeArguments', typeArguments)
+ ..addToken('period', period)
+ ..addNode('constructorName', constructorName)
+ ..addNode('arguments', arguments);
+ }
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitAnnotation(this);
@override
@@ -349,13 +346,6 @@
@override
Token get beginToken => leftParenthesis;
- @override
- // TODO(paulberry): Add commas.
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(leftParenthesis)
- ..addAll(_arguments)
- ..add(rightParenthesis);
-
List<ParameterElement?>? get correspondingStaticParameters =>
_correspondingStaticParameters;
@@ -371,6 +361,13 @@
Token get endToken => rightParenthesis;
@override
+ // TODO(paulberry): Add commas.
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNodeList('arguments', arguments)
+ ..addToken('rightParenthesis', rightParenthesis);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitArgumentList(this);
@override
@@ -429,12 +426,6 @@
Token get beginToken => _expression.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_expression)
- ..add(asOperator)
- ..add(_type);
-
- @override
Token get endToken => _type.endToken;
@override
@@ -455,6 +446,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('expression', expression)
+ ..addToken('asOperator', asOperator)
+ ..addNode('type', type);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitAsExpression(this);
@override
@@ -500,15 +497,6 @@
Token get beginToken => assertKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(assertKeyword)
- ..add(leftParenthesis)
- ..add(_condition)
- ..add(comma)
- ..add(_message)
- ..add(rightParenthesis);
-
- @override
ExpressionImpl get condition => _condition;
set condition(Expression condition) {
@@ -526,6 +514,15 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('assertKeyword', assertKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('condition', condition)
+ ..addToken('comma', comma)
+ ..addNode('message', message)
+ ..addToken('rightParenthesis', rightParenthesis);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitAssertInitializer(this);
@override
@@ -573,16 +570,6 @@
Token get beginToken => assertKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(assertKeyword)
- ..add(leftParenthesis)
- ..add(_condition)
- ..add(comma)
- ..add(_message)
- ..add(rightParenthesis)
- ..add(semicolon);
-
- @override
ExpressionImpl get condition => _condition;
set condition(Expression condition) {
@@ -600,6 +587,16 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('assertKeyword', assertKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('condition', condition)
+ ..addToken('comma', comma)
+ ..addNode('message', message)
+ ..addToken('rightParenthesis', rightParenthesis)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitAssertStatement(this);
@override
@@ -644,12 +641,6 @@
Token get beginToken => _leftHandSide.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_leftHandSide)
- ..add(operator)
- ..add(_rightHandSide);
-
- @override
Token get endToken => _rightHandSide.endToken;
@override
@@ -670,6 +661,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('leftHandSide', leftHandSide)
+ ..addToken('operator', operator)
+ ..addNode('rightHandSide', rightHandSide);
+
+ @override
AstNode? get _nullShortingExtensionCandidate => parent;
/// If the AST structure has been resolved, and the function being invoked is
@@ -724,6 +721,10 @@
Map<String, Object>? _propertyMap;
@override
+ Iterable<SyntacticEntity> get childEntities =>
+ _childEntities.syntacticEntities;
+
+ @override
int get end => offset + length;
@override
@@ -736,6 +737,11 @@
return endToken.offset + endToken.length - beginToken.offset;
}
+ /// Return properties (tokens and nodes) of this node, with names, in the
+ /// order in which these entities should normally appear, not necessary in
+ /// the order they really are (because of recovery).
+ Iterable<ChildEntity> get namedChildEntities => _childEntities.entities;
+
@override
int get offset {
final beginToken = this.beginToken;
@@ -756,6 +762,8 @@
return root;
}
+ ChildEntities get _childEntities => ChildEntities();
+
void detachFromParent() {
_parent = null;
}
@@ -845,11 +853,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(awaitKeyword)
- ..add(_expression);
-
- @override
Token get endToken => _expression.endToken;
@override
@@ -863,6 +866,11 @@
Precedence get precedence => Precedence.prefix;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('awaitKeyword', awaitKeyword)
+ ..addNode('expression', expression);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitAwaitExpression(this);
@override
@@ -905,12 +913,6 @@
Token get beginToken => _leftOperand.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_leftOperand)
- ..add(operator)
- ..add(_rightOperand);
-
- @override
Token get endToken => _rightOperand.endToken;
@override
@@ -931,6 +933,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('leftOperand', leftOperand)
+ ..addToken('operator', operator)
+ ..addNode('rightOperand', rightOperand);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitBinaryExpression(this);
@override
@@ -983,12 +991,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(keyword)
- ..add(star)
- ..add(_block);
-
- @override
Token get endToken => _block.endToken;
@override
@@ -1001,6 +1003,12 @@
bool get isSynchronous => keyword?.lexeme != Keyword.ASYNC.lexeme;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('keyword', keyword)
+ ..addToken('star', star)
+ ..addNode('block', block);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitBlockFunctionBody(this);
@override
@@ -1038,18 +1046,18 @@
Token get beginToken => leftBracket;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(leftBracket)
- ..addAll(_statements)
- ..add(rightBracket);
-
- @override
Token get endToken => rightBracket;
@override
NodeListImpl<Statement> get statements => _statements;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('leftBracket', leftBracket)
+ ..addNodeList('statements', statements)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitBlock(this);
@override
@@ -1078,15 +1086,16 @@
Token get beginToken => literal;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()..add(literal);
-
- @override
Token get endToken => literal;
@override
bool get isSynthetic => literal.isSynthetic;
@override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('literal', literal);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitBooleanLiteral(this);
@override
@@ -1131,12 +1140,6 @@
Token get beginToken => breakKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(breakKeyword)
- ..add(_label)
- ..add(semicolon);
-
- @override
Token get endToken => semicolon;
@override
@@ -1147,6 +1150,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('breakKeyword', breakKeyword)
+ ..addNode('label', label)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitBreakStatement(this);
@override
@@ -1192,11 +1201,6 @@
NodeListImpl<Expression> get cascadeSections => _cascadeSections;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_target)
- ..addAll(_cascadeSections);
-
- @override
Token get endToken => _cascadeSections.endToken!;
@override
@@ -1215,6 +1219,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('target', target)
+ ..addNodeList('cascadeSections', cascadeSections);
+
+ @override
AstNode? get _nullShortingExtensionCandidate => null;
@override
@@ -1316,18 +1325,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(onKeyword)
- ..add(_exceptionType)
- ..add(catchKeyword)
- ..add(leftParenthesis)
- ..add(_exceptionParameter)
- ..add(comma)
- ..add(_stackTraceParameter)
- ..add(rightParenthesis)
- ..add(_body);
-
- @override
Token get endToken => _body.endToken;
@override
@@ -1352,6 +1349,18 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('onKeyword', onKeyword)
+ ..addNode('exceptionType', exceptionType)
+ ..addToken('catchKeyword', catchKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('exceptionParameter', exceptionParameter)
+ ..addToken('comma', comma)
+ ..addNode('stackTraceParameter', stackTraceParameter)
+ ..addToken('rightParenthesis', rightParenthesis)
+ ..addNode('body', body);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitCatchClause(this);
@override
@@ -1364,28 +1373,81 @@
}
/// Helper class to allow iteration of child entities of an AST node.
-class ChildEntities
- with IterableMixin<SyntacticEntity>
- implements Iterable<SyntacticEntity> {
+class ChildEntities {
/// The list of child entities to be iterated over.
- final List<SyntacticEntity> _entities = [];
+ final List<ChildEntity> entities = [];
- @override
- Iterator<SyntacticEntity> get iterator => _entities.iterator;
+ List<SyntacticEntity> get syntacticEntities {
+ var result = <SyntacticEntity>[];
+ for (var entity in entities) {
+ var entityValue = entity.value;
+ if (entityValue is SyntacticEntity) {
+ result.add(entityValue);
+ } else if (entityValue is List<Object>) {
+ for (var element in entityValue) {
+ if (element is SyntacticEntity) {
+ result.add(element);
+ }
+ }
+ }
+ }
- /// Add an AST node or token as the next child entity, if it is not `null`.
- void add(SyntacticEntity? entity) {
- if (entity != null) {
- _entities.add(entity);
+ var needsSorting = false;
+ int? lastOffset;
+ for (var entity in result) {
+ if (lastOffset != null && lastOffset > entity.offset) {
+ needsSorting = true;
+ break;
+ }
+ lastOffset = entity.offset;
+ }
+
+ if (needsSorting) {
+ result.sort((a, b) => a.offset - b.offset);
+ }
+
+ return result;
+ }
+
+ void addAll(ChildEntities other) {
+ entities.addAll(other.entities);
+ }
+
+ void addNode(String name, AstNode? value) {
+ if (value != null) {
+ entities.add(
+ ChildEntity(name, value),
+ );
}
}
- /// Add the given items as the next child entities, if [items] is not `null`.
- void addAll(Iterable<SyntacticEntity>? items) {
- if (items != null) {
- _entities.addAll(items);
+ void addNodeList(String name, List<AstNode> value) {
+ entities.add(
+ ChildEntity(name, value),
+ );
+ }
+
+ void addToken(String name, Token? value) {
+ if (value != null) {
+ entities.add(
+ ChildEntity(name, value),
+ );
}
}
+
+ void addTokenList(String name, List<Token> value) {
+ entities.add(
+ ChildEntity(name, value),
+ );
+ }
+}
+
+/// A named child of an [AstNode], usually a token, node, or a list of nodes.
+class ChildEntity {
+ final String name;
+ final Object value;
+
+ ChildEntity(this.name, this.value);
}
/// The declaration of a class.
@@ -1455,22 +1517,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(abstractKeyword)
- ..add(macroKeyword)
- ..add(augmentKeyword)
- ..add(classKeyword)
- ..add(_name)
- ..add(_typeParameters)
- ..add(_extendsClause)
- ..add(_withClause)
- ..add(_implementsClause)
- ..add(_nativeClause)
- ..add(leftBracket)
- ..addAll(members)
- ..add(rightBracket);
-
- @override
ClassElement? get declaredElement => _name.staticElement as ClassElement?;
@override
@@ -1503,6 +1549,22 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('abstractKeyword', abstractKeyword)
+ ..addToken('macroKeyword', macroKeyword)
+ ..addToken('augmentKeyword', augmentKeyword)
+ ..addToken('classKeyword', classKeyword)
+ ..addNode('name', name)
+ ..addNode('typeParameters', typeParameters)
+ ..addNode('extendsClause', extendsClause)
+ ..addNode('withClause', withClause)
+ ..addNode('implementsClause', implementsClause)
+ ..addNode('nativeClause', nativeClause)
+ ..addToken('leftBracket', leftBracket)
+ ..addNodeList('members', members)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitClassDeclaration(this);
@override
@@ -1710,20 +1772,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(typedefKeyword)
- ..add(_name)
- ..add(_typeParameters)
- ..add(equals)
- ..add(abstractKeyword)
- ..add(macroKeyword)
- ..add(augmentKeyword)
- ..add(_superclass)
- ..add(_withClause)
- ..add(_implementsClause)
- ..add(semicolon);
-
- @override
ClassElement? get declaredElement => _name.staticElement as ClassElement?;
@override
@@ -1768,6 +1816,20 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('typedefKeyword', typedefKeyword)
+ ..addNode('name', name)
+ ..addNode('typeParameters', typeParameters)
+ ..addToken('equals', equals)
+ ..addToken('abstractKeyword', abstractKeyword)
+ ..addToken('macroKeyword', macroKeyword)
+ ..addToken('augmentKeyword', augmentKeyword)
+ ..addNode('superclass', superclass)
+ ..addNode('withClause', withClause)
+ ..addNode('implementsClause', implementsClause)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitClassTypeAlias(this);
@override
@@ -1848,10 +1910,6 @@
Token get beginToken => tokens[0];
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..addAll(tokens);
-
- @override
Token get endToken => tokens[tokens.length - 1];
@override
@@ -1867,6 +1925,10 @@
NodeListImpl<CommentReference> get references => _references;
@override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addTokenList('tokens', tokens);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitComment(this);
@override
@@ -1920,11 +1982,6 @@
Token get beginToken => newKeyword ?? _expression.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(newKeyword)
- ..add(_expression);
-
- @override
Token get endToken => _expression.endToken;
@override
@@ -1944,6 +2001,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('newKeyword', newKeyword)
+ ..addNode('expression', expression);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitCommentReference(this);
@override
@@ -2048,19 +2110,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities {
- ChildEntities result = ChildEntities()..add(_scriptTag);
- if (_directivesAreBeforeDeclarations) {
- result
- ..addAll(_directives)
- ..addAll(_declarations);
- } else {
- result.addAll(sortedDirectivesAndDeclarations);
- }
- return result;
- }
-
- @override
NodeListImpl<CompilationUnitMember> get declarations => _declarations;
@override
@@ -2111,6 +2160,14 @@
]..sort(AstNode.LEXICAL_ORDER);
}
+ @override
+ ChildEntities get _childEntities {
+ return ChildEntities()
+ ..addNode('scriptTag', scriptTag)
+ ..addNodeList('directives', directives)
+ ..addNodeList('declarations', declarations);
+ }
+
/// Return `true` if all of the directives are lexically before any
/// declarations.
bool get _directivesAreBeforeDeclarations {
@@ -2211,14 +2268,6 @@
Token get beginToken => _condition.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_condition)
- ..add(question)
- ..add(_thenExpression)
- ..add(colon)
- ..add(_elseExpression);
-
- @override
ExpressionImpl get condition => _condition;
set condition(Expression expression) {
@@ -2246,6 +2295,14 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('condition', condition)
+ ..addToken('question', question)
+ ..addNode('thenExpression', thenExpression)
+ ..addToken('colon', colon)
+ ..addNode('elseExpression', elseExpression);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitConditionalExpression(this);
@@ -2300,16 +2357,6 @@
Token get beginToken => ifKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(ifKeyword)
- ..add(leftParenthesis)
- ..add(_name)
- ..add(equalToken)
- ..add(_value)
- ..add(rightParenthesis)
- ..add(_uri);
-
- @override
Token get endToken => _uri.endToken;
@override
@@ -2334,6 +2381,16 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('ifKeyword', ifKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('name', name)
+ ..addToken('equalToken', equalToken)
+ ..addNode('value', value)
+ ..addToken('rightParenthesis', rightParenthesis)
+ ..addNode('uri', uri);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitConfiguration(this);
@override
@@ -2478,20 +2535,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(externalKeyword)
- ..add(constKeyword)
- ..add(factoryKeyword)
- ..add(_returnType)
- ..add(period)
- ..add(_name)
- ..add(_parameters)
- ..add(separator)
- ..addAll(initializers)
- ..add(_redirectedConstructor)
- ..add(_body);
-
- @override
Token get endToken {
return _body.endToken;
}
@@ -2536,6 +2579,20 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('externalKeyword', externalKeyword)
+ ..addToken('constKeyword', constKeyword)
+ ..addToken('factoryKeyword', factoryKeyword)
+ ..addNode('returnType', returnType)
+ ..addToken('period', period)
+ ..addNode('name', name)
+ ..addNode('parameters', parameters)
+ ..addToken('separator', separator)
+ ..addNodeList('initializers', initializers)
+ ..addNode('redirectedConstructor', redirectedConstructor)
+ ..addNode('body', body);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitConstructorDeclaration(this);
@@ -2594,14 +2651,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(thisKeyword)
- ..add(period)
- ..add(_fieldName)
- ..add(equals)
- ..add(_expression);
-
- @override
Token get endToken => _expression.endToken;
@override
@@ -2619,6 +2668,14 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('thisKeyword', thisKeyword)
+ ..addToken('period', period)
+ ..addNode('fieldName', fieldName)
+ ..addToken('equals', equals)
+ ..addNode('expression', expression);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitConstructorFieldInitializer(this);
@@ -2672,12 +2729,6 @@
Token get beginToken => _type.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_type)
- ..add(period)
- ..add(_name);
-
- @override
Token get endToken {
if (_name != null) {
return _name!.endToken;
@@ -2704,6 +2755,12 @@
NamedTypeImpl get type2 => _type;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('type', type)
+ ..addToken('period', period)
+ ..addNode('name', name);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitConstructorName(this);
@override
@@ -2731,10 +2788,6 @@
Token get beginToken => constructorName.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..add(constructorName);
-
- @override
ConstructorNameImpl get constructorName => _constructorName;
set constructorName(ConstructorNameImpl value) {
@@ -2748,6 +2801,10 @@
Precedence get precedence => Precedence.postfix;
@override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addNode('constructorName', constructorName);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitConstructorReference(this);
@@ -2776,12 +2833,12 @@
Token get beginToken => period;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(period)
- ..add(name);
+ Token get endToken => name.token;
@override
- Token get endToken => name.token;
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('period', period)
+ ..addNode('name', name);
@override
E? accept<E>(AstVisitor<E> visitor) {
@@ -2827,12 +2884,6 @@
Token get beginToken => continueKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(continueKeyword)
- ..add(_label)
- ..add(semicolon);
-
- @override
Token get endToken => semicolon;
@override
@@ -2843,6 +2894,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('continueKeyword', continueKeyword)
+ ..addNode('label', label)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitContinueStatement(this);
@override
@@ -2892,12 +2949,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(keyword)
- ..add(_type)
- ..add(_identifier);
-
- @override
LocalVariableElement? get declaredElement {
return _identifier.staticElement as LocalVariableElement;
}
@@ -2931,6 +2982,12 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('keyword', keyword)
+ ..addNode('type', type)
+ ..addNode('identifier', identifier);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitDeclaredIdentifier(this);
@override
@@ -2996,12 +3053,6 @@
Token get beginToken => _parameter.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_parameter)
- ..add(separator)
- ..add(_defaultValue);
-
- @override
Token? get covariantKeyword => null;
@override
@@ -3045,6 +3096,12 @@
Token? get requiredKeyword => null;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('parameter', parameter)
+ ..addToken('separator', separator)
+ ..addNode('defaultValue', defaultValue);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitDefaultFormalParameter(this);
@@ -3138,16 +3195,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(doKeyword)
- ..add(_body)
- ..add(whileKeyword)
- ..add(leftParenthesis)
- ..add(_condition)
- ..add(rightParenthesis)
- ..add(semicolon);
-
- @override
ExpressionImpl get condition => _condition;
set condition(Expression expression) {
@@ -3158,6 +3205,16 @@
Token get endToken => semicolon;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('doKeyword', doKeyword)
+ ..addNode('body', body)
+ ..addToken('whileKeyword', whileKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('condition', condition)
+ ..addToken('rightParenthesis', rightParenthesis)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitDoStatement(this);
@override
@@ -3184,17 +3241,17 @@
Token get beginToken => _components.beginToken!;
@override
- // TODO(paulberry): add "." tokens.
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..addAll(_components);
-
- @override
NodeListImpl<SimpleIdentifier> get components => _components;
@override
Token get endToken => _components.endToken!;
@override
+ // TODO(paulberry): add "." tokens.
+ ChildEntities get _childEntities =>
+ ChildEntities()..addNodeList('components', components);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitDottedName(this);
@override
@@ -3227,10 +3284,11 @@
Token get beginToken => literal;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()..add(literal);
+ Token get endToken => literal;
@override
- Token get endToken => literal;
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('literal', literal);
@override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitDoubleLiteral(this);
@@ -3260,11 +3318,11 @@
Token get beginToken => semicolon;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..add(semicolon);
+ Token get endToken => semicolon;
@override
- Token get endToken => semicolon;
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('semicolon', semicolon);
@override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitEmptyFunctionBody(this);
@@ -3295,16 +3353,16 @@
Token get beginToken => semicolon;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..add(semicolon);
-
- @override
Token get endToken => semicolon;
@override
bool get isSynthetic => semicolon.isSynthetic;
@override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitEmptyStatement(this);
@override
@@ -3339,13 +3397,13 @@
(typeArguments ?? constructorSelector ?? argumentList).beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(typeArguments)
- ..add(constructorSelector)
- ..add(argumentList);
+ Token get endToken => argumentList.endToken;
@override
- Token get endToken => argumentList.endToken;
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('typeArguments', typeArguments)
+ ..addNode('constructorSelector', constructorSelector)
+ ..addNode('argumentList', argumentList);
@override
E? accept<E>(AstVisitor<E> visitor) {
@@ -3387,11 +3445,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(_name)
- ..add(arguments);
-
- @override
FieldElement get declaredElement => _name.staticElement as FieldElement;
@override
@@ -3408,6 +3461,11 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addNode('name', name)
+ ..addNode('arguments', arguments);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitEnumConstantDeclaration(this);
@@ -3486,19 +3544,6 @@
}
@override
- // TODO(brianwilkerson) Add commas?
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(enumKeyword)
- ..add(_name)
- ..add(_typeParameters)
- ..add(_withClause)
- ..add(_implementsClause)
- ..add(leftBracket)
- ..addAll(_constants)
- ..addAll(_members)
- ..add(rightBracket);
-
- @override
NodeListImpl<EnumConstantDeclaration> get constants => _constants;
@override
@@ -3536,6 +3581,19 @@
}
@override
+ // TODO(brianwilkerson) Add commas?
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('enumKeyword', enumKeyword)
+ ..addNode('name', name)
+ ..addNode('typeParameters', typeParameters)
+ ..addNode('withClause', withClause)
+ ..addNode('implementsClause', implementsClause)
+ ..addToken('leftBracket', leftBracket)
+ ..addNodeList('constants', constants)
+ ..addNodeList('members', members)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitEnumDeclaration(this);
@override
@@ -3581,13 +3639,6 @@
combinators, semicolon);
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(keyword)
- ..add(_uri)
- ..addAll(combinators)
- ..add(semicolon);
-
- @override
ExportElement? get element => super.element as ExportElement?;
@override
@@ -3596,6 +3647,13 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('keyword', keyword)
+ ..addNode('uri', uri)
+ ..addNodeList('combinators', combinators)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitExportDirective(this);
@override
@@ -3654,14 +3712,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(keyword)
- ..add(star)
- ..add(functionDefinition)
- ..add(_expression)
- ..add(semicolon);
-
- @override
Token get endToken {
if (semicolon != null) {
return semicolon!;
@@ -3686,6 +3736,14 @@
bool get isSynchronous => keyword?.lexeme != Keyword.ASYNC.lexeme;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('keyword', keyword)
+ ..addToken('star', star)
+ ..addToken('functionDefinition', functionDefinition)
+ ..addNode('expression', expression)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitExpressionFunctionBody(this);
@@ -3817,11 +3875,6 @@
Token get beginToken => _expression.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_expression)
- ..add(semicolon);
-
- @override
Token get endToken {
if (semicolon != null) {
return semicolon!;
@@ -3841,6 +3894,11 @@
_expression.isSynthetic && (semicolon == null || semicolon!.isSynthetic);
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('expression', expression)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitExpressionStatement(this);
@override
@@ -3870,11 +3928,6 @@
Token get beginToken => extendsKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(extendsKeyword)
- ..add(_superclass);
-
- @override
Token get endToken => _superclass.endToken;
@override
@@ -3889,6 +3942,11 @@
NamedTypeImpl get superclass2 => _superclass;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('extendsKeyword', extendsKeyword)
+ ..addNode('superclass', superclass);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitExtendsClause(this);
@override
@@ -3967,17 +4025,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(extensionKeyword)
- ..add(name)
- ..add(typeParameters)
- ..add(onKeyword)
- ..add(extendedType)
- ..add(leftBracket)
- ..addAll(members)
- ..add(rightBracket);
-
- @override
ExtensionElement? get declaredElement => _declaredElement;
/// Set the element declared by this declaration to the given [element].
@@ -4030,6 +4077,17 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('extensionKeyword', extensionKeyword)
+ ..addNode('name', name)
+ ..addNode('typeParameters', typeParameters)
+ ..addToken('onKeyword', onKeyword)
+ ..addNode('extendedType', extendedType)
+ ..addToken('leftBracket', leftBracket)
+ ..addNodeList('members', members)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitExtensionDeclaration(this);
@@ -4085,12 +4143,6 @@
Token get beginToken => _extensionName.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_extensionName)
- ..add(_typeArguments)
- ..add(_argumentList);
-
- @override
Token get endToken => _argumentList.endToken;
@override
@@ -4123,6 +4175,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('extensionName', extensionName)
+ ..addNode('typeArguments', typeArguments)
+ ..addNode('argumentList', argumentList);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) {
return visitor.visitExtensionOverride(this);
}
@@ -4180,12 +4238,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(staticKeyword)
- ..add(_fieldList)
- ..add(semicolon);
-
- @override
Element? get declaredElement => null;
@override
@@ -4209,6 +4261,12 @@
bool get isStatic => staticKeyword != null;
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('staticKeyword', staticKeyword)
+ ..addNode('fields', fields)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitFieldDeclaration(this);
@override
@@ -4299,15 +4357,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(keyword)
- ..add(_type)
- ..add(thisKeyword)
- ..add(period)
- ..add(identifier)
- ..add(_parameters);
-
- @override
Token get endToken {
return question ?? _parameters?.endToken ?? identifier.endToken;
}
@@ -4343,6 +4392,15 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('keyword', keyword)
+ ..addNode('type', type)
+ ..addToken('thisKeyword', thisKeyword)
+ ..addToken('period', period)
+ ..addNode('identifier', identifier)
+ ..addNode('parameters', parameters);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitFieldFormalParameter(this);
@@ -4375,11 +4433,6 @@
Token get beginToken => inKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(inKeyword)
- ..add(_iterable);
-
- @override
Token get endToken => _iterable.endToken;
@override
@@ -4390,6 +4443,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('inKeyword', inKeyword)
+ ..addNode('iterable', iterable);
+
+ @override
void visitChildren(AstVisitor visitor) {
_iterable.accept(visitor);
}
@@ -4412,11 +4470,6 @@
Token get beginToken => _loopVariable.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_loopVariable)
- ..addAll(super.childEntities);
-
- @override
DeclaredIdentifierImpl get loopVariable => _loopVariable;
set loopVariable(DeclaredIdentifier variable) {
@@ -4424,6 +4477,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('loopVariable', loopVariable)
+ ..addAll(super._childEntities);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitForEachPartsWithDeclaration(this);
@@ -4451,11 +4509,6 @@
Token get beginToken => _identifier.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_identifier)
- ..addAll(super.childEntities);
-
- @override
SimpleIdentifierImpl get identifier => _identifier;
set identifier(SimpleIdentifier identifier) {
@@ -4463,6 +4516,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('identifier', identifier)
+ ..addAll(super._childEntities);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitForEachPartsWithIdentifier(this);
@@ -4509,15 +4567,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(awaitKeyword)
- ..add(forKeyword)
- ..add(leftParenthesis)
- ..add(_forLoopParts)
- ..add(rightParenthesis)
- ..add(_body);
-
- @override
Token get endToken => _body.endToken;
@override
@@ -4528,6 +4577,15 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('awaitKeyword', awaitKeyword)
+ ..addToken('forKeyword', forKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('forLoopParts', forLoopParts)
+ ..addToken('rightParenthesis', rightParenthesis)
+ ..addNode('body', body);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitForElement(this);
@override
@@ -4670,25 +4728,6 @@
Token get beginToken => leftParenthesis;
@override
- Iterable<SyntacticEntity> get childEntities {
- // TODO(paulberry): include commas.
- ChildEntities result = ChildEntities()..add(leftParenthesis);
- bool leftDelimiterNeeded = leftDelimiter != null;
- int length = _parameters.length;
- for (int i = 0; i < length; i++) {
- FormalParameter parameter = _parameters[i];
- if (leftDelimiterNeeded && leftDelimiter!.offset < parameter.offset) {
- result.add(leftDelimiter);
- leftDelimiterNeeded = false;
- }
- result.add(parameter);
- }
- return result
- ..add(rightDelimiter)
- ..add(rightParenthesis);
- }
-
- @override
Token get endToken => rightParenthesis;
@override
@@ -4705,6 +4744,25 @@
NodeListImpl<FormalParameter> get parameters => _parameters;
@override
+ ChildEntities get _childEntities {
+ // TODO(paulberry): include commas.
+ var result = ChildEntities()..addToken('leftParenthesis', leftParenthesis);
+ bool leftDelimiterNeeded = leftDelimiter != null;
+ int length = _parameters.length;
+ for (int i = 0; i < length; i++) {
+ FormalParameter parameter = _parameters[i];
+ if (leftDelimiterNeeded && leftDelimiter!.offset < parameter.offset) {
+ result..addToken('leftDelimiter', leftDelimiter);
+ leftDelimiterNeeded = false;
+ }
+ result..addNode('parameter', parameter);
+ }
+ return result
+ ..addToken('rightDelimiter', rightDelimiter)
+ ..addToken('rightParenthesis', rightParenthesis);
+ }
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitFormalParameterList(this);
@override
@@ -4741,13 +4799,6 @@
Token get beginToken => leftSeparator;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(leftSeparator)
- ..add(_condition)
- ..add(rightSeparator)
- ..addAll(_updaters);
-
- @override
ExpressionImpl? get condition => _condition;
set condition(Expression? expression) {
@@ -4761,6 +4812,13 @@
NodeListImpl<Expression> get updaters => _updaters;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('leftSeparator', leftSeparator)
+ ..addNode('condition', condition)
+ ..addToken('rightSeparator', rightSeparator)
+ ..addNodeList('updaters', updaters);
+
+ @override
void visitChildren(AstVisitor visitor) {
_condition?.accept(visitor);
_updaters.accept(visitor);
@@ -4791,11 +4849,6 @@
Token get beginToken => _variableList.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_variableList)
- ..addAll(super.childEntities);
-
- @override
VariableDeclarationListImpl get variables => _variableList;
set variables(VariableDeclarationList? variableList) {
@@ -4804,6 +4857,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('variables', variables)
+ ..addAll(super._childEntities);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitForPartsWithDeclarations(this);
@@ -4838,11 +4896,6 @@
Token get beginToken => initialization?.beginToken ?? super.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_initialization)
- ..addAll(super.childEntities);
-
- @override
ExpressionImpl? get initialization => _initialization;
set initialization(Expression? initialization) {
@@ -4850,6 +4903,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('initialization', initialization)
+ ..addAll(super._childEntities);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitForPartsWithExpression(this);
@@ -4896,15 +4954,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(awaitKeyword)
- ..add(forKeyword)
- ..add(leftParenthesis)
- ..add(_forLoopParts)
- ..add(rightParenthesis)
- ..add(_body);
-
- @override
Token get endToken => _body.endToken;
@override
@@ -4915,6 +4964,15 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('awaitKeyword', awaitKeyword)
+ ..addToken('forKeyword', forKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('forLoopParts', forLoopParts)
+ ..addToken('rightParenthesis', rightParenthesis)
+ ..addNode('body', body);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitForStatement(this);
@override
@@ -5027,14 +5085,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(externalKeyword)
- ..add(_returnType)
- ..add(propertyKeyword)
- ..add(_name)
- ..add(_functionExpression);
-
- @override
ExecutableElement? get declaredElement =>
_name.staticElement as ExecutableElement?;
@@ -5071,6 +5121,14 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('externalKeyword', externalKeyword)
+ ..addNode('returnType', returnType)
+ ..addToken('propertyKeyword', propertyKeyword)
+ ..addNode('name', name)
+ ..addNode('functionExpression', functionExpression);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitFunctionDeclaration(this);
@override
@@ -5097,10 +5155,6 @@
Token get beginToken => _functionDeclaration.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..add(_functionDeclaration);
-
- @override
Token get endToken => _functionDeclaration.endToken;
@override
@@ -5112,6 +5166,10 @@
}
@override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addNode('functionDeclaration', functionDeclaration);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitFunctionDeclarationStatement(this);
@@ -5171,12 +5229,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_typeParameters)
- ..add(_parameters)
- ..add(_body);
-
- @override
Token get endToken {
return _body.endToken;
}
@@ -5199,6 +5251,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('typeParameters', typeParameters)
+ ..addNode('parameters', parameters)
+ ..addNode('body', body);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitFunctionExpression(this);
@override
@@ -5239,11 +5297,6 @@
Token get beginToken => _function.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_function)
- ..add(_argumentList);
-
- @override
Token get endToken => _argumentList.endToken;
@override
@@ -5257,6 +5310,11 @@
Precedence get precedence => Precedence.postfix;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('function', function)
+ ..addNode('argumentList', argumentList);
+
+ @override
AstNode? get _nullShortingExtensionCandidate => parent;
@override
@@ -5295,11 +5353,6 @@
Token get beginToken => function.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(function)
- ..add(typeArguments);
-
- @override
Token get endToken => typeArguments?.endToken ?? function.endToken;
@override
@@ -5321,6 +5374,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('function', function)
+ ..addNode('typeArguments', typeArguments);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitFunctionReference(this);
@override
@@ -5370,15 +5428,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(typedefKeyword)
- ..add(_returnType)
- ..add(_name)
- ..add(_typeParameters)
- ..add(_parameters)
- ..add(semicolon);
-
- @override
TypeAliasElement? get declaredElement =>
_name.staticElement as TypeAliasElement?;
@@ -5404,6 +5453,15 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('typedefKeyword', typedefKeyword)
+ ..addNode('returnType', returnType)
+ ..addNode('name', name)
+ ..addNode('typeParameters', typeParameters)
+ ..addNode('parameters', parameters)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitFunctionTypeAlias(this);
@override
@@ -5474,12 +5532,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(_returnType)
- ..add(identifier)
- ..add(parameters);
-
- @override
Token get endToken => question ?? _parameters.endToken;
@override
@@ -5513,6 +5565,12 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addNode('returnType', returnType)
+ ..addNode('identifier', identifier)
+ ..addNode('parameters', parameters);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitFunctionTypedFormalParameter(this);
@@ -5593,14 +5651,6 @@
Token get beginToken => _returnType?.beginToken ?? functionKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_returnType)
- ..add(functionKeyword)
- ..add(_typeParameters)
- ..add(_parameters)
- ..add(question);
-
- @override
Token get endToken => question ?? _parameters.endToken;
@override
@@ -5629,6 +5679,14 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('returnType', returnType)
+ ..addToken('functionKeyword', functionKeyword)
+ ..addNode('typeParameters', typeParameters)
+ ..addNode('parameters', parameters)
+ ..addToken('question', question);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) {
return visitor.visitGenericFunctionType(this);
}
@@ -5676,15 +5734,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..addAll(metadata)
- ..add(typedefKeyword)
- ..add(name)
- ..add(_typeParameters)
- ..add(equals)
- ..add(_type);
-
- @override
Element? get declaredElement => name.staticElement;
/// The type of function being defined by the alias.
@@ -5718,6 +5767,15 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNodeList('metadata', metadata)
+ ..addToken('typedefKeyword', typedefKeyword)
+ ..addNode('name', name)
+ ..addNode('typeParameters', typeParameters)
+ ..addToken('equals', equals)
+ ..addNode('type', type);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) {
return visitor.visitGenericTypeAlias(this);
}
@@ -5752,17 +5810,17 @@
Token get beginToken => hideKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(hideKeyword)
- ..addAll(elements);
-
- @override
NodeListImpl<ShowHideClauseElement> get elements => _elements;
@override
Token get endToken => _elements.endToken!;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('hideKeyword', hideKeyword)
+ ..addNodeList('elements', elements);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitHideClause(this);
@override
@@ -5787,17 +5845,17 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(keyword)
- ..addAll(_hiddenNames);
-
- @override
Token get endToken => _hiddenNames.endToken!;
@override
NodeListImpl<SimpleIdentifier> get hiddenNames => _hiddenNames;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('keyword', keyword)
+ ..addNodeList('hiddenNames', hiddenNames);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitHideCombinator(this);
@override
@@ -5858,16 +5916,6 @@
Token get beginToken => ifKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(ifKeyword)
- ..add(leftParenthesis)
- ..add(_condition)
- ..add(rightParenthesis)
- ..add(_thenElement)
- ..add(elseKeyword)
- ..add(_elseElement);
-
- @override
ExpressionImpl get condition => _condition;
set condition(Expression condition) {
@@ -5892,6 +5940,16 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('ifKeyword', ifKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('condition', condition)
+ ..addToken('rightParenthesis', rightParenthesis)
+ ..addNode('thenElement', thenElement)
+ ..addToken('elseKeyword', elseKeyword)
+ ..addNode('elseElement', elseElement);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitIfElement(this);
@override
@@ -5954,16 +6012,6 @@
Token get beginToken => ifKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(ifKeyword)
- ..add(leftParenthesis)
- ..add(_condition)
- ..add(rightParenthesis)
- ..add(_thenStatement)
- ..add(elseKeyword)
- ..add(_elseStatement);
-
- @override
ExpressionImpl get condition => _condition;
set condition(Expression condition) {
@@ -5993,6 +6041,16 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('ifKeyword', ifKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('condition', condition)
+ ..addToken('rightParenthesis', rightParenthesis)
+ ..addNode('thenStatement', thenStatement)
+ ..addToken('elseKeyword', elseKeyword)
+ ..addNode('elseStatement', elseStatement);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitIfStatement(this);
@override
@@ -6024,12 +6082,6 @@
Token get beginToken => implementsKeyword;
@override
- // TODO(paulberry): add commas.
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(implementsKeyword)
- ..addAll(interfaces);
-
- @override
Token get endToken => _interfaces.endToken!;
@override
@@ -6040,6 +6092,12 @@
NodeListImpl<NamedType> get interfaces2 => _interfaces;
@override
+ // TODO(paulberry): add commas.
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('implementsKeyword', implementsKeyword)
+ ..addNodeList('interfaces', interfaces);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitImplementsClause(this);
@override
@@ -6074,11 +6132,6 @@
Token get beginToken => expression.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(expression)
- ..add(typeArguments);
-
- @override
Token get endToken => typeArguments?.endToken ?? expression.endToken;
@override
@@ -6100,6 +6153,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('expression', expression)
+ ..addNode('typeArguments', typeArguments);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) {
return visitor.visitImplicitCallReference(this);
}
@@ -6157,16 +6215,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(keyword)
- ..add(_uri)
- ..add(deferredKeyword)
- ..add(asKeyword)
- ..add(_prefix)
- ..addAll(combinators)
- ..add(semicolon);
-
- @override
ImportElement? get element => super.element as ImportElement?;
@override
@@ -6182,6 +6230,16 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('keyword', keyword)
+ ..addNode('uri', uri)
+ ..addToken('deferredKeyword', deferredKeyword)
+ ..addToken('asKeyword', asKeyword)
+ ..addNode('prefix', prefix)
+ ..addNodeList('combinators', combinators)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitImportDirective(this);
@override
@@ -6249,14 +6307,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_target)
- ..add(period)
- ..add(leftBracket)
- ..add(_index)
- ..add(rightBracket);
-
- @override
Token get endToken => rightBracket;
@override
@@ -6314,6 +6364,14 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('target', target)
+ ..addToken('period', period)
+ ..addToken('leftBracket', leftBracket)
+ ..addNode('index', index)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
AstNode get _nullShortingExtensionCandidate => parent!;
/// If the AST structure has been resolved, and the function being invoked is
@@ -6428,13 +6486,6 @@
Token get beginToken => keyword ?? _constructorName.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(keyword)
- ..add(_constructorName)
- ..add(_typeArguments)
- ..add(_argumentList);
-
- @override
ConstructorNameImpl get constructorName => _constructorName;
set constructorName(ConstructorName name) {
@@ -6474,6 +6525,13 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('keyword', keyword)
+ ..addNode('constructorName', constructorName)
+ ..addNode('typeArguments', typeArguments)
+ ..addNode('argumentList', argumentList);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitInstanceCreationExpression(this);
@@ -6513,9 +6571,6 @@
Token get beginToken => literal;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()..add(literal);
-
- @override
Token get endToken => literal;
/// Returns whether this literal's [parent] is a [PrefixExpression] of unary
@@ -6531,6 +6586,10 @@
}
@override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('literal', literal);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitIntegerLiteral(this);
@override
@@ -6631,12 +6690,6 @@
Token get beginToken => leftBracket;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(leftBracket)
- ..add(_expression)
- ..add(rightBracket);
-
- @override
Token get endToken => rightBracket ?? _expression.endToken;
@override
@@ -6647,6 +6700,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('leftBracket', leftBracket)
+ ..addNode('expression', expression)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitInterpolationExpression(this);
@@ -6678,9 +6737,6 @@
Token get beginToken => contents;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()..add(contents);
-
- @override
int get contentsEnd => offset + _lexemeHelper.end;
@override
@@ -6692,6 +6748,10 @@
@override
StringInterpolation get parent => super.parent as StringInterpolation;
+ @override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('contents', contents);
+
StringLexemeHelper get _lexemeHelper {
String lexeme = contents.lexeme;
return StringLexemeHelper(lexeme, identical(this, parent.elements.first),
@@ -6774,13 +6834,6 @@
Token get beginToken => _expression.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_expression)
- ..add(isOperator)
- ..add(notOperator)
- ..add(_type);
-
- @override
Token get endToken => _type.endToken;
@override
@@ -6801,6 +6854,13 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('expression', expression)
+ ..addToken('isOperator', isOperator)
+ ..addToken('notOperator', notOperator)
+ ..addNode('type', type);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitIsExpression(this);
@override
@@ -6836,11 +6896,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..addAll(_labels)
- ..add(_statement);
-
- @override
Token get endToken => _statement.endToken;
@override
@@ -6857,6 +6912,11 @@
StatementImpl get unlabeled => _statement.unlabeled;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNodeList('labels', labels)
+ ..addNode('statement', statement);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitLabeledStatement(this);
@override
@@ -6887,11 +6947,6 @@
Token get beginToken => _label.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_label)
- ..add(colon);
-
- @override
Token get endToken => colon;
@override
@@ -6902,6 +6957,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('label', label)
+ ..addToken('colon', colon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitLabel(this);
@override
@@ -6936,12 +6996,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(libraryKeyword)
- ..add(_name)
- ..add(semicolon);
-
- @override
Token get endToken => semicolon;
@override
@@ -6958,6 +7012,12 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('libraryKeyword', libraryKeyword)
+ ..addNode('name', name)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitLibraryDirective(this);
@override
@@ -6985,11 +7045,6 @@
Token get beginToken => _components.beginToken!;
@override
- // TODO(paulberry): add "." tokens.
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..addAll(_components);
-
- @override
NodeListImpl<SimpleIdentifier> get components => _components;
@override
@@ -7019,6 +7074,11 @@
Element? get staticElement => null;
@override
+ // TODO(paulberry): add "." tokens.
+ ChildEntities get _childEntities =>
+ ChildEntities()..addNodeList('components', components);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitLibraryIdentifier(this);
@override
@@ -7077,19 +7137,19 @@
}
@override
- // TODO(paulberry): add commas.
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(leftBracket)
- ..addAll(_elements)
- ..add(rightBracket);
-
- @override
NodeListImpl<CollectionElement> get elements => _elements;
@override
Token get endToken => rightBracket;
@override
+ // TODO(paulberry): add commas.
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('leftBracket', leftBracket)
+ ..addNodeList('elements', elements)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitListLiteral(this);
@override
@@ -7153,12 +7213,6 @@
Token get beginToken => _key.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_key)
- ..add(separator)
- ..add(_value);
-
- @override
Token get endToken => _value.endToken;
@override
@@ -7176,6 +7230,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('key', key)
+ ..addToken('separator', separator)
+ ..addNode('value', value);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitMapLiteralEntry(this);
@override
@@ -7278,17 +7338,6 @@
_body = _becomeParentOf(functionBody as FunctionBodyImpl);
}
- @override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(externalKeyword)
- ..add(modifierKeyword)
- ..add(_returnType)
- ..add(propertyKeyword)
- ..add(operatorKeyword)
- ..add(_name)
- ..add(_parameters)
- ..add(_body);
-
/// Return the element associated with this method, or `null` if the AST
/// structure has not been resolved. The element can either be a
/// [MethodElement], if this represents the declaration of a normal method, or
@@ -7357,6 +7406,17 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('externalKeyword', externalKeyword)
+ ..addToken('modifierKeyword', modifierKeyword)
+ ..addNode('returnType', returnType)
+ ..addToken('propertyKeyword', propertyKeyword)
+ ..addToken('operatorKeyword', operatorKeyword)
+ ..addNode('name', name)
+ ..addNode('parameters', parameters)
+ ..addNode('body', body);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitMethodDeclaration(this);
@override
@@ -7419,13 +7479,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_target)
- ..add(operator)
- ..add(_methodName)
- ..add(_argumentList);
-
- @override
Token get endToken => _argumentList.endToken;
@override
@@ -7501,6 +7554,13 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('target', target)
+ ..addToken('operator', operator)
+ ..addNode('methodName', methodName)
+ ..addNode('argumentList', argumentList);
+
+ @override
AstNode? get _nullShortingExtensionCandidate => parent;
@override
@@ -7556,17 +7616,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(mixinKeyword)
- ..add(_name)
- ..add(_typeParameters)
- ..add(_onClause)
- ..add(_implementsClause)
- ..add(leftBracket)
- ..addAll(members)
- ..add(rightBracket);
-
- @override
ClassElement? get declaredElement => _name.staticElement as ClassElement?;
@override
@@ -7591,6 +7640,17 @@
TypeParameterListImpl? get typeParameters => _typeParameters;
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('mixinKeyword', mixinKeyword)
+ ..addNode('name', name)
+ ..addNode('typeParameters', typeParameters)
+ ..addNode('onClause', onClause)
+ ..addNode('implementsClause', implementsClause)
+ ..addToken('leftBracket', leftBracket)
+ ..addNodeList('members', members)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitMixinDeclaration(this);
@override
@@ -7649,11 +7709,6 @@
Token get beginToken => _name.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_name)
- ..add(_expression);
-
- @override
ParameterElement? get element {
var element = _name.label.staticElement;
if (element is ParameterElement) {
@@ -7683,6 +7738,11 @@
Precedence get precedence => Precedence.none;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('name', name)
+ ..addNode('expression', expression);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitNamedExpression(this);
@override
@@ -7724,12 +7784,6 @@
Token get beginToken => _name.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_name)
- ..add(_typeArguments)
- ..add(question);
-
- @override
Token get endToken => question ?? _typeArguments?.endToken ?? _name.endToken;
@override
@@ -7759,6 +7813,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('name', name)
+ ..addNode('typeArguments', typeArguments)
+ ..addToken('question', question);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitNamedType(this);
@override
@@ -7850,11 +7910,6 @@
Token get beginToken => nativeKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(nativeKeyword)
- ..add(_name);
-
- @override
Token get endToken {
return _name?.endToken ?? nativeKeyword;
}
@@ -7867,6 +7922,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('nativeKeyword', nativeKeyword)
+ ..addNode('name', name);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitNativeClause(this);
@override
@@ -7905,12 +7965,6 @@
Token get beginToken => nativeKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(nativeKeyword)
- ..add(_stringLiteral)
- ..add(semicolon);
-
- @override
Token get endToken => semicolon;
@override
@@ -7921,6 +7975,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('nativeKeyword', nativeKeyword)
+ ..addNode('stringLiteral', stringLiteral)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitNativeFunctionBody(this);
@override
@@ -8123,19 +8183,13 @@
]..sort(AstNode.LEXICAL_ORDER);
}
+ @override
ChildEntities get _childEntities {
- ChildEntities result = ChildEntities();
- if (_commentIsBeforeAnnotations()) {
- result
- ..add(_comment)
- ..addAll(_metadata);
- } else {
- result.addAll(sortedCommentAndAnnotations);
- }
- result
- ..add(requiredKeyword)
- ..add(covariantKeyword);
- return result;
+ return ChildEntities()
+ ..addNode('documentationComment', documentationComment)
+ ..addNodeList('metadata', metadata)
+ ..addToken('requiredKeyword', requiredKeyword)
+ ..addToken('covariantKeyword', covariantKeyword);
}
@override
@@ -8182,10 +8236,11 @@
Token get beginToken => literal;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()..add(literal);
+ Token get endToken => literal;
@override
- Token get endToken => literal;
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('literal', literal);
@override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitNullLiteral(this);
@@ -8243,12 +8298,6 @@
Token get beginToken => onKeyword;
@override
- // TODO(paulberry): add commas.
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(onKeyword)
- ..addAll(superclassConstraints);
-
- @override
Token get endToken => _superclassConstraints.endToken!;
@override
@@ -8259,6 +8308,12 @@
NodeListImpl<NamedType> get superclassConstraints2 => _superclassConstraints;
@override
+ // TODO(paulberry): add commas.
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('onKeyword', onKeyword)
+ ..addNodeList('superclassConstraints', superclassConstraints);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitOnClause(this);
@override
@@ -8294,12 +8349,6 @@
Token get beginToken => leftParenthesis;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(leftParenthesis)
- ..add(_expression)
- ..add(rightParenthesis);
-
- @override
Token get endToken => rightParenthesis;
@override
@@ -8324,6 +8373,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('expression', expression)
+ ..addToken('rightParenthesis', rightParenthesis);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitParenthesizedExpression(this);
@@ -8354,12 +8409,6 @@
: super(comment, metadata, partUri);
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(partKeyword)
- ..add(_uri)
- ..add(semicolon);
-
- @override
Token get endToken => semicolon;
@override
@@ -8372,6 +8421,12 @@
CompilationUnitElement? get uriElement => element as CompilationUnitElement?;
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('partKeyword', partKeyword)
+ ..addNode('uri', uri)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitPartDirective(this);
}
@@ -8417,14 +8472,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(partKeyword)
- ..add(ofKeyword)
- ..add(_uri)
- ..add(_libraryName)
- ..add(semicolon);
-
- @override
Token get endToken => semicolon;
@override
@@ -8448,6 +8495,14 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('partKeyword', partKeyword)
+ ..addToken('ofKeyword', ofKeyword)
+ ..addNode('uri', uri)
+ ..addNode('libraryName', libraryName)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitPartOfDirective(this);
@override
@@ -8487,11 +8542,6 @@
Token get beginToken => _operand.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_operand)
- ..add(operator);
-
- @override
Token get endToken => operator;
@override
@@ -8505,6 +8555,11 @@
Precedence get precedence => Precedence.postfix;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('operand', operand)
+ ..addToken('operator', operator);
+
+ @override
AstNode? get _nullShortingExtensionCandidate => parent;
/// If the AST structure has been resolved, and the function being invoked is
@@ -8561,12 +8616,6 @@
Token get beginToken => _prefix.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_prefix)
- ..add(period)
- ..add(_identifier);
-
- @override
Token get endToken => _identifier.endToken;
@override
@@ -8609,6 +8658,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('prefix', prefix)
+ ..addToken('period', period)
+ ..addNode('identifier', identifier);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitPrefixedIdentifier(this);
@override
@@ -8647,11 +8702,6 @@
Token get beginToken => operator;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(operator)
- ..add(_operand);
-
- @override
Token get endToken => _operand.endToken;
@override
@@ -8665,6 +8715,11 @@
Precedence get precedence => Precedence.prefix;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('operator', operator)
+ ..addNode('operand', operand);
+
+ @override
AstNode? get _nullShortingExtensionCandidate => parent;
/// If the AST structure has been resolved, and the function being invoked is
@@ -8731,12 +8786,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_target)
- ..add(operator)
- ..add(_propertyName);
-
- @override
Token get endToken => _propertyName.endToken;
@override
@@ -8794,6 +8843,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('target', target)
+ ..addToken('operator', operator)
+ ..addNode('propertyName', propertyName);
+
+ @override
AstNode? get _nullShortingExtensionCandidate => parent;
@override
@@ -8859,13 +8914,6 @@
Token get beginToken => thisKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(thisKeyword)
- ..add(period)
- ..add(_constructorName)
- ..add(_argumentList);
-
- @override
SimpleIdentifierImpl? get constructorName => _constructorName;
set constructorName(SimpleIdentifier? identifier) {
@@ -8876,6 +8924,13 @@
Token get endToken => _argumentList.endToken;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('thisKeyword', thisKeyword)
+ ..addToken('period', period)
+ ..addNode('constructorName', constructorName)
+ ..addNode('argumentList', argumentList);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitRedirectingConstructorInvocation(this);
@@ -8903,16 +8958,16 @@
Token get beginToken => rethrowKeyword;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..add(rethrowKeyword);
-
- @override
Token get endToken => rethrowKeyword;
@override
Precedence get precedence => Precedence.assignment;
@override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('rethrowKeyword', rethrowKeyword);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitRethrowExpression(this);
@override
@@ -8948,12 +9003,6 @@
Token get beginToken => returnKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(returnKeyword)
- ..add(_expression)
- ..add(semicolon);
-
- @override
Token get endToken => semicolon;
@override
@@ -8964,6 +9013,12 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('returnKeyword', returnKeyword)
+ ..addNode('expression', expression)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitReturnStatement(this);
@override
@@ -8989,11 +9044,11 @@
Token get beginToken => scriptTag;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..add(scriptTag);
+ Token get endToken => scriptTag;
@override
- Token get endToken => scriptTag;
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('scriptTag', scriptTag);
@override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitScriptTag(this);
@@ -9054,13 +9109,6 @@
}
@override
- // TODO(paulberry): add commas.
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(leftBracket)
- ..addAll(elements)
- ..add(rightBracket);
-
- @override
NodeListImpl<CollectionElement> get elements => _elements;
@override
@@ -9073,6 +9121,13 @@
bool get isSet => _resolvedKind == _SetOrMapKind.set;
@override
+ // TODO(paulberry): add commas.
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('leftBracket', leftBracket)
+ ..addNodeList('elements', elements)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitSetOrMapLiteral(this);
void becomeMap() {
@@ -9119,17 +9174,17 @@
Token get beginToken => showKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(showKeyword)
- ..addAll(elements);
-
- @override
NodeListImpl<ShowHideClauseElement> get elements => _elements;
@override
Token get endToken => _elements.endToken!;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('showKeyword', showKeyword)
+ ..addNodeList('elements', elements);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitShowClause(this);
@override
@@ -9155,18 +9210,18 @@
}
@override
- // TODO(paulberry): add commas.
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(keyword)
- ..addAll(_shownNames);
-
- @override
Token get endToken => _shownNames.endToken!;
@override
NodeListImpl<SimpleIdentifier> get shownNames => _shownNames;
@override
+ // TODO(paulberry): add commas.
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('keyword', keyword)
+ ..addNodeList('shownNames', shownNames);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitShowCombinator(this);
@override
@@ -9199,11 +9254,12 @@
Token get beginToken => modifier ?? name.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..addAll([if (modifier != null) modifier!, name]);
+ Token get endToken => name.endToken;
@override
- Token get endToken => name.endToken;
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('modifier', modifier)
+ ..addNode('name', name);
@override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitShowHideElement(this);
@@ -9270,12 +9326,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(keyword)
- ..add(_type)
- ..add(identifier);
-
- @override
Token get endToken => identifier?.endToken ?? type!.endToken;
@override
@@ -9292,6 +9342,12 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('keyword', keyword)
+ ..addNode('type', type)
+ ..addNode('identifier', identifier);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitSimpleFormalParameter(this);
@@ -9350,9 +9406,6 @@
Token get beginToken => token;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()..add(token);
-
- @override
Token get endToken => token;
@override
@@ -9416,6 +9469,9 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()..addToken('token', token);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitSimpleIdentifier(this);
@override
@@ -9551,9 +9607,6 @@
Token get beginToken => literal;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()..add(literal);
-
- @override
int get contentsEnd => offset + _helper.end;
@override
@@ -9574,6 +9627,10 @@
@override
bool get isSynthetic => literal.isSynthetic;
+ @override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('literal', literal);
+
StringLexemeHelper get _helper {
return StringLexemeHelper(literal.lexeme, true, true);
}
@@ -9615,11 +9672,6 @@
Token get beginToken => spreadOperator;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(spreadOperator)
- ..add(_expression);
-
- @override
Token get endToken => _expression.endToken;
@override
@@ -9634,6 +9686,11 @@
spreadOperator.type == TokenType.PERIOD_PERIOD_PERIOD_QUESTION;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('spreadOperator', spreadOperator)
+ ..addNode('expression', expression);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) {
return visitor.visitSpreadElement(this);
}
@@ -9705,10 +9762,6 @@
Token get beginToken => _elements.beginToken!;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..addAll(_elements);
-
- @override
int get contentsEnd {
var element = _elements.last as InterpolationString;
return element.contentsEnd;
@@ -9744,6 +9797,10 @@
InterpolationStringImpl get lastString =>
elements.last as InterpolationStringImpl;
+ @override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addNodeList('elements', elements);
+
StringLexemeHelper get _firstHelper {
var lastString = _elements.first as InterpolationString;
String lexeme = lastString.contents.lexeme;
@@ -9927,13 +9984,6 @@
Token get beginToken => superKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(superKeyword)
- ..add(period)
- ..add(_constructorName)
- ..add(_argumentList);
-
- @override
SimpleIdentifierImpl? get constructorName => _constructorName;
set constructorName(SimpleIdentifier? identifier) {
@@ -9944,6 +9994,13 @@
Token get endToken => _argumentList.endToken;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('superKeyword', superKeyword)
+ ..addToken('period', period)
+ ..addNode('constructorName', constructorName)
+ ..addNode('argumentList', argumentList);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitSuperConstructorInvocation(this);
@@ -9970,16 +10027,16 @@
Token get beginToken => superKeyword;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..add(superKeyword);
-
- @override
Token get endToken => superKeyword;
@override
Precedence get precedence => Precedence.primary;
@override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('superKeyword', superKeyword);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitSuperExpression(this);
@override
@@ -10069,15 +10126,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(keyword)
- ..add(_type)
- ..add(superKeyword)
- ..add(period)
- ..add(identifier)
- ..add(_parameters);
-
- @override
Token get endToken {
return question ?? _parameters?.endToken ?? identifier.endToken;
}
@@ -10113,6 +10161,15 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('keyword', keyword)
+ ..addNode('type', type)
+ ..addToken('superKeyword', superKeyword)
+ ..addToken('period', period)
+ ..addNode('identifier', identifier)
+ ..addNode('parameters', parameters);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitSuperFormalParameter(this);
@@ -10143,14 +10200,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..addAll(labels)
- ..add(keyword)
- ..add(_expression)
- ..add(colon)
- ..addAll(statements);
-
- @override
ExpressionImpl get expression => _expression;
set expression(Expression expression) {
@@ -10158,6 +10207,14 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNodeList('labels', labels)
+ ..addToken('keyword', keyword)
+ ..addNode('expression', expression)
+ ..addToken('colon', colon)
+ ..addNodeList('statements', statements);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitSwitchCase(this);
@override
@@ -10180,11 +10237,11 @@
: super(labels, keyword, colon, statements);
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..addAll(labels)
- ..add(keyword)
- ..add(colon)
- ..addAll(statements);
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNodeList('labels', labels)
+ ..addToken('keyword', keyword)
+ ..addToken('colon', colon)
+ ..addNodeList('statements', statements);
@override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitSwitchDefault(this);
@@ -10297,16 +10354,6 @@
Token get beginToken => switchKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(switchKeyword)
- ..add(leftParenthesis)
- ..add(_expression)
- ..add(rightParenthesis)
- ..add(leftBracket)
- ..addAll(_members)
- ..add(rightBracket);
-
- @override
Token get endToken => rightBracket;
@override
@@ -10320,6 +10367,16 @@
NodeListImpl<SwitchMember> get members => _members;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('switchKeyword', switchKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('expression', expression)
+ ..addToken('rightParenthesis', rightParenthesis)
+ ..addToken('leftBracket', leftBracket)
+ ..addNodeList('members', members)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitSwitchStatement(this);
@override
@@ -10349,13 +10406,13 @@
Token get beginToken => poundSign;
@override
- // TODO(paulberry): add "." tokens.
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(poundSign)
- ..addAll(components);
+ Token get endToken => components[components.length - 1];
@override
- Token get endToken => components[components.length - 1];
+ // TODO(paulberry): add "." tokens.
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('poundSign', poundSign)
+ ..addTokenList('components', components);
@override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitSymbolLiteral(this);
@@ -10382,16 +10439,16 @@
Token get beginToken => thisKeyword;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..add(thisKeyword);
-
- @override
Token get endToken => thisKeyword;
@override
Precedence get precedence => Precedence.primary;
@override
+ ChildEntities get _childEntities =>
+ ChildEntities()..addToken('thisKeyword', thisKeyword);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitThisExpression(this);
@override
@@ -10421,11 +10478,6 @@
Token get beginToken => throwKeyword;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(throwKeyword)
- ..add(_expression);
-
- @override
Token get endToken {
return _expression.endToken;
}
@@ -10441,6 +10493,11 @@
Precedence get precedence => Precedence.assignment;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('throwKeyword', throwKeyword)
+ ..addNode('expression', expression);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitThrowExpression(this);
@override
@@ -10480,11 +10537,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(_variableList)
- ..add(semicolon);
-
- @override
Element? get declaredElement => null;
@override
@@ -10502,6 +10554,11 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addNode('variables', variables)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitTopLevelVariableDeclaration(this);
@@ -10563,14 +10620,6 @@
NodeListImpl<CatchClause> get catchClauses => _catchClauses;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(tryKeyword)
- ..add(_body)
- ..addAll(_catchClauses)
- ..add(finallyKeyword)
- ..add(_finallyBlock);
-
- @override
Token get endToken {
if (_finallyBlock != null) {
return _finallyBlock!.endToken;
@@ -10590,6 +10639,14 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('tryKeyword', tryKeyword)
+ ..addNode('body', body)
+ ..addNodeList('catchClauses', catchClauses)
+ ..addToken('finallyKeyword', finallyKeyword)
+ ..addNode('finallyBlock', finallyBlock);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitTryStatement(this);
@override
@@ -10669,14 +10726,14 @@
Token get beginToken => leftBracket;
@override
- // TODO(paulberry): Add commas.
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(leftBracket)
- ..addAll(_arguments)
- ..add(rightBracket);
+ Token get endToken => rightBracket;
@override
- Token get endToken => rightBracket;
+ // TODO(paulberry): Add commas.
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('leftBracket', leftBracket)
+ ..addNodeList('arguments', arguments)
+ ..addToken('rightBracket', rightBracket);
@override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitTypeArgumentList(this);
@@ -10721,9 +10778,10 @@
_typeArguments = _becomeParentOf(typeArguments as TypeArgumentListImpl?);
}
+ @override
ChildEntities get _childEntities => ChildEntities()
- ..add(constKeyword)
- ..add(_typeArguments);
+ ..addToken('constKeyword', constKeyword)
+ ..addNode('typeArguments', typeArguments);
@override
void visitChildren(AstVisitor visitor) {
@@ -10753,10 +10811,6 @@
Token get beginToken => _typeName.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities =>
- ChildEntities()..add(_typeName);
-
- @override
Token get endToken => _typeName.endToken;
@override
@@ -10772,6 +10826,9 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()..addNode('type', type);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitTypeLiteral(this);
@override
@@ -10822,12 +10879,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(_name)
- ..add(extendsKeyword)
- ..add(_bound);
-
- @override
TypeParameterElement? get declaredElement =>
_name.staticElement as TypeParameterElement?;
@@ -10850,6 +10901,12 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addNode('name', name)
+ ..addToken('extendsKeyword', extendsKeyword)
+ ..addNode('bound', bound);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitTypeParameter(this);
@override
@@ -10886,18 +10943,18 @@
Token get beginToken => leftBracket;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(leftBracket)
- ..addAll(_typeParameters)
- ..add(rightBracket);
-
- @override
Token get endToken => rightBracket;
@override
NodeListImpl<TypeParameter> get typeParameters => _typeParameters;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('leftBracket', leftBracket)
+ ..addNodeList('typeParameters', typeParameters)
+ ..addToken('rightBracket', rightBracket);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitTypeParameterList(this);
@override
@@ -11031,12 +11088,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(_name)
- ..add(equals)
- ..add(_initializer);
-
- @override
VariableElement? get declaredElement =>
_name.staticElement as VariableElement?;
@@ -11099,6 +11150,12 @@
}
@override
+ ChildEntities get _childEntities => super._childEntities
+ ..addNode('name', name)
+ ..addToken('equals', equals)
+ ..addNode('initializer', initializer);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitVariableDeclaration(this);
@override
@@ -11156,13 +11213,6 @@
}
@override
- // TODO(paulberry): include commas.
- Iterable<SyntacticEntity> get childEntities => super._childEntities
- ..add(keyword)
- ..add(_type)
- ..addAll(_variables);
-
- @override
Token get endToken => _variables.endToken!;
@override
@@ -11192,6 +11242,13 @@
NodeListImpl<VariableDeclaration> get variables => _variables;
@override
+ // TODO(paulberry): include commas.
+ ChildEntities get _childEntities => super._childEntities
+ ..addToken('keyword', keyword)
+ ..addNode('type', type)
+ ..addNodeList('variables', variables);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitVariableDeclarationList(this);
@@ -11226,11 +11283,6 @@
Token get beginToken => _variableList.beginToken;
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(_variableList)
- ..add(semicolon);
-
- @override
Token get endToken => semicolon;
@override
@@ -11241,6 +11293,11 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addNode('variables', variables)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) =>
visitor.visitVariableDeclarationStatement(this);
@@ -11291,14 +11348,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(whileKeyword)
- ..add(leftParenthesis)
- ..add(_condition)
- ..add(rightParenthesis)
- ..add(_body);
-
- @override
ExpressionImpl get condition => _condition;
set condition(Expression expression) {
@@ -11309,6 +11358,14 @@
Token get endToken => _body.endToken;
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('whileKeyword', whileKeyword)
+ ..addToken('leftParenthesis', leftParenthesis)
+ ..addNode('condition', condition)
+ ..addToken('rightParenthesis', rightParenthesis)
+ ..addNode('body', body);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitWhileStatement(this);
@override
@@ -11339,12 +11396,6 @@
Token get beginToken => withKeyword;
@override
- // TODO(paulberry): add commas.
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(withKeyword)
- ..addAll(_mixinTypes);
-
- @override
Token get endToken => _mixinTypes.endToken!;
@override
@@ -11355,6 +11406,12 @@
NodeListImpl<NamedType> get mixinTypes2 => _mixinTypes;
@override
+ // TODO(paulberry): add commas.
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('withKeyword', withKeyword)
+ ..addNodeList('mixinTypes', mixinTypes);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitWithClause(this);
@override
@@ -11396,13 +11453,6 @@
}
@override
- Iterable<SyntacticEntity> get childEntities => ChildEntities()
- ..add(yieldKeyword)
- ..add(star)
- ..add(_expression)
- ..add(semicolon);
-
- @override
Token get endToken {
return semicolon;
}
@@ -11415,6 +11465,13 @@
}
@override
+ ChildEntities get _childEntities => ChildEntities()
+ ..addToken('yieldKeyword', yieldKeyword)
+ ..addToken('star', star)
+ ..addNode('expression', expression)
+ ..addToken('semicolon', semicolon);
+
+ @override
E? accept<E>(AstVisitor<E> visitor) => visitor.visitYieldStatement(this);
@override
diff --git a/pkg/analyzer/lib/src/error/required_parameters_verifier.dart b/pkg/analyzer/lib/src/error/required_parameters_verifier.dart
index c5d9146..0e553d3 100644
--- a/pkg/analyzer/lib/src/error/required_parameters_verifier.dart
+++ b/pkg/analyzer/lib/src/error/required_parameters_verifier.dart
@@ -20,9 +20,9 @@
@override
void visitEnumConstantDeclaration(EnumConstantDeclaration node) {
_check(
- node.constructorElement?.parameters,
- node.arguments?.argumentList.arguments ?? <Expression>[],
- node.name,
+ parameters: node.constructorElement?.parameters,
+ arguments: node.arguments?.argumentList.arguments ?? <Expression>[],
+ errorNode: node.name,
);
}
@@ -31,9 +31,9 @@
var type = node.staticInvokeType;
if (type is FunctionType) {
_check(
- type.parameters,
- node.argumentList.arguments,
- node,
+ parameters: type.parameters,
+ arguments: node.argumentList.arguments,
+ errorNode: node,
);
}
}
@@ -41,9 +41,9 @@
@override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
_check(
- node.constructorName.staticElement?.parameters,
- node.argumentList.arguments,
- node.constructorName,
+ parameters: node.constructorName.staticElement?.parameters,
+ arguments: node.argumentList.arguments,
+ errorNode: node.constructorName,
);
}
@@ -52,16 +52,19 @@
if (node.methodName.name == FunctionElement.CALL_METHOD_NAME) {
var targetType = node.realTarget?.staticType;
if (targetType is FunctionType) {
- _check(targetType.parameters, node.argumentList.arguments,
- node.argumentList);
+ _check(
+ parameters: targetType.parameters,
+ arguments: node.argumentList.arguments,
+ errorNode: node.argumentList,
+ );
return;
}
}
_check(
- _executableElement(node.methodName.staticElement)?.parameters,
- node.argumentList.arguments,
- node.methodName,
+ parameters: _executableElement(node.methodName.staticElement)?.parameters,
+ arguments: node.argumentList.arguments,
+ errorNode: node.methodName,
);
}
@@ -69,26 +72,31 @@
void visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
_check(
- _executableElement(node.staticElement)?.parameters,
- node.argumentList.arguments,
- node,
+ parameters: _executableElement(node.staticElement)?.parameters,
+ arguments: node.argumentList.arguments,
+ errorNode: node,
);
}
@override
- void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+ void visitSuperConstructorInvocation(
+ SuperConstructorInvocation node, {
+ ConstructorElement? enclosingConstructor,
+ }) {
_check(
- _executableElement(node.staticElement)?.parameters,
- node.argumentList.arguments,
- node,
+ parameters: _executableElement(node.staticElement)?.parameters,
+ enclosingConstructor: enclosingConstructor,
+ arguments: node.argumentList.arguments,
+ errorNode: node,
);
}
- void _check(
- List<ParameterElement>? parameters,
- List<Expression> arguments,
- AstNode node,
- ) {
+ void _check({
+ required List<ParameterElement>? parameters,
+ ConstructorElement? enclosingConstructor,
+ required List<Expression> arguments,
+ required AstNode errorNode,
+ }) {
if (parameters == null) {
return;
}
@@ -96,10 +104,11 @@
for (ParameterElement parameter in parameters) {
if (parameter.isRequiredNamed) {
String parameterName = parameter.name;
- if (!_containsNamedExpression(arguments, parameterName)) {
+ if (!_containsNamedExpression(
+ enclosingConstructor, arguments, parameterName)) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.MISSING_REQUIRED_ARGUMENT,
- node,
+ errorNode,
[parameterName],
);
}
@@ -108,18 +117,19 @@
var annotation = _requiredAnnotation(parameter);
if (annotation != null) {
String parameterName = parameter.name;
- if (!_containsNamedExpression(arguments, parameterName)) {
+ if (!_containsNamedExpression(
+ enclosingConstructor, arguments, parameterName)) {
var reason = annotation.reason;
if (reason != null) {
_errorReporter.reportErrorForNode(
HintCode.MISSING_REQUIRED_PARAM_WITH_DETAILS,
- node,
+ errorNode,
[parameterName, reason],
);
} else {
_errorReporter.reportErrorForNode(
HintCode.MISSING_REQUIRED_PARAM,
- node,
+ errorNode,
[parameterName],
);
}
@@ -130,6 +140,7 @@
}
static bool _containsNamedExpression(
+ ConstructorElement? enclosingConstructor,
List<Expression> arguments,
String name,
) {
@@ -141,6 +152,12 @@
}
}
}
+
+ if (enclosingConstructor != null) {
+ return enclosingConstructor.parameters.any((e) =>
+ e is SuperFormalParameterElement && e.isNamed && e.name == name);
+ }
+
return false;
}
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 83d50a2..f516e46 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -1159,7 +1159,10 @@
@override
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
- _requiredParametersVerifier.visitSuperConstructorInvocation(node);
+ _requiredParametersVerifier.visitSuperConstructorInvocation(
+ node,
+ enclosingConstructor: _enclosingExecutable.element.ifTypeOrNull(),
+ );
_isInConstructorInitializer = true;
try {
super.visitSuperConstructorInvocation(node);
@@ -5280,3 +5283,11 @@
}
}
}
+
+extension on Object? {
+ /// If the target is [T], return it, otherwise `null`.
+ T? ifTypeOrNull<T>() {
+ final self = this;
+ return self is T ? self : null;
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/getter_not_subtype_setter_types_test.dart b/pkg/analyzer/test/src/diagnostics/getter_not_subtype_setter_types_test.dart
index ac4c1f3..f20aedb 100644
--- a/pkg/analyzer/test/src/diagnostics/getter_not_subtype_setter_types_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/getter_not_subtype_setter_types_test.dart
@@ -235,19 +235,51 @@
]);
}
- test_enum_instance() async {
+ test_enum_instance_mixinGetter_mixinSetter() async {
await assertErrorsInCode('''
-enum E {
- v;
+mixin M1 {
num get foo => 0;
+}
+
+mixin M2 {
set foo(int v) {}
}
+
+enum E with M1, M2 {
+ v
+}
''', [
- error(CompileTimeErrorCode.GETTER_NOT_SUBTYPE_SETTER_TYPES, 24, 3),
+ error(CompileTimeErrorCode.GETTER_NOT_SUBTYPE_SETTER_TYPES, 73, 1),
]);
}
- test_enum_instance_field() async {
+ test_enum_instance_mixinGetter_thisSetter() async {
+ await assertErrorsInCode('''
+mixin M {
+ num get foo => 0;
+}
+
+enum E with M {
+ v;
+ set foo(int v) {}
+}
+''', [
+ error(CompileTimeErrorCode.GETTER_NOT_SUBTYPE_SETTER_TYPES, 60, 3),
+ ]);
+ }
+
+ test_enum_instance_superGetter_thisSetter_index() async {
+ await assertErrorsInCode('''
+enum E {
+ v;
+ set index(String _) {}
+}
+''', [
+ error(CompileTimeErrorCode.GETTER_NOT_SUBTYPE_SETTER_TYPES, 20, 5),
+ ]);
+ }
+
+ test_enum_instance_thisField_thisSetter() async {
await assertErrorsInCode('''
enum E {
v;
@@ -259,6 +291,18 @@
]);
}
+ test_enum_instance_thisGetter_thisSetter() async {
+ await assertErrorsInCode('''
+enum E {
+ v;
+ num get foo => 0;
+ set foo(int v) {}
+}
+''', [
+ error(CompileTimeErrorCode.GETTER_NOT_SUBTYPE_SETTER_TYPES, 24, 3),
+ ]);
+ }
+
test_enum_static() async {
await assertErrorsInCode('''
enum E {
diff --git a/pkg/analyzer/test/src/diagnostics/missing_required_param_test.dart b/pkg/analyzer/test/src/diagnostics/missing_required_param_test.dart
index c70450c..e066e13 100644
--- a/pkg/analyzer/test/src/diagnostics/missing_required_param_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/missing_required_param_test.dart
@@ -101,6 +101,18 @@
]);
}
+ test_constructor_superFormalParameter() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ A({required int a});
+}
+
+class B extends A {
+ B({required super.a}) : super();
+}
+''');
+ }
+
test_enumConstant_withArguments() async {
await assertErrorsInCode(r'''
enum E {
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm.cc b/runtime/vm/compiler/asm_intrinsifier_arm.cc
index 7702c24..3b6b707 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm.cc
@@ -26,41 +26,6 @@
#define __ assembler->
-// Allocate a GrowableObjectArray:: using the backing array specified.
-// On stack: type argument (+1), data (+0).
-void AsmIntrinsifier::GrowableArray_Allocate(Assembler* assembler,
- Label* normal_ir_body) {
- // The newly allocated object is returned in R0.
- const intptr_t kTypeArgumentsOffset = 1 * target::kWordSize;
- const intptr_t kArrayOffset = 0 * target::kWordSize;
-
- // Try allocating in new space.
- const Class& cls = GrowableObjectArrayClass();
- __ TryAllocate(cls, normal_ir_body, Assembler::kFarJump, R0, R1);
-
- // Store backing array object in growable array object.
- __ ldr(R1, Address(SP, kArrayOffset)); // Data argument.
- // R0 is new, no barrier needed.
- __ StoreIntoObjectNoBarrier(
- R0, FieldAddress(R0, target::GrowableObjectArray::data_offset()), R1);
-
- // R0: new growable array object start as a tagged pointer.
- // Store the type argument field in the growable array object.
- __ ldr(R1, Address(SP, kTypeArgumentsOffset)); // Type argument.
- __ StoreIntoObjectNoBarrier(
- R0,
- FieldAddress(R0, target::GrowableObjectArray::type_arguments_offset()),
- R1);
-
- // Set the length field in the growable array object to 0.
- __ LoadImmediate(R1, 0);
- __ StoreIntoObjectNoBarrier(
- R0, FieldAddress(R0, target::GrowableObjectArray::length_offset()), R1);
- __ Ret(); // Returns the newly allocated object in R0.
-
- __ Bind(normal_ir_body);
-}
-
// Loads args from stack into R0 and R1
// Tests if they are smis, jumps to label not_smi if not.
static void TestBothArgumentsSmis(Assembler* assembler, Label* not_smi) {
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index 429099c..eb5cacc 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -26,40 +26,6 @@
#define __ assembler->
-// Allocate a GrowableObjectArray:: using the backing array specified.
-// On stack: type argument (+1), data (+0).
-void AsmIntrinsifier::GrowableArray_Allocate(Assembler* assembler,
- Label* normal_ir_body) {
- // The newly allocated object is returned in R0.
- const intptr_t kTypeArgumentsOffset = 1 * target::kWordSize;
- const intptr_t kArrayOffset = 0 * target::kWordSize;
-
- // Try allocating in new space.
- const Class& cls = GrowableObjectArrayClass();
- __ TryAllocate(cls, normal_ir_body, Assembler::kFarJump, R0, R1);
-
- // Store backing array object in growable array object.
- __ ldr(R1, Address(SP, kArrayOffset)); // Data argument.
- // R0 is new, no barrier needed.
- __ StoreCompressedIntoObjectNoBarrier(
- R0, FieldAddress(R0, target::GrowableObjectArray::data_offset()), R1);
-
- // R0: new growable array object start as a tagged pointer.
- // Store the type argument field in the growable array object.
- __ ldr(R1, Address(SP, kTypeArgumentsOffset)); // Type argument.
- __ StoreCompressedIntoObjectNoBarrier(
- R0,
- FieldAddress(R0, target::GrowableObjectArray::type_arguments_offset()),
- R1);
-
- // Set the length field in the growable array object to 0.
- __ StoreCompressedIntoObjectNoBarrier(
- R0, FieldAddress(R0, target::GrowableObjectArray::length_offset()), ZR);
- __ ret(); // Returns the newly allocated object in R0.
-
- __ Bind(normal_ir_body);
-}
-
// Loads args from stack into R0 and R1
// Tests if they are smis, jumps to label not_smi if not.
static void TestBothArgumentsSmis(Assembler* assembler, Label* not_smi) {
diff --git a/runtime/vm/compiler/asm_intrinsifier_ia32.cc b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
index 7d264aa7..815a2e0 100644
--- a/runtime/vm/compiler/asm_intrinsifier_ia32.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
@@ -31,42 +31,6 @@
#define __ assembler->
-// Allocate a GrowableObjectArray:: using the backing array specified.
-// On stack: type argument (+2), data (+1), return-address (+0).
-void AsmIntrinsifier::GrowableArray_Allocate(Assembler* assembler,
- Label* normal_ir_body) {
- // This snippet of inlined code uses the following registers:
- // EAX, EBX
- // and the newly allocated object is returned in EAX.
- const intptr_t kTypeArgumentsOffset = 2 * target::kWordSize;
-
- const intptr_t kArrayOffset = 1 * target::kWordSize;
-
- // Try allocating in new space.
- const Class& cls = GrowableObjectArrayClass();
- __ TryAllocate(cls, normal_ir_body, Assembler::kNearJump, EAX, EBX);
-
- // Store backing array object in growable array object.
- __ movl(EBX, Address(ESP, kArrayOffset)); // data argument.
- // EAX is new, no barrier needed.
- __ StoreIntoObjectNoBarrier(
- EAX, FieldAddress(EAX, target::GrowableObjectArray::data_offset()), EBX);
-
- // EAX: new growable array object start as a tagged pointer.
- // Store the type argument field in the growable array object.
- __ movl(EBX, Address(ESP, kTypeArgumentsOffset)); // type argument.
- __ StoreIntoObjectNoBarrier(
- EAX,
- FieldAddress(EAX, target::GrowableObjectArray::type_arguments_offset()),
- EBX);
-
- __ ZeroInitSmiField(
- FieldAddress(EAX, target::GrowableObjectArray::length_offset()));
- __ ret(); // returns the newly allocated object in EAX.
-
- __ Bind(normal_ir_body);
-}
-
// Tests if two top most arguments are smis, jumps to label not_smi if not.
// Topmost argument is in EAX.
static void TestBothArgumentsSmis(Assembler* assembler, Label* not_smi) {
diff --git a/runtime/vm/compiler/asm_intrinsifier_riscv.cc b/runtime/vm/compiler/asm_intrinsifier_riscv.cc
index 0e2da82..db03989 100644
--- a/runtime/vm/compiler/asm_intrinsifier_riscv.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_riscv.cc
@@ -26,40 +26,6 @@
#define __ assembler->
-// Allocate a GrowableObjectArray:: using the backing array specified.
-// On stack: type argument (+1), data (+0).
-void AsmIntrinsifier::GrowableArray_Allocate(Assembler* assembler,
- Label* normal_ir_body) {
- // The newly allocated object is returned in R0.
- const intptr_t kTypeArgumentsOffset = 1 * target::kWordSize;
- const intptr_t kArrayOffset = 0 * target::kWordSize;
-
- // Try allocating in new space.
- const Class& cls = GrowableObjectArrayClass();
- __ TryAllocate(cls, normal_ir_body, Assembler::kFarJump, A0, A1);
-
- // Store backing array object in growable array object.
- __ lx(A1, Address(SP, kArrayOffset)); // Data argument.
- // R0 is new, no barrier needed.
- __ StoreCompressedIntoObjectNoBarrier(
- A0, FieldAddress(A0, target::GrowableObjectArray::data_offset()), A1);
-
- // R0: new growable array object start as a tagged pointer.
- // Store the type argument field in the growable array object.
- __ lx(A1, Address(SP, kTypeArgumentsOffset)); // Type argument.
- __ StoreCompressedIntoObjectNoBarrier(
- A0,
- FieldAddress(A0, target::GrowableObjectArray::type_arguments_offset()),
- A1);
-
- // Set the length field in the growable array object to 0.
- __ StoreCompressedIntoObjectNoBarrier(
- A0, FieldAddress(A0, target::GrowableObjectArray::length_offset()), ZR);
- __ ret(); // Returns the newly allocated object in A0.
-
- __ Bind(normal_ir_body);
-}
-
// Loads args from stack into A0 and A1
// Tests if they are smis, jumps to label not_smi if not.
static void TestBothArgumentsSmis(Assembler* assembler, Label* not_smi) {
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index 48bfe54..0a47313 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -26,42 +26,6 @@
#define __ assembler->
-// Allocate a GrowableObjectArray using the backing array specified.
-// On stack: type argument (+2), data (+1), return-address (+0).
-void AsmIntrinsifier::GrowableArray_Allocate(Assembler* assembler,
- Label* normal_ir_body) {
- // This snippet of inlined code uses the following registers:
- // RAX, RCX, R13
- // and the newly allocated object is returned in RAX.
- const intptr_t kTypeArgumentsOffset = 2 * target::kWordSize;
- const intptr_t kArrayOffset = 1 * target::kWordSize;
-
- // Try allocating in new space.
- const Class& cls = GrowableObjectArrayClass();
- __ TryAllocate(cls, normal_ir_body, Assembler::kFarJump, RAX, R13);
-
- // Store backing array object in growable array object.
- __ movq(RCX, Address(RSP, kArrayOffset)); // data argument.
- // RAX is new, no barrier needed.
- __ StoreCompressedIntoObjectNoBarrier(
- RAX, FieldAddress(RAX, target::GrowableObjectArray::data_offset()), RCX);
-
- // RAX: new growable array object start as a tagged pointer.
- // Store the type argument field in the growable array object.
- __ movq(RCX, Address(RSP, kTypeArgumentsOffset)); // type argument.
- __ StoreCompressedIntoObjectNoBarrier(
- RAX,
- FieldAddress(RAX, target::GrowableObjectArray::type_arguments_offset()),
- RCX);
-
- // Set the length field in the growable array object to 0.
- __ ZeroInitCompressedSmiField(
- FieldAddress(RAX, target::GrowableObjectArray::length_offset()));
- __ ret(); // returns the newly allocated object in RAX.
-
- __ Bind(normal_ir_body);
-}
-
// Tests if two top most arguments are smis, jumps to label not_smi if not.
// Topmost argument is in RAX.
static void TestBothArgumentsSmis(Assembler* assembler, Label* not_smi) {
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 0311465..6fd0db2 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -877,6 +877,7 @@
case MethodRecognizer::kByteDataViewTypedData:
case MethodRecognizer::kTypedDataViewTypedData:
case MethodRecognizer::kClassIDgetID:
+ case MethodRecognizer::kGrowableArrayAllocateWithData:
case MethodRecognizer::kGrowableArrayCapacity:
case MethodRecognizer::kListFactory:
case MethodRecognizer::kObjectArrayAllocate:
@@ -1124,6 +1125,26 @@
body += LoadLocal(parsed_function_->RawParameterVariable(0));
body += LoadClassId();
break;
+ case MethodRecognizer::kGrowableArrayAllocateWithData: {
+ ASSERT(function.IsFactory());
+ ASSERT_EQUAL(function.NumParameters(), 2);
+ const Class& cls =
+ Class::ZoneHandle(Z, compiler::GrowableObjectArrayClass().ptr());
+ body += LoadLocal(parsed_function_->RawParameterVariable(0));
+ body += AllocateObject(TokenPosition::kNoSource, cls, 1);
+ LocalVariable* object = MakeTemporary();
+ body += LoadLocal(object);
+ body += LoadLocal(parsed_function_->RawParameterVariable(1));
+ body += StoreNativeField(Slot::GrowableObjectArray_data(),
+ StoreInstanceFieldInstr::Kind::kInitializing,
+ kNoStoreBarrier);
+ body += LoadLocal(object);
+ body += IntConstant(0);
+ body += StoreNativeField(Slot::GrowableObjectArray_length(),
+ StoreInstanceFieldInstr::Kind::kInitializing,
+ kNoStoreBarrier);
+ break;
+ }
case MethodRecognizer::kGrowableArrayCapacity:
ASSERT_EQUAL(function.NumParameters(), 1);
body += LoadLocal(parsed_function_->RawParameterVariable(0));
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index 6c67e4c..5d42253 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -18,6 +18,7 @@
V(List, ., ListFactory, 0xbc820cf9) \
V(_List, ., ObjectArrayAllocate, 0xd693eee6) \
V(_List, []=, ObjectArraySetIndexed, 0xd7b48abc) \
+ V(_GrowableList, ._withData, GrowableArrayAllocateWithData, 0xa32d060b) \
V(_GrowableList, []=, GrowableArraySetIndexed, 0xd7b48abc) \
V(_TypedList, _getInt8, ByteArrayBaseGetInt8, 0x1623dc34) \
V(_TypedList, _getUint8, ByteArrayBaseGetUint8, 0x177ffe2a) \
@@ -274,7 +275,6 @@
V(_Double, get:isNegative, Double_getIsNegative, 0xd4715091) \
V(_Double, _mulFromInteger, Double_mulFromInteger, 0x0a50d2cf) \
V(_Double, .fromInteger, DoubleFromInteger, 0x7d0fd999) \
- V(_GrowableList, ._withData, GrowableArray_Allocate, 0xa32d060b) \
V(_RegExp, _ExecuteMatch, RegExp_ExecuteMatch, 0x9911d549) \
V(_RegExp, _ExecuteMatchSticky, RegExp_ExecuteMatchSticky, 0x91dd880f) \
V(Object, ==, ObjectEquals, 0x46587030) \
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index b70a40e..199ae30 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -899,6 +899,43 @@
__ Ret();
}
+// Generates allocation stub for _GrowableList class.
+// This stub exists solely for performance reasons: default allocation
+// stub is slower as it doesn't use specialized inline allocation.
+void StubCodeCompiler::GenerateAllocateGrowableArrayStub(Assembler* assembler) {
+#if defined(TARGET_ARCH_IA32)
+ // This stub is not used on IA32 because IA32 version of
+ // StubCodeCompiler::GenerateAllocationStubForClass uses inline
+ // allocation. Also, AllocateObjectSlow stub is not generated on IA32.
+ __ Breakpoint();
+#else
+ const intptr_t instance_size = target::RoundedAllocationSize(
+ target::GrowableObjectArray::InstanceSize());
+
+ if (!FLAG_use_slow_path && FLAG_inline_alloc) {
+ Label slow_case;
+ __ Comment("Inline allocation of GrowableList");
+ __ TryAllocateObject(kGrowableObjectArrayCid, instance_size, &slow_case,
+ Assembler::kNearJump, AllocateObjectABI::kResultReg,
+ /*temp_reg=*/AllocateObjectABI::kTagsReg);
+ __ StoreIntoObjectNoBarrier(
+ AllocateObjectABI::kResultReg,
+ FieldAddress(AllocateObjectABI::kResultReg,
+ target::GrowableObjectArray::type_arguments_offset()),
+ AllocateObjectABI::kTypeArgumentsReg);
+
+ __ Ret();
+ __ Bind(&slow_case);
+ }
+
+ const uword tags = target::MakeTagWordForNewSpaceObject(
+ kGrowableObjectArrayCid, instance_size);
+ __ LoadImmediate(AllocateObjectABI::kTagsReg, tags);
+ __ Jump(
+ Address(THR, target::Thread::allocate_object_slow_entry_point_offset()));
+#endif // defined(TARGET_ARCH_IA32)
+}
+
// The UnhandledException class lives in the VM isolate, so it cannot cache
// an allocation stub for itself. Instead, we cache it in the stub code list.
void StubCodeCompiler::GenerateAllocateUnhandledExceptionStub(
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index 5174ca8..a37565a 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -1728,7 +1728,7 @@
static void GenerateAllocateObjectHelper(Assembler* assembler,
bool is_cls_parameterized) {
- const Register kTagsReg = R2;
+ const Register kTagsReg = AllocateObjectABI::kTagsReg;
{
Label slow_case;
@@ -1845,7 +1845,6 @@
void StubCodeCompiler::GenerateAllocateObjectSlowStub(Assembler* assembler) {
const Register kClsReg = R1;
- const Register kTagsReg = R2;
if (!FLAG_precompiled_mode) {
__ ldr(CODE_REG,
@@ -1856,7 +1855,8 @@
// calling into the runtime.
__ EnterStubFrame();
- __ ExtractClassIdFromTags(AllocateObjectABI::kResultReg, kTagsReg);
+ __ ExtractClassIdFromTags(AllocateObjectABI::kResultReg,
+ AllocateObjectABI::kTagsReg);
__ LoadClassById(kClsReg, AllocateObjectABI::kResultReg);
__ LoadObject(AllocateObjectABI::kResultReg, NullObject());
@@ -1903,7 +1903,7 @@
const uword tags =
target::MakeTagWordForNewSpaceObject(cls_id, instance_size);
- const Register kTagsReg = R2;
+ const Register kTagsReg = AllocateObjectABI::kTagsReg;
__ LoadImmediate(kTagsReg, tags);
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index e5fe61f..6dc4dbe 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -1904,7 +1904,7 @@
static void GenerateAllocateObjectHelper(Assembler* assembler,
bool is_cls_parameterized) {
- const Register kTagsReg = R2;
+ const Register kTagsReg = AllocateObjectABI::kTagsReg;
{
Label slow_case;
@@ -2014,20 +2014,19 @@
}
void StubCodeCompiler::GenerateAllocateObjectSlowStub(Assembler* assembler) {
- const Register kTagsToClsIdReg = R2;
-
if (!FLAG_precompiled_mode) {
__ ldr(CODE_REG,
Address(THR, target::Thread::call_to_runtime_stub_offset()));
}
- __ ExtractClassIdFromTags(kTagsToClsIdReg, kTagsToClsIdReg);
+ __ ExtractClassIdFromTags(AllocateObjectABI::kTagsReg,
+ AllocateObjectABI::kTagsReg);
// Create a stub frame as we are pushing some objects on the stack before
// calling into the runtime.
__ EnterStubFrame();
- __ LoadClassById(R0, kTagsToClsIdReg);
+ __ LoadClassById(R0, AllocateObjectABI::kTagsReg);
__ PushPair(R0, NULL_REG); // Pushes result slot, then class object.
// Should be Object::null() if class is non-parameterized.
@@ -2072,7 +2071,7 @@
target::MakeTagWordForNewSpaceObject(cls_id, instance_size);
// Note: Keep in sync with helper function.
- const Register kTagsReg = R2;
+ const Register kTagsReg = AllocateObjectABI::kTagsReg;
__ LoadImmediate(kTagsReg, tags);
diff --git a/runtime/vm/compiler/stub_code_compiler_riscv.cc b/runtime/vm/compiler/stub_code_compiler_riscv.cc
index c9f604b..14dd9f3 100644
--- a/runtime/vm/compiler/stub_code_compiler_riscv.cc
+++ b/runtime/vm/compiler/stub_code_compiler_riscv.cc
@@ -1831,7 +1831,7 @@
static void GenerateAllocateObjectHelper(Assembler* assembler,
bool is_cls_parameterized) {
- const Register kTagsReg = T2;
+ const Register kTagsReg = AllocateObjectABI::kTagsReg;
{
Label slow_case;
@@ -1936,8 +1936,6 @@
}
void StubCodeCompiler::GenerateAllocateObjectSlowStub(Assembler* assembler) {
- const Register kTagsToClsIdReg = T2;
-
if (!FLAG_precompiled_mode) {
__ lx(CODE_REG,
Address(THR, target::Thread::call_to_runtime_stub_offset()));
@@ -1947,8 +1945,9 @@
// calling into the runtime.
__ EnterStubFrame();
- __ ExtractClassIdFromTags(kTagsToClsIdReg, kTagsToClsIdReg);
- __ LoadClassById(A0, kTagsToClsIdReg);
+ __ ExtractClassIdFromTags(AllocateObjectABI::kTagsReg,
+ AllocateObjectABI::kTagsReg);
+ __ LoadClassById(A0, AllocateObjectABI::kTagsReg);
__ subi(SP, SP, 3 * target::kWordSize);
__ sx(ZR, Address(SP, 2 * target::kWordSize)); // Result slot.
@@ -1993,7 +1992,7 @@
target::MakeTagWordForNewSpaceObject(cls_id, instance_size);
// Note: Keep in sync with helper function.
- const Register kTagsReg = T2;
+ const Register kTagsReg = AllocateObjectABI::kTagsReg;
ASSERT(kTagsReg != AllocateObjectABI::kTypeArgumentsReg);
__ LoadImmediate(kTagsReg, tags);
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 5e3ba12..de42236 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -1938,7 +1938,7 @@
static void GenerateAllocateObjectHelper(Assembler* assembler,
bool is_cls_parameterized) {
// Note: Keep in sync with calling function.
- const Register kTagsReg = R8;
+ const Register kTagsReg = AllocateObjectABI::kTagsReg;
{
Label slow_case;
@@ -2049,14 +2049,13 @@
}
void StubCodeCompiler::GenerateAllocateObjectSlowStub(Assembler* assembler) {
- const Register kTagsToClsIdReg = R8;
-
if (!FLAG_precompiled_mode) {
__ movq(CODE_REG,
Address(THR, target::Thread::call_to_runtime_stub_offset()));
}
- __ ExtractClassIdFromTags(kTagsToClsIdReg, kTagsToClsIdReg);
+ __ ExtractClassIdFromTags(AllocateObjectABI::kTagsReg,
+ AllocateObjectABI::kTagsReg);
// Create a stub frame.
// Ensure constant pool is allowed so we can e.g. load class object.
@@ -2067,7 +2066,7 @@
__ pushq(AllocateObjectABI::kResultReg);
// Push class of object to be allocated.
- __ LoadClassById(AllocateObjectABI::kResultReg, kTagsToClsIdReg);
+ __ LoadClassById(AllocateObjectABI::kResultReg, AllocateObjectABI::kTagsReg);
__ pushq(AllocateObjectABI::kResultReg);
// Must be Object::null() if non-parameterized class.
@@ -2118,7 +2117,7 @@
const uword tags =
target::MakeTagWordForNewSpaceObject(cls_id, instance_size);
- const Register kTagsReg = R8;
+ const Register kTagsReg = AllocateObjectABI::kTagsReg;
__ movq(kTagsReg, Immediate(tags));
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index 94b214a..b7f2374 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -470,6 +470,7 @@
struct AllocateObjectABI {
static const Register kResultReg = R0;
static const Register kTypeArgumentsReg = R3;
+ static const Register kTagsReg = R2;
};
// ABI for AllocateClosureStub.
diff --git a/runtime/vm/constants_arm64.h b/runtime/vm/constants_arm64.h
index 9e4213d..b2f4e6e 100644
--- a/runtime/vm/constants_arm64.h
+++ b/runtime/vm/constants_arm64.h
@@ -309,6 +309,7 @@
struct AllocateObjectABI {
static const Register kResultReg = R0;
static const Register kTypeArgumentsReg = R1;
+ static const Register kTagsReg = R2;
};
// ABI for AllocateClosureStub.
diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h
index bac9dd5..d06f97f 100644
--- a/runtime/vm/constants_ia32.h
+++ b/runtime/vm/constants_ia32.h
@@ -206,6 +206,7 @@
struct AllocateObjectABI {
static const Register kResultReg = EAX;
static const Register kTypeArgumentsReg = EDX;
+ static const Register kTagsReg = kNoRegister; // Not used.
};
// ABI for Allocate{Mint,Double,Float32x4,Float64x2}Stub.
diff --git a/runtime/vm/constants_riscv.h b/runtime/vm/constants_riscv.h
index d04317b..a03694c 100644
--- a/runtime/vm/constants_riscv.h
+++ b/runtime/vm/constants_riscv.h
@@ -326,6 +326,7 @@
struct AllocateObjectABI {
static constexpr Register kResultReg = A0;
static constexpr Register kTypeArgumentsReg = T1;
+ static const Register kTagsReg = T2;
};
// ABI for AllocateClosureStub.
diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h
index 2bc9444..aae0a4c 100644
--- a/runtime/vm/constants_x64.h
+++ b/runtime/vm/constants_x64.h
@@ -281,6 +281,7 @@
struct AllocateObjectABI {
static const Register kResultReg = RAX;
static const Register kTypeArgumentsReg = RDX;
+ static const Register kTagsReg = R8;
};
// ABI for AllocateClosureStub.
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index b8eaf21..88d7a2e 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -208,6 +208,7 @@
RW(Code, allocate_float64x2_array_stub) \
RW(Code, allocate_closure_stub) \
RW(Code, allocate_context_stub) \
+ RW(Code, allocate_growable_array_stub) \
RW(Code, allocate_object_stub) \
RW(Code, allocate_object_parametrized_stub) \
RW(Code, allocate_unhandled_exception_stub) \
@@ -284,6 +285,7 @@
DO(allocate_float64x2_array_stub, AllocateFloat64x2Array) \
DO(allocate_closure_stub, AllocateClosure) \
DO(allocate_context_stub, AllocateContext) \
+ DO(allocate_growable_array_stub, AllocateGrowableArray) \
DO(allocate_object_stub, AllocateObject) \
DO(allocate_object_parametrized_stub, AllocateObjectParameterized) \
DO(allocate_unhandled_exception_stub, AllocateUnhandledException) \
diff --git a/runtime/vm/stack_trace.cc b/runtime/vm/stack_trace.cc
index 0ef8b4d..9b665c2 100644
--- a/runtime/vm/stack_trace.cc
+++ b/runtime/vm/stack_trace.cc
@@ -75,7 +75,6 @@
controller_subscription_class(Class::Handle(zone)),
buffering_stream_subscription_class(Class::Handle(zone)),
stream_iterator_class(Class::Handle(zone)),
- async_then_wrapper_(Function::Handle(zone)),
future_result_or_listeners_field(Field::Handle(zone)),
callback_field(Field::Handle(zone)),
future_listener_state_field(Field::Handle(zone)),
@@ -112,9 +111,6 @@
stream_iterator_class =
async_lib.LookupClassAllowPrivate(Symbols::_StreamIterator());
ASSERT(!stream_iterator_class.IsNull());
- async_then_wrapper_ =
- async_lib.LookupFunctionAllowPrivate(Symbols::AsyncThenWrapperHelper());
- ASSERT(!async_then_wrapper_.IsNull());
// Look up fields:
// - async:
diff --git a/runtime/vm/stack_trace.h b/runtime/vm/stack_trace.h
index 6893a7f..566a0a8 100644
--- a/runtime/vm/stack_trace.h
+++ b/runtime/vm/stack_trace.h
@@ -82,7 +82,6 @@
Class& controller_subscription_class;
Class& buffering_stream_subscription_class;
Class& stream_iterator_class;
- Function& async_then_wrapper_;
Field& future_result_or_listeners_field;
Field& callback_field;
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index 6303447..be40522 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -172,6 +172,10 @@
switch (cls.id()) {
case kArrayCid:
return object_store->allocate_array_stub();
+#if !defined(TARGET_ARCH_IA32)
+ case kGrowableObjectArrayCid:
+ return object_store->allocate_growable_array_stub();
+#endif // !defined(TARGET_ARCH_IA32)
case kContextCid:
return object_store->allocate_context_stub();
case kUnhandledExceptionCid:
diff --git a/runtime/vm/stub_code_list.h b/runtime/vm/stub_code_list.h
index 778e08c..3607cbc 100644
--- a/runtime/vm/stub_code_list.h
+++ b/runtime/vm/stub_code_list.h
@@ -52,6 +52,7 @@
V(AllocateMintSharedWithoutFPURegs) \
V(AllocateClosure) \
V(AllocateContext) \
+ V(AllocateGrowableArray) \
V(AllocateObject) \
V(AllocateObjectParameterized) \
V(AllocateObjectSlow) \
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 707df3d..7784041 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -31,7 +31,6 @@
V(AsyncStarMoveNextHelper, "_asyncStarMoveNextHelper") \
V(AwaitContextVar, ":await_ctx_var") \
V(AwaitJumpVar, ":await_jump_var") \
- V(AsyncThenWrapperHelper, "_asyncThenWrapperHelper") \
V(Bool, "bool") \
V(BooleanExpression, "boolean expression") \
V(BoundsCheckForPartialInstantiation, "_boundsCheckForPartialInstantiation") \
diff --git a/sdk/lib/_internal/vm/lib/growable_array.dart b/sdk/lib/_internal/vm/lib/growable_array.dart
index c6d9e46..2c47725 100644
--- a/sdk/lib/_internal/vm/lib/growable_array.dart
+++ b/sdk/lib/_internal/vm/lib/growable_array.dart
@@ -222,7 +222,7 @@
return list;
}
- @pragma("vm:recognized", "asm-intrinsic")
+ @pragma("vm:recognized", "other")
@pragma("vm:exact-result-type",
<dynamic>[_GrowableList, "result-type-uses-passed-type-arguments"])
@pragma("vm:external-name", "GrowableList_allocate")
diff --git a/tools/VERSION b/tools/VERSION
index e933701..aeefac4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 124
+PRERELEASE 125
PRERELEASE_PATCH 0
\ No newline at end of file