Convert about half of LocalReferenceContributor to use the SuggestionBuilder

I'm planning on converting the rest of the class in the next CL, but
thought that a combined CL would be too large.

Change-Id: Ic18ead8285019791053f696daee0b5182c4740de
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/145600
Reviewed-by: Jaime Wren <jwren@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Brian Wilkerson 2020-04-30 16:58:57 +00:00 committed by commit-bot@chromium.org
parent 52f9744c3a
commit cea45271b3
5 changed files with 142 additions and 216 deletions

View file

@ -102,8 +102,7 @@ ElementKind convertElementToElementKind(engine.Element element) {
if (element is engine.ClassElement) {
if (element.isEnum) {
return ElementKind.ENUM;
}
if (element.isMixin) {
} else if (element.isMixin) {
return ElementKind.MIXIN;
}
}

View file

@ -119,7 +119,7 @@ class FeatureComputer {
/// Return the value of the _has deprecated_ feature for the given [element].
double hasDeprecatedFeature(Element element) {
return element.hasDeprecated ? 0.0 : 1.0;
return element.hasOrInheritsDeprecated ? 0.0 : 1.0;
}
/// Return the inheritance distance between the [subclass] and the
@ -539,3 +539,19 @@ class _ContextTypeVisitor extends SimpleAstVisitor<DartType> {
return null;
}
}
/// Additional behavior for elements related to the computation of features.
extension ElementExtension on Element {
/// Return `true` if this element, or any enclosing element, has been
/// annotated with the `@deprecated` annotation.
bool get hasOrInheritsDeprecated {
var element = this;
while (element != null) {
if (element.hasDeprecated) {
return true;
}
element = element.enclosingElement;
}
return false;
}
}

View file

@ -6,21 +6,19 @@ import 'dart:async';
import 'package:analysis_server/src/computer/computer_hover.dart';
import 'package:analysis_server/src/protocol_server.dart'
show CompletionSuggestion, CompletionSuggestionKind, Location;
show CompletionSuggestion, CompletionSuggestionKind;
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
import 'package:analysis_server/src/services/completion/dart/utilities.dart';
import 'package:analysis_server/src/utilities/strings.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.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/dart/element/type_provider.dart';
import 'package:analyzer/src/util/comment.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol
show Element, ElementKind;
show ElementKind;
import 'package:analyzer_plugin/src/utilities/completion/optype.dart';
import 'package:analyzer_plugin/src/utilities/visitors/local_declaration_visitor.dart'
show LocalDeclarationVisitor;
@ -59,9 +57,14 @@ class LocalReferenceContributor extends DartCompletionContributor {
node = node.parent.parent;
}
var visitor =
_LocalVisitor(request, suggestLocalFields: suggestLocalFields);
visitor.visit(node);
var visitor = _LocalVisitor(request, builder,
suggestLocalFields: suggestLocalFields);
try {
builder.laterReplacesEarlier = false;
visitor.visit(node);
} finally {
builder.laterReplacesEarlier = true;
}
return visitor.suggestions;
}
}
@ -75,6 +78,9 @@ class _LocalVisitor extends LocalDeclarationVisitor {
/// The request for which suggestions are being computed.
final DartCompletionRequest request;
/// The builder used to build the suggestions.
final SuggestionBuilder builder;
/// The op type associated with the request.
final OpType opType;
@ -99,7 +105,7 @@ class _LocalVisitor extends LocalDeclarationVisitor {
/// Only used when [useNewRelevance] is `false`.
int privateMemberRelevance = DART_RELEVANCE_DEFAULT;
_LocalVisitor(this.request, {@required this.suggestLocalFields})
_LocalVisitor(this.request, this.builder, {@required this.suggestLocalFields})
: opType = request.opType,
useNewRelevance = request.useNewRelevance,
targetIsFunctionalArgument = request.target.isFunctionalArgument(),
@ -139,54 +145,31 @@ class _LocalVisitor extends LocalDeclarationVisitor {
TypeProvider get typeProvider => request.libraryElement.typeProvider;
CompletionSuggestionKind get _defaultKind => targetIsFunctionalArgument
? CompletionSuggestionKind.IDENTIFIER
: opType.suggestKind;
@override
void declaredClass(ClassDeclaration declaration) {
if (opType.includeTypeNameSuggestions) {
_addLocalSuggestion_includeTypeNameSuggestions(
declaration.declaredElement,
declaration.name,
NO_RETURN_TYPE,
protocol.ElementKind.CLASS,
isAbstract: declaration.isAbstract,
isDeprecated: isDeprecated(declaration),
type: _instantiateClassElement(declaration.declaredElement),
);
builder.suggestClass(declaration.declaredElement, kind: _defaultKind);
}
}
@override
void declaredClassTypeAlias(ClassTypeAlias declaration) {
if (opType.includeTypeNameSuggestions) {
_addLocalSuggestion_includeTypeNameSuggestions(
declaration.declaredElement,
declaration.name,
NO_RETURN_TYPE,
protocol.ElementKind.CLASS_TYPE_ALIAS,
isAbstract: true,
isDeprecated: isDeprecated(declaration),
type: _instantiateClassElement(declaration.declaredElement),
);
builder.suggestClass(declaration.declaredElement, kind: _defaultKind);
}
}
@override
void declaredEnum(EnumDeclaration declaration) {
if (opType.includeTypeNameSuggestions) {
_addLocalSuggestion_includeTypeNameSuggestions(
declaration.declaredElement,
declaration.name,
NO_RETURN_TYPE,
protocol.ElementKind.ENUM,
isDeprecated: isDeprecated(declaration),
type: _instantiateClassElement(declaration.declaredElement),
);
builder.suggestClass(declaration.declaredElement, kind: _defaultKind);
for (var enumConstant in declaration.constants) {
if (!enumConstant.isSynthetic) {
_addLocalSuggestion_includeReturnValueSuggestions_enumConstant(
enumConstant,
declaration,
isDeprecated: isDeprecated(declaration),
);
builder.suggestEnumConstant(enumConstant.declaredElement);
}
}
}
@ -195,14 +178,7 @@ class _LocalVisitor extends LocalDeclarationVisitor {
@override
void declaredExtension(ExtensionDeclaration declaration) {
if (opType.includeReturnValueSuggestions && declaration.name != null) {
_addLocalSuggestion_includeReturnValueSuggestions(
declaration.declaredElement,
declaration.name,
NO_RETURN_TYPE,
protocol.ElementKind.EXTENSION,
isDeprecated: isDeprecated(declaration),
type: null,
);
builder.suggestExtension(declaration.declaredElement, kind: _defaultKind);
}
}
@ -277,32 +253,16 @@ class _LocalVisitor extends LocalDeclarationVisitor {
@override
void declaredFunctionTypeAlias(FunctionTypeAlias declaration) {
if (opType.includeTypeNameSuggestions) {
// TODO (danrubel) determine parameters and return type
_addLocalSuggestion_includeTypeNameSuggestions(
declaration.declaredElement,
declaration.name,
declaration.returnType,
protocol.ElementKind.FUNCTION_TYPE_ALIAS,
isAbstract: true,
isDeprecated: isDeprecated(declaration),
type: _instantiateFunctionTypeAlias(declaration.declaredElement),
);
builder.suggestFunctionTypeAlias(declaration.declaredElement,
kind: _defaultKind);
}
}
@override
void declaredGenericTypeAlias(GenericTypeAlias declaration) {
if (opType.includeTypeNameSuggestions) {
// TODO (danrubel) determine parameters and return type
_addLocalSuggestion_includeTypeNameSuggestions(
declaration.declaredElement,
declaration.name,
declaration.functionType?.returnType,
protocol.ElementKind.FUNCTION_TYPE_ALIAS,
isAbstract: true,
isDeprecated: isDeprecated(declaration),
type: _instantiateFunctionTypeAlias(declaration.declaredElement),
);
builder.suggestFunctionTypeAlias(declaration.declaredElement,
kind: _defaultKind);
}
}
@ -339,6 +299,11 @@ class _LocalVisitor extends LocalDeclarationVisitor {
if ((opType.includeReturnValueSuggestions ||
opType.includeVoidReturnSuggestions) &&
(!opType.inStaticMethodBody || declaration.isStatic)) {
// var method = declaration.declaredElement;
// // TODO(brianwilkerson) Use request.featureComputer.inheritanceDistance to
// // compute the inheritance distance.
// var inheritanceDistance = -1.0;
// builder.suggestMethod(method, inheritanceDistance: inheritanceDistance, kind: _defaultKind);
protocol.ElementKind elemKind;
FormalParameterList param;
var typeName = declaration.returnType;
@ -383,15 +348,7 @@ class _LocalVisitor extends LocalDeclarationVisitor {
@override
void declaredMixin(MixinDeclaration declaration) {
if (opType.includeTypeNameSuggestions) {
_addLocalSuggestion_includeTypeNameSuggestions(
declaration.declaredElement,
declaration.name,
NO_RETURN_TYPE,
protocol.ElementKind.MIXIN,
isAbstract: true,
isDeprecated: isDeprecated(declaration),
type: _instantiateClassElement(declaration.declaredElement),
);
builder.suggestClass(declaration.declaredElement, kind: _defaultKind);
}
}
@ -421,6 +378,7 @@ class _LocalVisitor extends LocalDeclarationVisitor {
VariableDeclarationList varList, VariableDeclaration varDecl) {
if (opType.includeReturnValueSuggestions) {
var variableElement = varDecl.declaredElement;
// builder.suggestTopLevelPropertyAccessor((variableElement as TopLevelVariableElement).getter);
var variableType = variableElement.type;
int relevance;
if (useNewRelevance) {
@ -472,9 +430,7 @@ class _LocalVisitor extends LocalDeclarationVisitor {
if (id == null) {
return null;
}
kind ??= targetIsFunctionalArgument
? CompletionSuggestionKind.IDENTIFIER
: opType.suggestKind;
kind ??= _defaultKind;
var suggestion = _createLocalSuggestion(
id, isDeprecated, relevance, typeName,
classDecl: classDecl, kind: kind);
@ -499,32 +455,6 @@ class _LocalVisitor extends LocalDeclarationVisitor {
}
}
void _addLocalSuggestion_enumConstant(
EnumConstantDeclaration constantDeclaration,
EnumDeclaration enumDeclaration,
{bool isDeprecated = false,
int relevance = DART_RELEVANCE_DEFAULT}) {
var constantNameNode = constantDeclaration.name;
var constantName = constantNameNode.name;
var enumName = enumDeclaration.name.name;
var completion = '$enumName.$constantName';
if (!useNewRelevance) {
relevance = isDeprecated ? DART_RELEVANCE_LOW : relevance;
}
var suggestion = CompletionSuggestion(CompletionSuggestionKind.INVOCATION,
relevance, completion, completion.length, 0, isDeprecated, false,
returnType: enumName);
suggestionMap.putIfAbsent(suggestion.completion, () => suggestion);
var flags = protocol.Element.makeFlags(
isDeprecated: isDeprecated,
isPrivate: Identifier.isPrivateName(constantName));
suggestion.element = protocol.Element(
protocol.ElementKind.ENUM_CONSTANT, constantName, flags,
location: Location(request.source.fullName, constantNameNode.offset,
constantNameNode.length, 0, 0));
}
void _addLocalSuggestion_includeReturnValueSuggestions(
Element element,
SimpleIdentifier id,
@ -549,47 +479,6 @@ class _LocalVisitor extends LocalDeclarationVisitor {
}
}
void _addLocalSuggestion_includeReturnValueSuggestions_enumConstant(
EnumConstantDeclaration constantDeclaration,
EnumDeclaration enumDeclaration,
{@required bool isDeprecated}) {
var enumElement = enumDeclaration.declaredElement;
int relevance;
if (useNewRelevance) {
relevance = _relevanceForType(enumElement.thisType);
} else {
relevance = opType.returnValueSuggestionsFilter(
_instantiateClassElement(enumElement), DART_RELEVANCE_DEFAULT);
}
if (relevance != null) {
_addLocalSuggestion_enumConstant(constantDeclaration, enumDeclaration,
isDeprecated: isDeprecated, relevance: relevance);
}
}
void _addLocalSuggestion_includeTypeNameSuggestions(
Element element,
SimpleIdentifier id,
TypeAnnotation typeName,
protocol.ElementKind elemKind,
{bool isAbstract = false,
bool isDeprecated = false,
@required DartType type}) {
int relevance;
if (useNewRelevance) {
relevance = _relevanceForType(type);
} else {
relevance =
opType.typeNameSuggestionsFilter(type, DART_RELEVANCE_DEFAULT);
}
if (relevance != null) {
_addLocalSuggestion(element, id, typeName, elemKind,
isAbstract: isAbstract,
isDeprecated: isDeprecated,
relevance: relevance);
}
}
void _addParameterInfo(
CompletionSuggestion suggestion, FormalParameterList parameters) {
var paramList = parameters.parameters;
@ -661,44 +550,6 @@ class _LocalVisitor extends LocalDeclarationVisitor {
return suggestion;
}
InterfaceType _instantiateClassElement(ClassElement element) {
var typeParameters = element.typeParameters;
var typeArguments = const <DartType>[];
if (typeParameters.isNotEmpty) {
typeArguments = typeParameters.map((t) {
return typeProvider.dynamicType;
}).toList();
}
var nullabilitySuffix = request.featureSet.isEnabled(Feature.non_nullable)
? NullabilitySuffix.none
: NullabilitySuffix.star;
return element.instantiate(
typeArguments: typeArguments,
nullabilitySuffix: nullabilitySuffix,
);
}
FunctionType _instantiateFunctionTypeAlias(FunctionTypeAliasElement element) {
var typeParameters = element.typeParameters;
var typeArguments = const <DartType>[];
if (typeParameters.isNotEmpty) {
typeArguments = typeParameters.map((t) {
return typeProvider.dynamicType;
}).toList();
}
var nullabilitySuffix = request.featureSet.isEnabled(Feature.non_nullable)
? NullabilitySuffix.none
: NullabilitySuffix.star;
return element.instantiate(
typeArguments: typeArguments,
nullabilitySuffix: nullabilitySuffix,
);
}
bool _isVoid(TypeAnnotation returnType) {
if (returnType is TypeName) {
var id = returnType.name;

View file

@ -41,7 +41,7 @@ CompletionSuggestion createSuggestion(
}
kind ??= CompletionSuggestionKind.INVOCATION;
var suggestion = CompletionSuggestion(kind, relevance, completion,
completion.length, 0, element.hasDeprecated, false);
completion.length, 0, element.hasOrInheritsDeprecated, false);
// Attach docs.
var doc = DartUnitHoverComputer.computeDocumentation(
@ -291,6 +291,13 @@ class SuggestionBuilder {
final Map<String, CompletionSuggestion> _suggestionMap =
<String, CompletionSuggestion>{};
/// A flag indicating whether a suggestion should replace any earlier
/// suggestions for the same completion (`true`) or whether earlier
/// suggestions should take priority over more recent suggestions.
// TODO(brianwilkerson) Attempt to convert the contributors so that a single
// approach is followed.
bool laterReplacesEarlier = true;
/// A flag indicating whether the [_cachedContextType] has been computed.
bool _hasContextType = false;
@ -398,11 +405,14 @@ class SuggestionBuilder {
if (request.useNewRelevance) {
relevance = _computeTopLevelRelevance(classElement,
elementType: _instantiateClassElement(classElement));
} else if (classElement.hasDeprecated) {
} else if (classElement.hasOrInheritsDeprecated) {
relevance = DART_RELEVANCE_LOW;
} else {
relevance = request.opType.typeNameSuggestionsFilter(
_instantiateClassElement(classElement), DART_RELEVANCE_DEFAULT);
if (relevance == null) {
return;
}
}
_add(createSuggestion(request, classElement,
@ -440,7 +450,7 @@ class SuggestionBuilder {
if (request.useNewRelevance) {
relevance = _computeTopLevelRelevance(constructor);
} else {
relevance = constructor.hasDeprecated
relevance = constructor.hasOrInheritsDeprecated
? DART_RELEVANCE_LOW
: DART_RELEVANCE_DEFAULT;
}
@ -472,6 +482,31 @@ class SuggestionBuilder {
}
}
/// Add a suggestion for the enum [constant].
void suggestEnumConstant(FieldElement constant) {
var constantName = constant.name;
var enumElement = constant.enclosingElement;
var enumName = enumElement.name;
var completion = '$enumName.$constantName';
int relevance;
if (request.useNewRelevance) {
relevance =
_computeTopLevelRelevance(constant, elementType: constant.type);
} else if (constant.hasOrInheritsDeprecated) {
relevance = DART_RELEVANCE_LOW;
} else {
relevance = request.opType.returnValueSuggestionsFilter(
_instantiateClassElement(enumElement), DART_RELEVANCE_DEFAULT);
if (relevance == null) {
return;
}
}
_add(createSuggestion(request, constant,
completion: completion, relevance: relevance));
}
/// Add a suggestion for the [extension]. If a [kind] is provided it will be
/// used as the kind for the suggestion.
void suggestExtension(ExtensionElement extension,
@ -481,8 +516,9 @@ class SuggestionBuilder {
relevance = _computeTopLevelRelevance(extension,
elementType: extension.extendedType);
} else {
relevance =
extension.hasDeprecated ? DART_RELEVANCE_LOW : DART_RELEVANCE_DEFAULT;
relevance = extension.hasOrInheritsDeprecated
? DART_RELEVANCE_LOW
: DART_RELEVANCE_DEFAULT;
}
_add(
@ -529,14 +565,15 @@ class SuggestionBuilder {
{CompletionSuggestionKind kind = CompletionSuggestionKind.INVOCATION}) {
int relevance;
if (request.useNewRelevance) {
relevance =
_computeTopLevelRelevance(functionTypeAlias, defaultRelevance: 750);
relevance = _computeTopLevelRelevance(functionTypeAlias,
defaultRelevance: 750,
elementType: _instantiateFunctionTypeAlias(functionTypeAlias));
} else if (functionTypeAlias.hasOrInheritsDeprecated) {
relevance = DART_RELEVANCE_LOW;
} else {
relevance = functionTypeAlias.hasDeprecated
? DART_RELEVANCE_LOW
: (functionTypeAlias.library == request.libraryElement
? DART_RELEVANCE_LOCAL_FUNCTION
: DART_RELEVANCE_DEFAULT);
relevance = functionTypeAlias.library == request.libraryElement
? DART_RELEVANCE_LOCAL_FUNCTION
: DART_RELEVANCE_DEFAULT;
}
_add(createSuggestion(request, functionTypeAlias,
kind: kind, relevance: relevance));
@ -551,8 +588,9 @@ class SuggestionBuilder {
// than a fixed value.
relevance = Relevance.loadLibrary;
} else {
relevance =
function.hasDeprecated ? DART_RELEVANCE_LOW : DART_RELEVANCE_DEFAULT;
relevance = function.hasOrInheritsDeprecated
? DART_RELEVANCE_LOW
: DART_RELEVANCE_DEFAULT;
}
_add(createSuggestion(request, function, relevance: relevance));
@ -634,12 +672,12 @@ class SuggestionBuilder {
if (request.useNewRelevance) {
relevance =
_computeTopLevelRelevance(function, elementType: function.returnType);
} else if (function.hasOrInheritsDeprecated) {
relevance = DART_RELEVANCE_LOW;
} else {
relevance = function.hasDeprecated
? DART_RELEVANCE_LOW
: (function.library == request.libraryElement
? DART_RELEVANCE_LOCAL_FUNCTION
: DART_RELEVANCE_DEFAULT);
relevance = function.library == request.libraryElement
? DART_RELEVANCE_LOCAL_FUNCTION
: DART_RELEVANCE_DEFAULT;
}
_add(createSuggestion(request, function, kind: kind, relevance: relevance));
@ -664,12 +702,12 @@ class SuggestionBuilder {
if (request.useNewRelevance) {
relevance =
_computeTopLevelRelevance(variable, elementType: variable.type);
} else if (accessor.hasOrInheritsDeprecated) {
relevance = DART_RELEVANCE_LOW;
} else {
relevance = accessor.hasDeprecated
? DART_RELEVANCE_LOW
: (variable.library == request.libraryElement
? DART_RELEVANCE_LOCAL_TOP_LEVEL_VARIABLE
: DART_RELEVANCE_DEFAULT);
relevance = variable.library == request.libraryElement
? DART_RELEVANCE_LOCAL_TOP_LEVEL_VARIABLE
: DART_RELEVANCE_DEFAULT;
}
_add(createSuggestion(request, variable, kind: kind, relevance: relevance));
@ -678,7 +716,11 @@ class SuggestionBuilder {
/// Add the given [suggestion] if it isn't `null`.
void _add(protocol.CompletionSuggestion suggestion) {
if (suggestion != null) {
_suggestionMap[suggestion.completion] = suggestion;
if (laterReplacesEarlier) {
_suggestionMap[suggestion.completion] = suggestion;
} else {
_suggestionMap.putIfAbsent(suggestion.completion, () => suggestion);
}
}
}
@ -720,7 +762,7 @@ class SuggestionBuilder {
/// Compute the old relevance score for a member.
int _computeOldMemberRelevance(Element member,
{String containingMethodName}) {
if (member.hasDeprecated) {
if (member.hasOrInheritsDeprecated) {
return DART_RELEVANCE_LOW;
} else if (member.name == containingMethodName) {
// Boost the relevance of a super expression calling a method of the
@ -772,4 +814,22 @@ class SuggestionBuilder {
nullabilitySuffix: nullabilitySuffix,
);
}
FunctionType _instantiateFunctionTypeAlias(FunctionTypeAliasElement element) {
var typeParameters = element.typeParameters;
var typeArguments = const <DartType>[];
if (typeParameters.isNotEmpty) {
var neverType = request.libraryElement.typeProvider.neverType;
typeArguments = List.filled(typeParameters.length, neverType);
}
var nullabilitySuffix = request.featureSet.isEnabled(Feature.non_nullable)
? NullabilitySuffix.none
: NullabilitySuffix.star;
return element.instantiate(
typeArguments: typeArguments,
nullabilitySuffix: nullabilitySuffix,
);
}
}

View file

@ -2192,7 +2192,7 @@ $docLines
class MyClass {}
$docLines
class MyClassTypeAlias = Object with MyClass;
class MyMixinApplication = Object with MyClass;
$docLines
enum MyEnum {A, B, C}
@ -2211,7 +2211,7 @@ main() {^}
assertDoc(suggestion);
}
{
var suggestion = assertSuggestClassTypeAlias('MyClassTypeAlias');
var suggestion = assertSuggestClass('MyMixinApplication');
assertDoc(suggestion);
}
{
@ -2494,7 +2494,7 @@ class C {foo(){^} void bar() {}}''');
assertSuggestMethod('bar', 'C', 'void',
relevance: DART_RELEVANCE_LOCAL_METHOD);
assertSuggestFunctionTypeAlias('F2', 'int');
assertSuggestClassTypeAlias('Clz');
assertSuggestClass('Clz');
assertSuggestClass('C');
assertNotSuggested('x');
assertNotSuggested('_B');