Files
sdk/pkg/analysis_server/tool/code_completion/visitors.dart
T
Paul Berry afcfbbeba8 Migrate developer experience packages to new constructor decl syntax.
(Part of https://github.com/dart-lang/sdk/issues/63288)

This change migrates the packages owned by the developer experience
team to use the new constructor declaration syntax, described in
https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations.

This change was performed in an automated fashion, by (a) bumping the
packages' SDK constraints to `3.13.0-0`, (b) enabling the lints
`unnecessary_type_name_in_constructor` and
`unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint
failures using `dart fix`, and then (d) reformatting the affected
files.

To ease code review, I've reverted unrelated formatting changes.

Since this change requires bumping SDK constaints to `3.13.0-0`, it
was only performed on packages that are *not* published on
pub. (Packages that *are* published on pub should remain on lower
language versions until at least after the stable version of 3.13 is
released, so that we don't block users on the stable channel from
receiving updates to those packages.)

Change-Id: Ibb4daebafd239da58251e838ea6a3f336a6a6964
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/505046
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
2026-05-27 14:52:58 -07:00

893 lines
27 KiB
Dart

// Copyright (c) 2020, 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/protocol_internal.dart';
import 'package:analysis_server/src/protocol_server.dart' as protocol;
import 'package:analysis_server/src/services/completion/dart/candidate_suggestion.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart' as element;
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/source/line_info.dart';
class ExpectedCompletion {
final String _filePath;
final SyntacticEntity _entity;
/// Some completions are special cased from the DAS "import" for instance is
/// suggested as a completion "import '';", the completion string here in this
/// instance would have the value "import '';".
final String? _completionString;
final protocol.CompletionSuggestionKind? _kind;
final int _lineNumber;
final int _columnNumber;
final protocol.ElementKind? _elementKind;
new(
this._filePath,
this._entity,
this._lineNumber,
this._columnNumber,
this._kind,
this._elementKind,
) : _completionString = null;
/// Return an instance extracted from the decoded JSON [map].
factory fromJson(Map<String, dynamic> map) {
var jsonDecoder = ResponseDecoder(null);
var filePath = map['filePath'] as String;
var offset = map['offset'] as int;
var lineNumber = map['lineNumber'] as int;
var columnNumber = map['columnNumber'] as int;
var completionString = map['completionString'] as String;
var kind = map['kind'] != null
? protocol.CompletionSuggestionKind.fromJson(
jsonDecoder,
'',
map['kind'] as String,
)
: null;
var elementKind = map['elementKind'] != null
? protocol.ElementKind.fromJson(
jsonDecoder,
'',
map['elementKind'] as String,
)
: null;
return ExpectedCompletion.specialCompletionString(
filePath,
_SyntacticEntity(offset),
lineNumber,
columnNumber,
completionString,
kind,
elementKind,
);
}
new specialCompletionString(
this._filePath,
this._entity,
this._lineNumber,
this._columnNumber,
this._completionString,
this._kind,
this._elementKind,
);
int get columnNumber => _columnNumber;
String get completion => _completionString ?? _entity.toString();
protocol.ElementKind? get elementKind => _elementKind;
String get filePath => _filePath;
protocol.CompletionSuggestionKind? get kind => _kind;
int get lineNumber => _lineNumber;
String get location => '$filePath:$lineNumber:$columnNumber';
int get offset => _entity.offset;
SyntacticEntity get syntacticEntity => _entity;
bool matches(CandidateSuggestion completionSuggestion) {
// This method used to use the `_kind` and `_elementKind` to further
// validate that the right suggestion had been found, but that was removed
// because candidate suggestions don't have that information.
return completionSuggestion.completion == completion;
}
/// Return a map used to represent this expected completion in a JSON structure.
Map<String, dynamic> toJson() {
var kind = _kind;
var elementKind = _elementKind;
return {
'filePath': _filePath,
'offset': _entity.offset,
'lineNumber': _lineNumber,
'columnNumber': _columnNumber,
'completionString': _completionString,
if (kind != null) 'kind': kind.toJson(),
if (elementKind != null) 'elementKind': elementKind.toJson(),
};
}
@override
String toString() =>
"'$completion', kind = $kind, elementKind = $elementKind, $location";
}
class ExpectedCompletionsVisitor extends RecursiveAstVisitor<void> {
static const _asyncStar = 'async*';
static const _defaultColon = 'default:';
static const _deferredAs = 'deferred as';
static const _exportStatement = "export '';";
static const _importStatement = "import '';";
static const _partStatement = "part '';";
static const _syncStar = 'sync*';
static const _yieldStar = 'yield*';
/// The result of resolving the file being visited.
final ResolvedUnitResult result;
/// The offset from the location of each entity passed to
/// [safelyRecordEntity], to be used as the column for each
/// [ExpectedCompletion] created.
final int _caretOffset;
/// The completions that are expected to be produced in the file being
/// visited.
final List<ExpectedCompletion> expectedCompletions = [];
/// This boolean is set to enable whether or not we should assert that some
/// found keyword in Dart syntax should be in the completion set returned from
/// the analysis server. This is off by default because with syntax such as
/// "^get foo() => _foo;", "get" and "set" won't be suggested because this
/// syntax has specified the "get" modifier, i.e. it would be invalid to
/// include it again: "get get foo() => _foo;".
final bool _doExpectKeywordCompletions = false;
/// This boolean is set to enable if identifiers in comments should be
/// expected to be completed. The default is false as typos in a dartdoc
/// comment don't yield an error like Dart syntax mistakes would yield.
final bool _doExpectCommentRefs = false;
new(this.result, {required this._caretOffset});
/// Return the path of the file that is being visited.
String get filePath => result.path;
/// Return the line info for the file that is being visited.
LineInfo get lineInfo => result.lineInfo;
void safelyRecordEntity(
SyntacticEntity? entity, {
protocol.CompletionSuggestionKind? kind,
protocol.ElementKind? elementKind,
}) {
// Only record if this entity is not null, has a length, etc.
if (entity != null && entity.offset >= 0 && entity.length > 0) {
var location = lineInfo.getLocation(entity.offset);
var lineNumber = location.lineNumber;
var columnNumber = location.columnNumber;
if (entity.length >= _caretOffset) {
columnNumber += _caretOffset;
}
bool isKeyword() => kind == protocol.CompletionSuggestionKind.KEYWORD;
// Some special cases in the if and if-else blocks, 'import' from the
// DAS is "import '';" which we want to be sure to match.
var lexeme = entity.toString();
if (lexeme == 'async') {
expectedCompletions.add(
ExpectedCompletion.specialCompletionString(
filePath,
entity,
lineNumber,
columnNumber,
_asyncStar,
kind,
elementKind,
),
);
} else if (lexeme == 'default') {
expectedCompletions.add(
ExpectedCompletion.specialCompletionString(
filePath,
entity,
lineNumber,
columnNumber,
_defaultColon,
kind,
elementKind,
),
);
} else if (lexeme == 'deferred') {
expectedCompletions.add(
ExpectedCompletion.specialCompletionString(
filePath,
entity,
lineNumber,
columnNumber,
_deferredAs,
kind,
elementKind,
),
);
} else if (lexeme == 'export' && isKeyword()) {
expectedCompletions.add(
ExpectedCompletion.specialCompletionString(
filePath,
entity,
lineNumber,
columnNumber,
_exportStatement,
kind,
elementKind,
),
);
} else if (lexeme == 'import' && isKeyword()) {
expectedCompletions.add(
ExpectedCompletion.specialCompletionString(
filePath,
entity,
lineNumber,
columnNumber,
_importStatement,
kind,
elementKind,
),
);
} else if (lexeme == 'part' && isKeyword()) {
expectedCompletions.add(
ExpectedCompletion.specialCompletionString(
filePath,
entity,
lineNumber,
columnNumber,
_partStatement,
kind,
elementKind,
),
);
} else if (lexeme == 'sync') {
expectedCompletions.add(
ExpectedCompletion.specialCompletionString(
filePath,
entity,
lineNumber,
columnNumber,
_syncStar,
kind,
elementKind,
),
);
} else if (lexeme == 'yield') {
expectedCompletions.add(
ExpectedCompletion.specialCompletionString(
filePath,
entity,
lineNumber,
columnNumber,
_yieldStar,
kind,
elementKind,
),
);
} else {
expectedCompletions.add(
ExpectedCompletion(
filePath,
entity,
lineNumber,
columnNumber,
kind,
elementKind,
),
);
}
}
}
void safelyRecordKeywordCompletion(SyntacticEntity? entity) {
if (_doExpectKeywordCompletions) {
safelyRecordEntity(
entity,
kind: protocol.CompletionSuggestionKind.KEYWORD,
);
}
}
@override
void visitAsExpression(AsExpression node) {
safelyRecordKeywordCompletion(node.asOperator);
super.visitAsExpression(node);
}
@override
void visitAssertInitializer(AssertInitializer node) {
safelyRecordKeywordCompletion(node.assertKeyword);
super.visitAssertInitializer(node);
}
@override
void visitAssertStatement(AssertStatement node) {
safelyRecordKeywordCompletion(node.assertKeyword);
super.visitAssertStatement(node);
}
@override
void visitAwaitExpression(AwaitExpression node) {
safelyRecordKeywordCompletion(node.awaitKeyword);
super.visitAwaitExpression(node);
}
@override
void visitBlockFunctionBody(BlockFunctionBody node) {
// 'async' | 'async' '*' | 'sync' '*':
safelyRecordKeywordCompletion(node.keyword);
super.visitBlockFunctionBody(node);
}
@override
void visitBooleanLiteral(BooleanLiteral node) {
// 'false' | 'true'
safelyRecordKeywordCompletion(node.literal);
super.visitBooleanLiteral(node);
}
@override
void visitBreakStatement(BreakStatement node) {
safelyRecordKeywordCompletion(node.breakKeyword);
super.visitBreakStatement(node);
}
@override
void visitCatchClause(CatchClause node) {
safelyRecordKeywordCompletion(node.catchKeyword);
safelyRecordKeywordCompletion(node.onKeyword);
super.visitCatchClause(node);
}
@override
void visitClassDeclaration(ClassDeclaration node) {
safelyRecordKeywordCompletion(node.abstractKeyword);
safelyRecordKeywordCompletion(node.classKeyword);
super.visitClassDeclaration(node);
}
@override
void visitClassTypeAlias(ClassTypeAlias node) {
safelyRecordKeywordCompletion(node.abstractKeyword);
safelyRecordKeywordCompletion(node.typedefKeyword);
super.visitClassTypeAlias(node);
}
@override
void visitConfiguration(Configuration node) {
safelyRecordKeywordCompletion(node.ifKeyword);
super.visitConfiguration(node);
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
safelyRecordKeywordCompletion(node.externalKeyword);
safelyRecordKeywordCompletion(node.constKeyword);
safelyRecordKeywordCompletion(node.factoryKeyword);
super.visitConstructorDeclaration(node);
}
@override
void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
safelyRecordKeywordCompletion(node.thisKeyword);
super.visitConstructorFieldInitializer(node);
}
@override
void visitContinueStatement(ContinueStatement node) {
safelyRecordKeywordCompletion(node.continueKeyword);
super.visitContinueStatement(node);
}
@override
void visitDeclaredIdentifier(DeclaredIdentifier node) {
// 'final', 'const' or 'var'
safelyRecordKeywordCompletion(node.keyword);
super.visitDeclaredIdentifier(node);
}
@override
void visitDoStatement(DoStatement node) {
safelyRecordKeywordCompletion(node.doKeyword);
safelyRecordKeywordCompletion(node.whileKeyword);
super.visitDoStatement(node);
}
@override
void visitEnumDeclaration(EnumDeclaration node) {
safelyRecordKeywordCompletion(node.enumKeyword);
super.visitEnumDeclaration(node);
}
@override
void visitExportDirective(ExportDirective node) {
safelyRecordKeywordCompletion(node.exportKeyword);
super.visitExportDirective(node);
}
@override
void visitExpressionFunctionBody(ExpressionFunctionBody node) {
safelyRecordKeywordCompletion(node.keyword);
super.visitExpressionFunctionBody(node);
}
@override
void visitExtendsClause(ExtendsClause node) {
safelyRecordKeywordCompletion(node.extendsKeyword);
super.visitExtendsClause(node);
}
@override
void visitExtensionDeclaration(ExtensionDeclaration node) {
safelyRecordKeywordCompletion(node.extensionKeyword);
safelyRecordKeywordCompletion(node.onClause);
super.visitExtensionDeclaration(node);
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
safelyRecordKeywordCompletion(node.abstractKeyword);
safelyRecordKeywordCompletion(node.covariantKeyword);
safelyRecordKeywordCompletion(node.externalKeyword);
safelyRecordKeywordCompletion(node.staticKeyword);
super.visitFieldDeclaration(node);
}
@override
void visitFieldFormalParameter(FieldFormalParameter node) {
// 'final', 'const' or 'var'
safelyRecordKeywordCompletion(node.constFinalOrVarKeyword);
safelyRecordKeywordCompletion(node.thisKeyword);
super.visitFieldFormalParameter(node);
}
@override
void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
safelyRecordKeywordCompletion(node.inKeyword);
super.visitForEachPartsWithDeclaration(node);
}
@override
void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) {
safelyRecordKeywordCompletion(node.inKeyword);
super.visitForEachPartsWithIdentifier(node);
}
@override
void visitForElement(ForElement node) {
safelyRecordKeywordCompletion(node.awaitKeyword);
safelyRecordKeywordCompletion(node.forKeyword);
super.visitForElement(node);
}
@override
void visitForStatement(ForStatement node) {
safelyRecordKeywordCompletion(node.awaitKeyword);
safelyRecordKeywordCompletion(node.forKeyword);
super.visitForStatement(node);
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
safelyRecordKeywordCompletion(node.externalKeyword);
// 'get' or 'set':
safelyRecordKeywordCompletion(node.propertyKeyword);
super.visitFunctionDeclaration(node);
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
safelyRecordKeywordCompletion(node.typedefKeyword);
super.visitFunctionTypeAlias(node);
}
@override
void visitGenericFunctionType(GenericFunctionType node) {
safelyRecordKeywordCompletion(node.functionKeyword);
super.visitGenericFunctionType(node);
}
@override
void visitGenericTypeAlias(GenericTypeAlias node) {
safelyRecordKeywordCompletion(node.typedefKeyword);
super.visitGenericTypeAlias(node);
}
@override
void visitHideCombinator(HideCombinator node) {
safelyRecordKeywordCompletion(node.keyword);
super.visitHideCombinator(node);
}
@override
void visitIfElement(IfElement node) {
safelyRecordKeywordCompletion(node.ifKeyword);
safelyRecordKeywordCompletion(node.elseKeyword);
super.visitIfElement(node);
}
@override
void visitIfStatement(IfStatement node) {
safelyRecordKeywordCompletion(node.ifKeyword);
safelyRecordKeywordCompletion(node.elseKeyword);
super.visitIfStatement(node);
}
@override
void visitImplementsClause(ImplementsClause node) {
safelyRecordKeywordCompletion(node.implementsKeyword);
super.visitImplementsClause(node);
}
@override
void visitImportDirective(ImportDirective node) {
safelyRecordKeywordCompletion(node.importKeyword);
safelyRecordKeywordCompletion(node.asKeyword);
safelyRecordKeywordCompletion(node.deferredKeyword);
super.visitImportDirective(node);
}
@override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
// Here we explicitly do not record 'new' as we don't suggest it in the
// completion service.
// https://dart-review.googlesource.com/c/sdk/+/131020
if (node.keyword?.type == Keyword.CONST) {
safelyRecordKeywordCompletion(node.keyword);
}
super.visitInstanceCreationExpression(node);
}
@override
void visitIsExpression(IsExpression node) {
safelyRecordKeywordCompletion(node.isOperator);
super.visitIsExpression(node);
}
@override
void visitLibraryDirective(LibraryDirective node) {
safelyRecordKeywordCompletion(node.libraryKeyword);
super.visitLibraryDirective(node);
}
@override
void visitListLiteral(ListLiteral node) {
safelyRecordKeywordCompletion(node.constKeyword);
super.visitListLiteral(node);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
safelyRecordKeywordCompletion(node.externalKeyword);
safelyRecordKeywordCompletion(node.modifierKeyword);
safelyRecordKeywordCompletion(node.operatorKeyword);
safelyRecordKeywordCompletion(node.propertyKeyword);
super.visitMethodDeclaration(node);
}
@override
void visitMixinDeclaration(MixinDeclaration node) {
safelyRecordKeywordCompletion(node.mixinKeyword);
super.visitMixinDeclaration(node);
}
@override
void visitMixinOnClause(MixinOnClause node) {
safelyRecordKeywordCompletion(node.onKeyword);
super.visitMixinOnClause(node);
}
@override
void visitNativeClause(NativeClause node) {
safelyRecordKeywordCompletion(node.nativeKeyword);
super.visitNativeClause(node);
}
@override
void visitNativeFunctionBody(NativeFunctionBody node) {
safelyRecordKeywordCompletion(node.nativeKeyword);
super.visitNativeFunctionBody(node);
}
@override
void visitNullLiteral(NullLiteral node) {
safelyRecordKeywordCompletion(node.literal);
super.visitNullLiteral(node);
}
@override
void visitPartDirective(PartDirective node) {
safelyRecordKeywordCompletion(node.partKeyword);
super.visitPartDirective(node);
}
@override
void visitPartOfDirective(PartOfDirective node) {
safelyRecordKeywordCompletion(node.partKeyword);
safelyRecordKeywordCompletion(node.ofKeyword);
super.visitPartOfDirective(node);
}
@override
void visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node,
) {
safelyRecordKeywordCompletion(node.thisKeyword);
super.visitRedirectingConstructorInvocation(node);
}
@override
void visitRegularFormalParameter(RegularFormalParameter node) {
// 'final', 'const' or 'var'
safelyRecordKeywordCompletion(node.constFinalOrVarKeyword);
safelyRecordKeywordCompletion(node.covariantKeyword);
super.visitRegularFormalParameter(node);
}
@override
void visitRethrowExpression(RethrowExpression node) {
safelyRecordKeywordCompletion(node.rethrowKeyword);
return super.visitRethrowExpression(node);
}
@override
void visitReturnStatement(ReturnStatement node) {
safelyRecordKeywordCompletion(node.returnKeyword);
super.visitReturnStatement(node);
}
@override
void visitSetOrMapLiteral(SetOrMapLiteral node) {
safelyRecordKeywordCompletion(node.constKeyword);
super.visitSetOrMapLiteral(node);
}
@override
void visitShowCombinator(ShowCombinator node) {
safelyRecordKeywordCompletion(node.keyword);
super.visitShowCombinator(node);
}
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
if (_doIncludeSimpleIdentifier(node)) {
protocol.ElementKind? elementKind;
var kind = node.element?.kind;
if (kind != null) {
elementKind = protocol.convertElementKind(kind);
// If the completed element kind is a getter or setter set the element
// kind to null as the exact kind from the DAS is unknown at this
// point.
if (elementKind == protocol.ElementKind.GETTER ||
elementKind == protocol.ElementKind.SETTER) {
elementKind = null;
}
// Map PREFIX element kinds to LIBRARY kinds as this is the ElementKind
// used for prefixes reported from the completion engine.
if (elementKind == protocol.ElementKind.PREFIX) {
elementKind = protocol.ElementKind.LIBRARY;
}
// For identifiers in a FieldFormalParameter, i.e. the 'foo' in some
// ClassName(this.foo), set the elementKind to FIELD which is what the
// completion engine does (elementKind before this if statement is a
// PARAMETER).
if (node.parent is FieldFormalParameter) {
elementKind = protocol.ElementKind.FIELD;
}
// Class references that are constructor calls are constructor kinds,
// unless either:
// 1) the constructor is a named constructor, i.e. some "Foo.bar()",
// the "Foo" in this case is a class,
// 2) or, there is an explicit const or new keyword before the
// constructor invocation in which case the "Foo" above is
// considered a constructor still
if (elementKind == protocol.ElementKind.CLASS) {
var constructorName = node.parent?.parent;
if (constructorName is ConstructorName) {
// TODO(scheglov): Commented out, probably does not work now.
// var instanceCreationExpression = constructorName.parent;
// if (instanceCreationExpression is InstanceCreationExpression &&
// constructorName.type.name == node) {
// if (instanceCreationExpression.keyword != null ||
// constructorName.name == null) {
// elementKind = protocol.ElementKind.CONSTRUCTOR;
// }
// }
}
}
// A class reference followed by parens in an annotation is a
// constructor reference.
if (elementKind == protocol.ElementKind.CLASS &&
node.thisOrAncestorOfType<Annotation>() != null &&
node.endToken.next?.type == TokenType.OPEN_PAREN) {
elementKind = protocol.ElementKind.CONSTRUCTOR;
}
}
safelyRecordEntity(node, elementKind: elementKind);
}
super.visitSimpleIdentifier(node);
}
@override
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
safelyRecordKeywordCompletion(node.superKeyword);
super.visitSuperConstructorInvocation(node);
}
@override
void visitSuperExpression(SuperExpression node) {
safelyRecordKeywordCompletion(node.superKeyword);
super.visitSuperExpression(node);
}
@override
void visitSwitchCase(SwitchCase node) {
safelyRecordKeywordCompletion(node.keyword);
super.visitSwitchCase(node);
}
@override
void visitSwitchDefault(SwitchDefault node) {
safelyRecordKeywordCompletion(node.keyword);
super.visitSwitchDefault(node);
}
@override
void visitSwitchStatement(SwitchStatement node) {
safelyRecordKeywordCompletion(node.switchKeyword);
super.visitSwitchStatement(node);
}
@override
void visitThisExpression(ThisExpression node) {
safelyRecordKeywordCompletion(node.thisKeyword);
super.visitThisExpression(node);
}
@override
void visitThrowExpression(ThrowExpression node) {
safelyRecordKeywordCompletion(node.throwKeyword);
super.visitThrowExpression(node);
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
safelyRecordKeywordCompletion(node.externalKeyword);
super.visitTopLevelVariableDeclaration(node);
}
@override
void visitTryStatement(TryStatement node) {
safelyRecordKeywordCompletion(node.tryKeyword);
safelyRecordKeywordCompletion(node.finallyKeyword);
super.visitTryStatement(node);
}
@override
void visitTypeParameter(TypeParameter node) {
safelyRecordKeywordCompletion(node.extendsKeyword);
super.visitTypeParameter(node);
}
@override
void visitVariableDeclarationList(VariableDeclarationList node) {
// 'final', 'const' or 'var'
safelyRecordKeywordCompletion(node.keyword);
safelyRecordKeywordCompletion(node.lateKeyword);
super.visitVariableDeclarationList(node);
}
@override
void visitWhileStatement(WhileStatement node) {
safelyRecordKeywordCompletion(node.whileKeyword);
super.visitWhileStatement(node);
}
@override
void visitWithClause(WithClause node) {
safelyRecordKeywordCompletion(node.withKeyword);
super.visitWithClause(node);
}
@override
void visitYieldStatement(YieldStatement node) {
safelyRecordKeywordCompletion(node.yieldKeyword);
super.visitYieldStatement(node);
}
bool _doIncludeSimpleIdentifier(SimpleIdentifier node) {
// Do not continue if this node is synthetic, or if the node is in a
// declaration context
if (node.isSynthetic || node.inDeclarationContext()) {
return false;
}
// If the type of the SimpleIdentifier is dynamic, don't include.
var staticType = node.staticType;
if (staticType != null && staticType is DynamicType) {
return false;
}
// If we are in a comment reference context, return if we should include
// such identifiers.
if (node.thisOrAncestorOfType<CommentReference>() != null) {
return _doExpectCommentRefs;
}
// Ignore the SimpleIdentifiers that make up library directives.
if (node.thisOrAncestorOfType<LibraryDirective>() != null) {
return false;
}
// Ignore identifiers in hide and show combinators because we don't suggest
// names that are already in the list.
if (node.parent is HideCombinator || node.parent is ShowCombinator) {
return false;
}
// TODO(jwren): If there is a mode of completing at a token location where
// the token is removed before the completion query happens, then this
// should be disabled in such a case:
// Named arguments, i.e. the 'foo' in 'method_call(foo: 1)' should not be
// included, by design, the completion engine won't suggest named arguments
// already it the source.
//
// The null check, node.staticElement == null, handles the cases where the
// invocation is unknown, i.e. the 'arg' in 'foo.bar(arg: 1)', where foo is
// dynamic.
if ((node.element == null ||
node.element?.kind == element.ElementKind.PARAMETER) &&
node.parent is Label &&
node.thisOrAncestorOfType<ArgumentList>() != null) {
return false;
}
return true;
}
}
class _SyntacticEntity extends SyntacticEntity {
@override
final int offset;
new(this.offset);
@override
int get end => offset + length;
@override
int get length => 0;
}