Files
sdk/pkg/analysis_server/tool/code_completion/visitors.dart
T
Paul Berry 8600df906e [messages] Add SyntacticEntity.sourceRange getter.
This will allow diagnostic reporting code in the analyzer to be more
easily converted to the new literate diagnostic reporting API, because
it will make it possible to use `SourceRange` as the common
denominator for situations where the location where a diagnostic needs
to be reported might come from either a `SyntacticEntity` or a
`SourceRange`.

Change-Id: I6a6a6964f3ec2ea33e8900d93b5abe714c9e1dcc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/467682
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2025-12-15 09:02:26 -08:00

894 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;
ExpectedCompletion(
this._filePath,
this._entity,
this._lineNumber,
this._columnNumber,
this._kind,
this._elementKind,
) : _completionString = null;
/// Return an instance extracted from the decoded JSON [map].
factory ExpectedCompletion.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,
);
}
ExpectedCompletion.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;
ExpectedCompletionsVisitor(this.result, {required int caretOffset})
: _caretOffset = 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.keyword);
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 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 visitSimpleFormalParameter(SimpleFormalParameter node) {
// 'final', 'const' or 'var'
safelyRecordKeywordCompletion(node.keyword);
safelyRecordKeywordCompletion(node.covariantKeyword);
super.visitSimpleFormalParameter(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;
_SyntacticEntity(this.offset);
@override
int get end => offset + length;
@override
int get length => 0;
}