Files
swift-mirror/lib/IDE/CompletionLookup.cpp
Konrad `ktoso` Malawski 3e36c42ec5 Add nonisolated(nonsending) to completion lookup
Seems we perhaps missed adding the completion for `nonsending` here?


cc @xedin
2025-04-25 19:46:30 +09:00

3552 lines
127 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//===--- CompletionLookup.cpp ---------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/IDE/CompletionLookup.h"
#include "CodeCompletionResultBuilder.h"
#include "ExprContextAnalysis.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/SourceFile.h"
#include "swift/Basic/Assertions.h"
using namespace swift;
using namespace swift::ide;
namespace {
static bool SwiftKeyPathFilter(ValueDecl *decl, DeclVisibilityKind) {
switch (decl->getKind()) {
case DeclKind::Var:
case DeclKind::Subscript:
return true;
default:
return false;
}
}
static bool isTopLevelSubcontext(const DeclContext *DC) {
for (; DC && DC->isLocalContext(); DC = DC->getParent()) {
switch (DC->getContextKind()) {
case DeclContextKind::TopLevelCodeDecl:
return true;
case DeclContextKind::AbstractFunctionDecl:
case DeclContextKind::SubscriptDecl:
case DeclContextKind::EnumElementDecl:
return false;
default:
continue;
}
}
return false;
}
static KnownProtocolKind
protocolForLiteralKind(CodeCompletionLiteralKind kind) {
switch (kind) {
case CodeCompletionLiteralKind::ArrayLiteral:
return KnownProtocolKind::ExpressibleByArrayLiteral;
case CodeCompletionLiteralKind::BooleanLiteral:
return KnownProtocolKind::ExpressibleByBooleanLiteral;
case CodeCompletionLiteralKind::ColorLiteral:
return KnownProtocolKind::ExpressibleByColorLiteral;
case CodeCompletionLiteralKind::ImageLiteral:
return KnownProtocolKind::ExpressibleByImageLiteral;
case CodeCompletionLiteralKind::DictionaryLiteral:
return KnownProtocolKind::ExpressibleByDictionaryLiteral;
case CodeCompletionLiteralKind::IntegerLiteral:
return KnownProtocolKind::ExpressibleByIntegerLiteral;
case CodeCompletionLiteralKind::NilLiteral:
return KnownProtocolKind::ExpressibleByNilLiteral;
case CodeCompletionLiteralKind::StringLiteral:
return KnownProtocolKind::ExpressibleByUnicodeScalarLiteral;
case CodeCompletionLiteralKind::Tuple:
llvm_unreachable("no such protocol kind");
}
llvm_unreachable("Unhandled CodeCompletionLiteralKind in switch.");
}
static Type defaultTypeLiteralKind(CodeCompletionLiteralKind kind,
ASTContext &Ctx) {
switch (kind) {
case CodeCompletionLiteralKind::BooleanLiteral:
return Ctx.getBoolType();
case CodeCompletionLiteralKind::IntegerLiteral:
return Ctx.getIntType();
case CodeCompletionLiteralKind::StringLiteral:
return Ctx.getStringType();
case CodeCompletionLiteralKind::ArrayLiteral:
if (!Ctx.getArrayDecl())
return Type();
return Ctx.getArrayDecl()->getDeclaredInterfaceType();
case CodeCompletionLiteralKind::DictionaryLiteral:
if (!Ctx.getDictionaryDecl())
return Type();
return Ctx.getDictionaryDecl()->getDeclaredInterfaceType();
case CodeCompletionLiteralKind::NilLiteral:
case CodeCompletionLiteralKind::ColorLiteral:
case CodeCompletionLiteralKind::ImageLiteral:
case CodeCompletionLiteralKind::Tuple:
return Type();
}
llvm_unreachable("Unhandled CodeCompletionLiteralKind in switch.");
}
/// Whether funcType has a single argument (not including defaulted arguments)
/// that is of type () -> ().
static bool hasTrivialTrailingClosure(const FuncDecl *FD,
AnyFunctionType *funcType) {
ParameterListInfo paramInfo(funcType->getParams(), FD,
/*skipCurriedSelf*/ FD->hasCurriedSelf());
if (paramInfo.size() - paramInfo.numNonDefaultedParameters() == 1) {
auto param = funcType->getParams().back();
if (!param.isAutoClosure()) {
if (auto Fn = param.getOldType()->getAs<AnyFunctionType>()) {
return Fn->getParams().empty() && Fn->getResult()->isVoid();
}
}
}
return false;
}
} // end anonymous namespace
bool swift::ide::DefaultFilter(ValueDecl *VD, DeclVisibilityKind Kind,
DynamicLookupInfo dynamicLookupInfo) {
return true;
}
bool swift::ide::KeyPathFilter(ValueDecl *decl, DeclVisibilityKind,
DynamicLookupInfo dynamicLookupInfo) {
return isa<TypeDecl>(decl) ||
(isa<VarDecl>(decl) && decl->getDeclContext()->isTypeContext());
}
bool swift::ide::MacroFilter(ValueDecl *decl, DeclVisibilityKind,
DynamicLookupInfo dynamicLookupInfo) {
return isa<MacroDecl>(decl);
}
bool swift::ide::isCodeCompletionAtTopLevel(const DeclContext *DC) {
if (DC->isModuleScopeContext())
return true;
// CC token at top-level is parsed as an expression. If the only element
// body of the TopLevelCodeDecl is a CodeCompletionExpr without a base
// expression, the user might be writing a top-level declaration.
if (const TopLevelCodeDecl *TLCD = dyn_cast<const TopLevelCodeDecl>(DC)) {
auto body = TLCD->getBody();
if (!body || body->empty())
return true;
if (body->getElements().size() > 1)
return false;
auto expr = body->getFirstElement().dyn_cast<Expr *>();
if (!expr)
return false;
if (CodeCompletionExpr *CCExpr = dyn_cast<CodeCompletionExpr>(expr)) {
if (CCExpr->getBase() == nullptr)
return true;
}
}
return false;
}
bool swift::ide::isCompletionDeclContextLocalContext(DeclContext *DC) {
if (!DC->isLocalContext())
return false;
if (isCodeCompletionAtTopLevel(DC))
return false;
return true;
}
/// Returns \c true if \p DC can handles async call.
bool swift::ide::canDeclContextHandleAsync(const DeclContext *DC) {
if (auto *func = dyn_cast<AbstractFunctionDecl>(DC))
return func->isAsyncContext();
if (auto *closure = dyn_cast<ClosureExpr>(DC)) {
// See if the closure has 'async' function type.
if (auto closureType = closure->getType())
if (auto fnType = closureType->getAs<AnyFunctionType>())
if (fnType->isAsync())
return true;
// If the closure doesn't contain any async call in the body, closure itself
// doesn't have 'async' type even if 'async' closure is expected.
// func foo(fn: () async -> Void)
// foo { <HERE> }
// In this case, the closure is wrapped with a 'FunctionConversionExpr'
// which has 'async' function type.
struct AsyncClosureChecker : public ASTWalker {
const ClosureExpr *Target;
bool Result = false;
/// Walk everything in a macro.
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::ArgumentsAndExpansion;
}
AsyncClosureChecker(const ClosureExpr *Target) : Target(Target) {}
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
if (E == Target)
return Action::SkipNode(E);
if (auto conversionExpr = dyn_cast<FunctionConversionExpr>(E)) {
if (conversionExpr->getSubExpr() == Target) {
if (conversionExpr->getType()->is<AnyFunctionType>() &&
conversionExpr->getType()->castTo<AnyFunctionType>()->isAsync())
Result = true;
return Action::SkipNode(E);
}
}
return Action::Continue(E);
}
} checker(closure);
closure->getParent()->walkContext(checker);
return checker.Result;
}
return false;
}
/// Return \c true if the completion happens at top-level of a library file.
bool swift::ide::isCodeCompletionAtTopLevelOfLibraryFile(
const DeclContext *DC) {
if (DC->getParentSourceFile()->isScriptMode())
return false;
return isCodeCompletionAtTopLevel(DC);
}
// MARK: - CompletionLookup
void CompletionLookup::foundFunction(const AbstractFunctionDecl *AFD) {
FoundFunctionCalls = true;
const DeclName Name = AFD->getName();
auto ArgNames = Name.getArgumentNames();
if (ArgNames.empty())
return;
if (ArgNames[0].empty())
FoundFunctionsWithoutFirstKeyword = true;
}
void CompletionLookup::foundFunction(const AnyFunctionType *AFT) {
FoundFunctionCalls = true;
auto Params = AFT->getParams();
if (Params.empty())
return;
if (Params.size() == 1 && !Params[0].hasLabel()) {
FoundFunctionsWithoutFirstKeyword = true;
return;
}
if (!Params[0].hasLabel())
FoundFunctionsWithoutFirstKeyword = true;
}
bool CompletionLookup::canBeUsedAsRequirementFirstType(Type selfTy,
TypeAliasDecl *TAD) {
if (TAD->isGeneric())
return false;
auto T = TAD->getDeclaredInterfaceType();
auto subMap = selfTy->getContextSubstitutionMap(TAD->getDeclContext());
return T.subst(subMap)->is<ArchetypeType>();
}
CompletionLookup::CompletionLookup(CodeCompletionResultSink &Sink,
ASTContext &Ctx,
const DeclContext *CurrDeclContext,
CodeCompletionContext *CompletionContext)
: Sink(Sink), Ctx(Ctx), CurrDeclContext(CurrDeclContext),
CurrModule(CurrDeclContext ? CurrDeclContext->getParentModule()
: nullptr),
Importer(static_cast<ClangImporter *>(
CurrDeclContext->getASTContext().getClangModuleLoader())),
CompletionContext(CompletionContext) {
// Determine if we are doing code completion inside a static method.
if (CurrDeclContext) {
CurrentMethod = CurrDeclContext->getInnermostMethodContext();
if (auto *FD = dyn_cast_or_null<FuncDecl>(CurrentMethod))
InsideStaticMethod = FD->isStatic();
CanCurrDeclContextHandleAsync = canDeclContextHandleAsync(CurrDeclContext);
}
}
void CompletionLookup::addSubModuleNames(
std::vector<std::pair<std::string, bool>> &SubModuleNameVisibilityPairs) {
for (auto &Pair : SubModuleNameVisibilityPairs) {
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Declaration, SemanticContextKind::None);
auto *MD = ModuleDecl::createEmpty(Ctx.getIdentifier(Pair.first), Ctx);
Builder.setAssociatedDecl(MD);
Builder.addBaseName(MD->getNameStr());
Builder.addTypeAnnotation("Module");
if (Pair.second)
Builder.setContextualNotRecommended(
ContextualNotRecommendedReason::RedundantImport);
}
}
void CompletionLookup::collectImportedModules(
llvm::StringSet<> &directImportedModules,
llvm::StringSet<> &allImportedModules) {
SmallVector<ImportedModule, 16> Imported;
SmallVector<ImportedModule, 16> FurtherImported;
CurrDeclContext->getParentSourceFile()->getImportedModules(
Imported, ModuleDecl::getImportFilterLocal());
for (ImportedModule &imp : Imported)
directImportedModules.insert(imp.importedModule->getNameStr());
while (!Imported.empty()) {
ModuleDecl *MD = Imported.back().importedModule;
Imported.pop_back();
if (!allImportedModules.insert(MD->getNameStr()).second)
continue;
FurtherImported.clear();
MD->getImportedModules(FurtherImported,
ModuleDecl::ImportFilterKind::Exported);
Imported.append(FurtherImported.begin(), FurtherImported.end());
}
}
void CompletionLookup::addModuleName(
ModuleDecl *MD, std::optional<ContextualNotRecommendedReason> R) {
// Don't add underscored cross-import overlay modules.
if (MD->getDeclaringModuleIfCrossImportOverlay())
return;
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Declaration, SemanticContextKind::None);
Builder.setAssociatedDecl(MD);
auto moduleName = MD->getName();
// This checks if module aliasing was used. For example, when editing
// `import ...`, and `-module-alias Foo=Bar` was passed, we want to show
// Foo as an option to import, instead of Bar (name of the binary), as
// Foo is the name that should appear in source files.
auto aliasedName = Ctx.getRealModuleName(
moduleName, ASTContext::ModuleAliasLookupOption::aliasFromRealName);
if (aliasedName != moduleName && // check if module aliasing was applied
!aliasedName.empty()) { // check an alias mapped to the binary name exists
moduleName = aliasedName; // if so, use the aliased name
}
Builder.addBaseName(moduleName.str());
Builder.addTypeAnnotation("Module");
if (R)
Builder.setContextualNotRecommended(*R);
}
void CompletionLookup::addImportModuleNames() {
SmallVector<Identifier, 0> ModuleNames;
Ctx.getVisibleTopLevelModuleNames(ModuleNames);
llvm::StringSet<> directImportedModules;
llvm::StringSet<> allImportedModules;
collectImportedModules(directImportedModules, allImportedModules);
auto mainModuleName = CurrModule->getName();
for (auto ModuleName : ModuleNames) {
if (ModuleName == mainModuleName || isHiddenModuleName(ModuleName))
continue;
auto *MD = ModuleDecl::createEmpty(ModuleName, Ctx);
std::optional<ContextualNotRecommendedReason> Reason = std::nullopt;
// Imported modules are not recommended.
if (directImportedModules.contains(MD->getNameStr())) {
Reason = ContextualNotRecommendedReason::RedundantImport;
} else if (allImportedModules.contains(MD->getNameStr())) {
Reason = ContextualNotRecommendedReason::RedundantImportIndirect;
}
addModuleName(MD, Reason);
}
}
SemanticContextKind
CompletionLookup::getSemanticContext(const Decl *D, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
if (ForcedSemanticContext)
return *ForcedSemanticContext;
switch (Reason) {
case DeclVisibilityKind::LocalDecl:
case DeclVisibilityKind::FunctionParameter:
case DeclVisibilityKind::GenericParameter:
return SemanticContextKind::Local;
case DeclVisibilityKind::MemberOfCurrentNominal:
return SemanticContextKind::CurrentNominal;
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
case DeclVisibilityKind::MemberOfSuper:
return SemanticContextKind::Super;
case DeclVisibilityKind::MemberOfOutsideNominal:
return SemanticContextKind::OutsideNominal;
case DeclVisibilityKind::VisibleAtTopLevel:
if (CurrDeclContext && D->getModuleContext() == CurrModule) {
// Treat global variables from the same source file as local when
// completing at top-level.
if (isa<VarDecl>(D) && isTopLevelSubcontext(CurrDeclContext) &&
D->getDeclContext()->getParentSourceFile() ==
CurrDeclContext->getParentSourceFile()) {
return SemanticContextKind::Local;
} else {
return SemanticContextKind::CurrentModule;
}
} else {
return SemanticContextKind::OtherModule;
}
case DeclVisibilityKind::DynamicLookup:
switch (dynamicLookupInfo.getKind()) {
case DynamicLookupInfo::None:
llvm_unreachable("invalid DynamicLookupInfo::Kind for dynamic lookup");
case DynamicLookupInfo::AnyObject:
// AnyObject results can come from different modules, including the
// current module, but we always assign them the OtherModule semantic
// context. These declarations are uniqued by signature, so it is
// totally random (determined by the hash function) which of the
// equivalent declarations (across multiple modules) we will get.
return SemanticContextKind::OtherModule;
case DynamicLookupInfo::KeyPathDynamicMember:
// Use the visibility of the underlying declaration.
// FIXME: KeyPath<AnyObject, U> !?!?
assert(dynamicLookupInfo.getKeyPathDynamicMember().originalVisibility !=
DeclVisibilityKind::DynamicLookup);
return getSemanticContext(
D, dynamicLookupInfo.getKeyPathDynamicMember().originalVisibility,
{});
}
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
llvm_unreachable("should not see this kind");
}
llvm_unreachable("unhandled kind");
}
bool CompletionLookup::isUnresolvedMemberIdealType(Type Ty) {
assert(Ty);
if (!IsUnresolvedMember)
return false;
Type idealTy = expectedTypeContext.getIdealType();
if (!idealTy)
return false;
/// Consider optional object type is the ideal.
/// For example:
/// enum MyEnum { case foo, bar }
/// func foo(_: MyEnum?)
/// fooo(.<HERE>)
/// Prefer '.foo' and '.bar' over '.some' and '.none'.
idealTy = idealTy->lookThroughAllOptionalTypes();
return idealTy->isEqual(Ty);
}
CodeCompletionResultBuilder
CompletionLookup::makeResultBuilder(CodeCompletionResultKind kind,
SemanticContextKind semanticContext) const {
CodeCompletionResultBuilder builder(Sink, kind, semanticContext);
builder.setTypeContext(expectedTypeContext, CurrDeclContext);
return builder;
}
void CompletionLookup::addValueBaseName(CodeCompletionResultBuilder &Builder,
DeclBaseName Name) {
auto NameStr = Name.userFacingName();
if (Name.mustAlwaysBeEscaped()) {
// Names that are raw identifiers must always be escaped regardless of
// their position.
SmallString<16> buffer;
Builder.addBaseName(Builder.escapeWithBackticks(NameStr, buffer));
return;
}
bool shouldEscapeKeywords;
if (Name.isSpecial()) {
// Special names (i.e. 'init') are always displayed as its user facing
// name.
shouldEscapeKeywords = false;
} else if (ExprType) {
// After dot. User can write any keyword after '.' except for `init` and
// `self`. E.g. 'func `init`()' must be called by 'expr.`init`()'.
shouldEscapeKeywords = NameStr == "self" || NameStr == "init";
} else {
// As primary expresson. We have to escape almost every keywords except
// for 'self' and 'Self'.
shouldEscapeKeywords = NameStr != "self" && NameStr != "Self";
}
if (!shouldEscapeKeywords) {
Builder.addBaseName(NameStr);
} else {
SmallString<16> buffer;
Builder.addBaseName(Builder.escapeKeyword(NameStr, true, buffer));
}
}
void CompletionLookup::addIdentifier(CodeCompletionResultBuilder &Builder,
Identifier Name) {
if (Name.mustAlwaysBeEscaped()) {
SmallString<16> buffer;
Builder.addBaseName(Builder.escapeWithBackticks(Name.str(), buffer));
} else {
Builder.addBaseName(Name.str());
}
}
void CompletionLookup::addLeadingDot(CodeCompletionResultBuilder &Builder) {
if (NeedOptionalUnwrap) {
Builder.setNumBytesToErase(NumBytesToEraseForOptionalUnwrap);
Builder.addQuestionMark();
Builder.addLeadingDot();
return;
}
if (needDot())
Builder.addLeadingDot();
}
void CompletionLookup::addTypeAnnotation(CodeCompletionResultBuilder &Builder,
Type T, GenericSignature genericSig) {
PrintOptions PO;
PO.OpaqueReturnTypePrinting =
PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword;
if (auto typeContext = CurrDeclContext->getInnermostTypeContext())
PO.setBaseType(typeContext->getDeclaredTypeInContext());
Builder.addTypeAnnotation(eraseArchetypes(T, genericSig), PO);
Builder.setResultTypes(T);
}
void CompletionLookup::addTypeAnnotationForImplicitlyUnwrappedOptional(
CodeCompletionResultBuilder &Builder, Type T, GenericSignature genericSig,
bool dynamicOrOptional) {
std::string suffix;
// FIXME: This retains previous behavior, but in reality the type of dynamic
// lookups is IUO, not Optional as it is for the @optional attribute.
if (dynamicOrOptional) {
T = T->getOptionalObjectType();
suffix = "?";
}
PrintOptions PO;
PO.PrintOptionalAsImplicitlyUnwrapped = true;
PO.OpaqueReturnTypePrinting =
PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword;
if (auto typeContext = CurrDeclContext->getInnermostTypeContext())
PO.setBaseType(typeContext->getDeclaredTypeInContext());
Builder.addTypeAnnotation(eraseArchetypes(T, genericSig), PO, suffix);
Builder.setResultTypes(T);
Builder.setTypeContext(expectedTypeContext, CurrDeclContext);
}
/// For printing in code completion results, replace archetypes with
/// protocol compositions.
///
/// FIXME: Perhaps this should be an option in PrintOptions instead.
Type CompletionLookup::eraseArchetypes(Type type, GenericSignature genericSig) {
if (!genericSig)
return type;
if (auto *genericFuncType = type->getAs<GenericFunctionType>()) {
assert(genericFuncType->getGenericSignature()->isEqual(genericSig) &&
"if not, just use the GFT's signature instead below");
SmallVector<AnyFunctionType::Param, 8> erasedParams;
for (const auto &param : genericFuncType->getParams()) {
auto erasedTy = eraseArchetypes(param.getPlainType(), genericSig);
erasedParams.emplace_back(param.withType(erasedTy));
}
return GenericFunctionType::get(
genericSig, erasedParams,
eraseArchetypes(genericFuncType->getResult(), genericSig),
genericFuncType->getExtInfo());
}
return type.transformRec([&](Type t) -> std::optional<Type> {
// FIXME: Code completion should only deal with one or the other,
// and not both.
if (auto *archetypeType = t->getAs<ArchetypeType>()) {
// Don't erase opaque archetype.
if (isa<OpaqueTypeArchetypeType>(archetypeType) &&
archetypeType->isRoot())
return std::nullopt;
auto genericSig = archetypeType->getGenericEnvironment()->getGenericSignature();
auto upperBound = genericSig->getUpperBound(
archetypeType->getInterfaceType(),
/*forExistentialSelf=*/false,
/*withParameterizedProtocols=*/false);
if (!upperBound->isAny())
return upperBound;
}
if (t->isTypeParameter()) {
auto upperBound = genericSig->getUpperBound(
t,
/*forExistentialSelf=*/false,
/*withParameterizedProtocols=*/false);
if (!upperBound->isAny())
return upperBound;
}
return std::nullopt;
});
}
Type CompletionLookup::getTypeOfMember(const ValueDecl *VD,
DynamicLookupInfo dynamicLookupInfo) {
switch (dynamicLookupInfo.getKind()) {
case DynamicLookupInfo::None:
return getTypeOfMember(VD, getMemberBaseType());
case DynamicLookupInfo::AnyObject:
return getTypeOfMember(VD, Type());
case DynamicLookupInfo::KeyPathDynamicMember: {
auto &keyPathInfo = dynamicLookupInfo.getKeyPathDynamicMember();
// Map the result of VD to keypath member lookup results.
// Given:
// struct Wrapper<T> {
// subscript<U>(dynamicMember: KeyPath<T, U>) -> Wrapped<U> { get }
// }
// struct Circle {
// var center: Point { get }
// var radius: Length { get }
// }
//
// Consider 'Wrapper<Circle>.center'.
// 'VD' is 'Circle.center' decl.
// 'keyPathInfo.subscript' is 'Wrapper<T>.subscript' decl.
// 'keyPathInfo.baseType' is 'Wrapper<Circle>' type.
// FIXME: Handle nested keypath member lookup.
// i.e. cases where 'ExprType' != 'keyPathInfo.baseType'.
auto *SD = keyPathInfo.subscript;
const auto elementTy = SD->getElementInterfaceType();
if (!elementTy->hasTypeParameter())
return elementTy;
// Map is:
// { τ_0_0(T) => Circle
// τ_1_0(U) => U }
auto subs = keyPathInfo.baseType->getMemberSubstitutions(SD);
// FIXME: The below should use substitution map substitution.
// If the keyPath result type has type parameters, that might affect the
// subscript result type.
auto keyPathResultTy = getResultTypeOfKeypathDynamicMember(SD);
if (keyPathResultTy->hasTypeParameter()) {
auto keyPathRootTy = getRootTypeOfKeypathDynamicMember(SD).subst(
QueryTypeSubstitutionMap{subs},
LookUpConformanceInModule());
// The result type of the VD.
// i.e. 'Circle.center' => 'Point'.
auto innerResultTy = getTypeOfMember(VD, keyPathRootTy);
if (auto paramTy = keyPathResultTy->getAs<GenericTypeParamType>()) {
// Replace keyPath result type in the map with the inner result type.
// i.e. Make the map as:
// { τ_0_0(T) => Circle
// τ_1_0(U) => Point }
auto key = paramTy->getCanonicalType()->castTo<GenericTypeParamType>();
subs[key] = innerResultTy;
} else {
// FIXME: Handle the case where the KeyPath result is generic.
// e.g. 'subscript<U>(dynamicMember: KeyPath<T, Box<U>>) -> Bag<U>'
// For now, just return the inner type.
return innerResultTy;
}
}
// Substitute the element type of the subscript using modified map.
// i.e. 'Wrapped<U>' => 'Wrapped<Point>'.
return elementTy.subst(QueryTypeSubstitutionMap{subs},
LookUpConformanceInModule());
}
}
llvm_unreachable("Unhandled DynamicLookupInfo Kind in switch");
}
Type CompletionLookup::getTypeOfMember(const ValueDecl *VD, Type ExprType) {
Type T;
if (auto *TD = dyn_cast<TypeDecl>(VD)) {
// For a type decl we're interested in the declared interface type, i.e
// we don't want a metatype.
T = TD->getDeclaredInterfaceType();
} else {
T = VD->getInterfaceType();
}
assert(!T.isNull());
if (ExprType) {
Type ContextTy = VD->getDeclContext()->getDeclaredInterfaceType();
if (ContextTy) {
// Look through lvalue types and metatypes
Type MaybeNominalType = ExprType->getRValueType();
if (auto Metatype = MaybeNominalType->getAs<MetatypeType>())
MaybeNominalType = Metatype->getInstanceType();
if (auto SelfType = MaybeNominalType->getAs<DynamicSelfType>())
MaybeNominalType = SelfType->getSelfType();
// For optional protocol requirements and dynamic dispatch,
// strip off optionality from the base type, but only if
// we're not actually completing a member of Optional.
if (!ContextTy->getOptionalObjectType() &&
MaybeNominalType->getOptionalObjectType())
MaybeNominalType = MaybeNominalType->getOptionalObjectType();
// For dynamic lookup don't substitute in the base type.
if (MaybeNominalType->isAnyObject())
return T;
// FIXME: Sometimes ExprType is the type of the member here,
// and not the type of the base. That is inconsistent and
// should be cleaned up.
if (!MaybeNominalType->mayHaveMembers())
return T;
// We can't do anything if the base type has unbound generic parameters.
if (MaybeNominalType->hasUnboundGenericType())
return T;
// If we are doing implicit member lookup on a protocol and we have found
// a declaration in a constrained extension, use the extension's `Self`
// type for the generic substitution.
// Eg in the following, the `Self` type returned by `qux` is
// `MyGeneric<Int>`, not `MyProto` because of the `Self` type restriction.
// ```
// protocol MyProto {}
// struct MyGeneric<T>: MyProto {}
// extension MyProto where Self == MyGeneric<Int> {
// static func qux() -> Self { .init() }
// }
// func takeMyProto(_: any MyProto) {}
// func test() {
// takeMyProto(.#^COMPLETE^#)
// }
// ```
if (MaybeNominalType->isExistentialType()) {
Type SelfType;
if (auto *ED = dyn_cast<ExtensionDecl>(VD->getDeclContext())) {
if (ED->getSelfProtocolDecl() && ED->isConstrainedExtension()) {
auto Sig = ED->getGenericSignature();
SelfType = Sig->getConcreteType(ED->getSelfInterfaceType());
}
}
if (SelfType) {
MaybeNominalType = SelfType;
} else {
return T;
}
}
// For everything else, substitute in the base type.
auto Subs = MaybeNominalType->getMemberSubstitutionMap(VD);
// For a GenericFunctionType, we only want to substitute the
// param/result types, as otherwise we might end up with a bad generic
// signature if there are UnresolvedTypes present in the base type. Note
// we pass in DesugarMemberTypes so that we see the actual concrete type
// witnesses instead of type alias types.
if (auto *GFT = T->getAs<GenericFunctionType>()) {
T = GFT->substGenericArgs(Subs, SubstFlags::DesugarMemberTypes);
} else {
T = T.subst(Subs, SubstFlags::DesugarMemberTypes);
}
}
}
return T;
}
Type CompletionLookup::getAssociatedTypeType(const AssociatedTypeDecl *ATD) {
Type BaseTy = getMemberBaseType();
if (!BaseTy && CurrDeclContext)
BaseTy =
CurrDeclContext->getInnermostTypeContext()->getDeclaredTypeInContext();
if (BaseTy) {
BaseTy = BaseTy->getInOutObjectType()->getMetatypeInstanceType();
if (BaseTy->getAnyNominal()) {
auto Conformance = lookupConformance(BaseTy, ATD->getProtocol());
if (Conformance.isConcrete()) {
return Conformance.getConcrete()->getTypeWitness(
const_cast<AssociatedTypeDecl *>(ATD));
}
}
}
return Type();
}
void CompletionLookup::analyzeActorIsolation(
const ValueDecl *VD, Type T, bool &implicitlyAsync,
std::optional<ContextualNotRecommendedReason> &NotRecommended) {
auto isolation = getActorIsolation(const_cast<ValueDecl *>(VD));
switch (isolation.getKind()) {
case ActorIsolation::ActorInstance: {
// TODO: implicitlyThrowing here for distributed
if (IsCrossActorReference) {
implicitlyAsync = true;
// TODO: 'NotRecommended' if this is a r-value reference.
}
break;
}
case ActorIsolation::GlobalActor: {
// For "preconcurrency" global actor isolation, automatic 'async' only happens
// if the context has adopted concurrency.
if (isolation.preconcurrency() &&
!CanCurrDeclContextHandleAsync &&
!completionContextUsesConcurrencyFeatures(CurrDeclContext)) {
return;
}
auto getClosureActorIsolation = [this](AbstractClosureExpr *CE) {
// Prefer solution-specific actor-isolations and fall back to the one
// recorded in the AST.
auto isolation = ClosureActorIsolations.find(CE);
if (isolation != ClosureActorIsolations.end()) {
return isolation->second;
} else {
return CE->getActorIsolation();
}
};
auto contextIsolation = getActorIsolationOfContext(
const_cast<DeclContext *>(CurrDeclContext), getClosureActorIsolation);
if (contextIsolation != isolation) {
implicitlyAsync = true;
}
break;
}
case ActorIsolation::Erased:
implicitlyAsync = true;
break;
case ActorIsolation::Unspecified:
case ActorIsolation::Nonisolated:
case ActorIsolation::CallerIsolationInheriting:
case ActorIsolation::NonisolatedUnsafe:
return;
}
}
void CompletionLookup::addVarDeclRef(const VarDecl *VD,
DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
if (!VD->hasName())
return;
const Identifier Name = VD->getName();
assert(!Name.empty() && "name should not be empty");
Type VarType;
auto SolutionSpecificType = SolutionSpecificVarTypes.find(VD);
if (SolutionSpecificType != SolutionSpecificVarTypes.end()) {
assert(!VarType && "Type recorded in the AST and is also solution-specific?");
VarType = SolutionSpecificType->second;
} else if (VD->hasInterfaceType()) {
VarType = getTypeOfMember(VD, dynamicLookupInfo);
}
std::optional<ContextualNotRecommendedReason> NotRecommended;
// "not recommended" in its own getter.
if (Kind == LookupKind::ValueInDeclContext) {
if (auto accessor = dyn_cast<AccessorDecl>(CurrDeclContext)) {
if (accessor->getStorage() == VD && accessor->isGetter())
NotRecommended =
ContextualNotRecommendedReason::VariableUsedInOwnDefinition;
}
}
bool implicitlyAsync = false;
analyzeActorIsolation(VD, VarType, implicitlyAsync, NotRecommended);
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(VD, Reason, dynamicLookupInfo));
Builder.setCanCurrDeclContextHandleAsync(CanCurrDeclContextHandleAsync);
Builder.setAssociatedDecl(VD);
addLeadingDot(Builder);
addValueBaseName(Builder, Name);
if (NotRecommended)
Builder.setContextualNotRecommended(*NotRecommended);
if (!VarType)
return;
if (auto *PD = dyn_cast<ParamDecl>(VD)) {
if (Name != Ctx.Id_self && PD->isInOut()) {
// It is useful to show inout for function parameters.
// But for 'self' it is just noise.
VarType = InOutType::get(VarType);
}
}
auto DynamicOrOptional =
IsDynamicLookup || VD->getAttrs().hasAttribute<OptionalAttr>();
if (DynamicOrOptional) {
// Values of properties that were found on a AnyObject have
// Optional<T> type. Same applies to optional members.
VarType = OptionalType::get(VarType);
}
auto genericSig =
VD->getInnermostDeclContext()->getGenericSignatureOfContext();
if (VD->isImplicitlyUnwrappedOptional())
addTypeAnnotationForImplicitlyUnwrappedOptional(
Builder, VarType, genericSig, DynamicOrOptional);
else
addTypeAnnotation(Builder, VarType, genericSig);
if (implicitlyAsync)
Builder.addAnnotatedAsync();
if (isUnresolvedMemberIdealType(VarType))
Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific);
if (auto Accessor = VD->getEffectfulGetAccessor()) {
if (auto AFT = getTypeOfMember(Accessor, dynamicLookupInfo)->getAs<AnyFunctionType>()) {
if (Accessor->hasImplicitSelfDecl()) {
AFT = AFT->getResult()->getAs<AnyFunctionType>();
assert(AFT);
}
addEffectsSpecifiers(Builder, AFT, Accessor);
}
}
}
/// Return whether \p param has a non-desirable default value for code
/// completion.
///
/// 'ClangImporter::Implementation::inferDefaultArgument()' automatically adds
/// default values for some parameters;
/// * NS_OPTIONS enum type with the name '...Options'.
/// * NSDictionary and labeled 'options', 'attributes', or 'userInfo'.
///
/// But sometimes, this behavior isn't really desirable. This function add a
/// heuristic where if a parameter matches all the following condition, we
/// consider the imported default value is _not_ desirable:
/// * it is the first parameter,
/// * it doesn't have an argument label, and
/// * the imported function base name ends with those words
/// For example, ClangImporter imports:
///
/// -(void)addAttributes:(NSDictionary *)attrs, options:(NSDictionary *)opts;
///
/// as:
///
/// func addAttributes(_ attrs: [AnyHashable:Any] = [:],
/// options opts: [AnyHashable:Any] = [:])
///
/// In this case, we don't want 'attrs' defaulted because the function name have
/// 'Attribute' in its name so calling 'value.addAttribute()' doesn't make
/// sense, but we _do_ want to keep 'opts' defaulted.
///
/// Note that:
///
/// -(void)performWithOptions:(NSDictionary *) opts;
///
/// This doesn't match the condition because the base name of the function in
/// Swift is 'peform':
///
/// func perform(options opts: [AnyHashable:Any] = [:])
///
bool isNonDesirableImportedDefaultArg(const ParamDecl *param) {
auto kind = param->getDefaultArgumentKind();
if (kind != DefaultArgumentKind::EmptyArray &&
kind != DefaultArgumentKind::EmptyDictionary)
return false;
if (!param->getArgumentName().empty())
return false;
auto *func = dyn_cast<FuncDecl>(param->getDeclContext());
if (!func->hasClangNode())
return false;
if (func->getParameters()->front() != param)
return false;
if (func->getBaseName().isSpecial())
return false;
auto baseName = func->getBaseName().getIdentifier().str();
switch (kind) {
case DefaultArgumentKind::EmptyArray:
return (baseName.ends_with("Options"));
case DefaultArgumentKind::EmptyDictionary:
return (baseName.ends_with("Options") || baseName.ends_with("Attributes") ||
baseName.ends_with("UserInfo"));
default:
llvm_unreachable("unhandled DefaultArgumentKind");
}
}
bool CompletionLookup::hasInterestingDefaultValue(const ParamDecl *param) {
if (!param)
return false;
switch (param->getDefaultArgumentKind()) {
case DefaultArgumentKind::Normal:
case DefaultArgumentKind::NilLiteral:
case DefaultArgumentKind::StoredProperty:
case DefaultArgumentKind::Inherited:
return true;
case DefaultArgumentKind::EmptyArray:
case DefaultArgumentKind::EmptyDictionary:
if (isNonDesirableImportedDefaultArg(param))
return false;
return true;
case DefaultArgumentKind::None:
#define MAGIC_IDENTIFIER(NAME, STRING) \
case DefaultArgumentKind::NAME:
#include "swift/AST/MagicIdentifierKinds.def"
case DefaultArgumentKind::ExpressionMacro:
return false;
}
}
bool CompletionLookup::shouldAddItemWithoutDefaultArgs(
const AbstractFunctionDecl *func) {
if (!func || !Sink.addCallWithNoDefaultArgs)
return false;
for (auto param : *func->getParameters()) {
if (hasInterestingDefaultValue(param))
return true;
}
return false;
}
bool CompletionLookup::addCallArgumentPatterns(
CodeCompletionResultBuilder &Builder,
ArrayRef<AnyFunctionType::Param> typeParams,
ArrayRef<const ParamDecl *> declParams, GenericSignature genericSig,
bool includeDefaultArgs) {
assert(declParams.empty() || typeParams.size() == declParams.size());
bool modifiedBuilder = false;
bool needComma = false;
// Iterate over each parameter.
for (unsigned i = 0; i != typeParams.size(); ++i) {
auto &typeParam = typeParams[i];
Identifier argName = typeParam.getLabel();
Identifier bodyName;
bool isIUO = false;
bool hasDefault = false;
if (!declParams.empty()) {
const ParamDecl *PD = declParams[i];
hasDefault =
PD->isDefaultArgument() && !isNonDesirableImportedDefaultArg(PD);
// Skip default arguments if we're either not including them or they
// aren't interesting
if (hasDefault &&
(!includeDefaultArgs || !hasInterestingDefaultValue(PD)))
continue;
argName = PD->getArgumentName();
bodyName = PD->getParameterName();
isIUO = PD->isImplicitlyUnwrappedOptional();
}
bool isVariadic = typeParam.isVariadic();
bool isInOut = typeParam.isInOut();
bool isAutoclosure = typeParam.isAutoClosure();
Type paramTy = typeParam.getPlainType();
if (isVariadic)
paramTy = ParamDecl::getVarargBaseTy(paramTy);
Type contextTy;
if (auto typeContext = CurrDeclContext->getInnermostTypeContext())
contextTy = typeContext->getDeclaredTypeInContext();
if (needComma)
Builder.addComma();
Builder.addCallArgument(argName, bodyName,
eraseArchetypes(paramTy, genericSig), contextTy,
isVariadic, isInOut, isIUO, isAutoclosure,
/*IsLabeledTrailingClosure=*/false,
/*IsForOperator=*/false, hasDefault);
modifiedBuilder = true;
needComma = true;
}
return modifiedBuilder;
}
bool CompletionLookup::addCallArgumentPatterns(
CodeCompletionResultBuilder &Builder, const AnyFunctionType *AFT,
const ParameterList *Params, GenericSignature genericSig,
bool includeDefaultArgs) {
ArrayRef<const ParamDecl *> declParams;
if (Params)
declParams = Params->getArray();
return addCallArgumentPatterns(Builder, AFT->getParams(), declParams,
genericSig, includeDefaultArgs);
}
void CompletionLookup::addEffectsSpecifiers(
CodeCompletionResultBuilder &Builder, const AnyFunctionType *AFT,
const AbstractFunctionDecl *AFD, bool forceAsync) {
assert(AFT != nullptr);
// 'async'.
if (forceAsync || (AFD && AFD->hasAsync()) ||
(AFT->hasExtInfo() && AFT->isAsync()))
Builder.addAnnotatedAsync();
// 'throws' or 'rethrows'.
if (AFD && AFD->getAttrs().hasAttribute<RethrowsAttr>())
Builder.addAnnotatedRethrows();
else if (AFT->hasExtInfo() && AFT->isThrowing())
Builder.addAnnotatedThrows();
}
void CompletionLookup::addPoundAvailable(std::optional<StmtKind> ParentKind) {
if (ParentKind != StmtKind::If && ParentKind != StmtKind::Guard)
return;
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Keyword,
// FIXME: SemanticContextKind::Local is not correct.
// Use 'None' (and fix prioritization) or introduce a new context.
SemanticContextKind::Local);
Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific);
Builder.addBaseName("available");
Builder.addLeftParen();
Builder.addSimpleTypedParameter("Platform", /*IsVarArg=*/true);
Builder.addComma();
Builder.addTextChunk("*");
Builder.addRightParen();
}
void CompletionLookup::addPoundSelector(bool needPound) {
// #selector is only available when the Objective-C runtime is.
if (!Ctx.LangOpts.EnableObjCInterop)
return;
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Keyword, SemanticContextKind::None);
if (needPound)
Builder.addTextChunk("#selector");
else
Builder.addTextChunk("selector");
Builder.addLeftParen();
Builder.addSimpleTypedParameter("@objc method", /*IsVarArg=*/false);
Builder.addRightParen();
Builder.addTypeAnnotation("Selector");
// This function is called only if the context type is 'Selector'.
Builder.setResultTypes(Ctx.getSelectorType());
Builder.setTypeContext(expectedTypeContext, CurrDeclContext);
}
void CompletionLookup::addPoundKeyPath(bool needPound) {
// #keyPath is only available when the Objective-C runtime is.
if (!Ctx.LangOpts.EnableObjCInterop)
return;
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Keyword, SemanticContextKind::None);
if (needPound)
Builder.addTextChunk("#keyPath");
else
Builder.addTextChunk("keyPath");
Builder.addLeftParen();
Builder.addSimpleTypedParameter("@objc property sequence",
/*IsVarArg=*/false);
Builder.addRightParen();
Builder.addTypeAnnotation("String");
Builder.setResultTypes(Ctx.getStringType());
Builder.setTypeContext(expectedTypeContext, CurrDeclContext);
}
SemanticContextKind
CompletionLookup::getSemanticContextKind(const ValueDecl *VD) {
// FIXME: to get the corect semantic context we need to know how lookup
// would have found the VD. For now, just infer a reasonable semantics.
if (!VD)
return SemanticContextKind::CurrentModule;
DeclContext *calleeDC = VD->getDeclContext();
if (calleeDC->isTypeContext())
// FIXME: We should distinguish CurrentNominal and Super. We need to
// propagate the base type to do that.
return SemanticContextKind::CurrentNominal;
if (calleeDC->isLocalContext())
return SemanticContextKind::Local;
if (calleeDC->getParentModule() == CurrModule)
return SemanticContextKind::CurrentModule;
return SemanticContextKind::OtherModule;
}
void CompletionLookup::addSubscriptCallPattern(
const AnyFunctionType *AFT, const SubscriptDecl *SD,
const std::optional<SemanticContextKind> SemanticContext) {
foundFunction(AFT);
GenericSignature genericSig;
if (SD)
genericSig = SD->getGenericSignatureOfContext();
CodeCompletionResultBuilder Builder = makeResultBuilder(
SD ? CodeCompletionResultKind::Declaration
: CodeCompletionResultKind::Pattern,
SemanticContext ? *SemanticContext : getSemanticContextKind(SD));
if (SD)
Builder.setAssociatedDecl(SD);
if (!HaveLParen) {
Builder.addLeftBracket();
} else {
// Add 'ArgumentLabels' only if it has '['. Without existing '[',
// consider it suggesting 'subscript' itself, not call arguments for it.
Builder.addFlair(CodeCompletionFlairBit::ArgumentLabels);
Builder.addAnnotatedLeftBracket();
}
ArrayRef<const ParamDecl *> declParams;
if (SD)
declParams = SD->getIndices()->getArray();
addCallArgumentPatterns(Builder, AFT->getParams(), declParams, genericSig);
if (!HaveLParen)
Builder.addRightBracket();
else
Builder.addAnnotatedRightBracket();
if (SD && SD->isImplicitlyUnwrappedOptional())
addTypeAnnotationForImplicitlyUnwrappedOptional(Builder, AFT->getResult(),
genericSig);
else
addTypeAnnotation(Builder, AFT->getResult(), genericSig);
}
void CompletionLookup::addFunctionCallPattern(
const AnyFunctionType *AFT, const AbstractFunctionDecl *AFD,
const std::optional<SemanticContextKind> SemanticContext) {
GenericSignature genericSig;
if (AFD)
genericSig = AFD->getGenericSignatureOfContext();
// Add the pattern, possibly including any default arguments.
auto addPattern = [&](ArrayRef<const ParamDecl *> declParams = {},
bool includeDefaultArgs = true) {
CodeCompletionResultBuilder Builder = makeResultBuilder(
AFD ? CodeCompletionResultKind::Declaration
: CodeCompletionResultKind::Pattern,
SemanticContext ? *SemanticContext : getSemanticContextKind(AFD));
Builder.addFlair(CodeCompletionFlairBit::ArgumentLabels);
if (DotLoc) {
Builder.setNumBytesToErase(Ctx.SourceMgr.getByteDistance(
DotLoc, Ctx.SourceMgr.getIDEInspectionTargetLoc()));
}
if (AFD)
Builder.setAssociatedDecl(AFD);
if (!HaveLParen)
Builder.addLeftParen();
else
Builder.addAnnotatedLeftParen();
addCallArgumentPatterns(Builder, AFT->getParams(), declParams, genericSig,
includeDefaultArgs);
// The rparen matches the lparen here so that we insert both or neither.
if (!HaveLParen)
Builder.addRightParen();
else
Builder.addAnnotatedRightParen();
addEffectsSpecifiers(Builder, AFT, AFD);
if (AFD && AFD->isImplicitlyUnwrappedOptional())
addTypeAnnotationForImplicitlyUnwrappedOptional(Builder, AFT->getResult(),
genericSig);
else
addTypeAnnotation(Builder, AFT->getResult(), genericSig);
Builder.setCanCurrDeclContextHandleAsync(CanCurrDeclContextHandleAsync);
};
if (!AFD || !AFD->getInterfaceType()->is<AnyFunctionType>()) {
// Probably, calling closure type expression.
foundFunction(AFT);
addPattern();
return;
} else {
// Calling function or method.
foundFunction(AFD);
// FIXME: Hack because we don't know we are calling instance
// method or not. There's invariant that funcTy is derived from AFD.
// Only if we are calling instance method on meta type, AFT is still
// curried. So we should be able to detect that by comparing curried level
// of AFT and the interface type of AFD.
auto getCurriedLevel = [](const AnyFunctionType *funcTy) -> unsigned {
unsigned level = 0;
while ((funcTy = funcTy->getResult()->getAs<AnyFunctionType>()))
++level;
return level;
};
bool isImplicitlyCurriedInstanceMethod =
(AFD->hasImplicitSelfDecl() &&
getCurriedLevel(AFT) ==
getCurriedLevel(
AFD->getInterfaceType()->castTo<AnyFunctionType>()) &&
// NOTE: shouldn't be necessary, but just in case curried level check
// is insufficient.
AFT->getParams().size() == 1 &&
AFT->getParams()[0].getLabel().empty());
if (isImplicitlyCurriedInstanceMethod) {
addPattern({AFD->getImplicitSelfDecl()}, /*includeDefaultArgs=*/true);
} else {
if (shouldAddItemWithoutDefaultArgs(AFD))
addPattern(AFD->getParameters()->getArray(),
/*includeDefaultArgs=*/false);
addPattern(AFD->getParameters()->getArray(),
/*includeDefaultArgs=*/true);
}
}
}
bool CompletionLookup::isImplicitlyCurriedInstanceMethod(
const AbstractFunctionDecl *FD) {
if (FD->isStatic())
return false;
switch (Kind) {
case LookupKind::ValueExpr:
case LookupKind::StoredProperty:
return ExprType->is<AnyMetatypeType>();
case LookupKind::ValueInDeclContext:
if (InsideStaticMethod)
return FD->getDeclContext() == CurrentMethod->getDeclContext();
if (auto Init = dyn_cast<Initializer>(CurrDeclContext)) {
if (auto PatInit = dyn_cast<PatternBindingInitializer>(Init)) {
if (PatInit->getInitializedLazyVar())
return false;
}
return FD->getDeclContext() == Init->getInnermostTypeContext();
}
return false;
case LookupKind::EnumElement:
case LookupKind::Type:
case LookupKind::TypeInDeclContext:
case LookupKind::GenericRequirement:
llvm_unreachable("cannot have a method call while doing a "
"type completion");
case LookupKind::ImportFromModule:
return false;
}
llvm_unreachable("Unhandled LookupKind in switch.");
}
void CompletionLookup::addMethodCall(const FuncDecl *FD,
DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
if (FD->getBaseIdentifier().empty())
return;
foundFunction(FD);
const Identifier Name = FD->getBaseIdentifier();
assert(!Name.empty() && "name should not be empty");
Type FunctionType = getTypeOfMember(FD, dynamicLookupInfo);
assert(FunctionType);
auto AFT = FunctionType->getAs<AnyFunctionType>();
bool IsImplicitlyCurriedInstanceMethod = false;
if (FD->hasImplicitSelfDecl()) {
IsImplicitlyCurriedInstanceMethod = isImplicitlyCurriedInstanceMethod(FD);
// Strip off '(_ self: Self)' if needed.
if (AFT && !IsImplicitlyCurriedInstanceMethod) {
AFT = AFT->getResult()->getAs<AnyFunctionType>();
// Check for duplicates with the adjusted type too.
if (isDuplicate(FD, AFT))
return;
}
}
bool trivialTrailingClosure = false;
if (AFT && !IsImplicitlyCurriedInstanceMethod)
trivialTrailingClosure = hasTrivialTrailingClosure(FD, AFT);
std::optional<ContextualNotRecommendedReason> NotRecommended;
bool implictlyAsync = false;
analyzeActorIsolation(FD, AFT, implictlyAsync, NotRecommended);
// Add the method, possibly including any default arguments.
auto addMethodImpl = [&](bool includeDefaultArgs = true,
bool trivialTrailingClosure = false) {
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(FD, Reason, dynamicLookupInfo));
Builder.setHasAsyncAlternative(
FD->getAsyncAlternative() &&
!FD->getAsyncAlternative()->shouldHideFromEditor());
Builder.setCanCurrDeclContextHandleAsync(CanCurrDeclContextHandleAsync);
Builder.setAssociatedDecl(FD);
if (IsSuperRefExpr && CurrentMethod &&
CurrentMethod->getOverriddenDecl() == FD)
Builder.addFlair(CodeCompletionFlairBit::SuperChain);
if (NotRecommended)
Builder.setContextualNotRecommended(*NotRecommended);
addLeadingDot(Builder);
addValueBaseName(Builder, Name);
if (IsDynamicLookup)
Builder.addDynamicLookupMethodCallTail();
else if (FD->getAttrs().hasAttribute<OptionalAttr>())
Builder.addOptionalMethodCallTail();
if (!AFT) {
addTypeAnnotation(Builder, FunctionType,
FD->getGenericSignatureOfContext());
return;
}
if (IsImplicitlyCurriedInstanceMethod) {
Builder.addLeftParen();
addCallArgumentPatterns(
Builder, AFT->getParams(), {FD->getImplicitSelfDecl()},
FD->getGenericSignatureOfContext(), includeDefaultArgs);
Builder.addRightParen();
} else if (trivialTrailingClosure) {
Builder.addBraceStmtWithCursor(" { code }");
addEffectsSpecifiers(Builder, AFT, FD, implictlyAsync);
} else {
Builder.addLeftParen();
addCallArgumentPatterns(Builder, AFT, FD->getParameters(),
FD->getGenericSignatureOfContext(),
includeDefaultArgs);
Builder.addRightParen();
addEffectsSpecifiers(Builder, AFT, FD, implictlyAsync);
}
// Build type annotation.
Type ResultType = AFT->getResult();
// As we did with parameters in addParamPatternFromFunction,
// for regular methods we'll print '!' after implicitly
// unwrapped optional results.
bool IsIUO = !IsImplicitlyCurriedInstanceMethod &&
FD->isImplicitlyUnwrappedOptional();
PrintOptions PO;
PO.OpaqueReturnTypePrinting =
PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword;
PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO;
if (auto typeContext = CurrDeclContext->getInnermostTypeContext())
PO.setBaseType(typeContext->getDeclaredTypeInContext());
Type AnnotationTy =
eraseArchetypes(ResultType, FD->getGenericSignatureOfContext());
if (Builder.shouldAnnotateResults()) {
Builder.withNestedGroup(
CodeCompletionString::Chunk::ChunkKind::TypeAnnotationBegin, [&] {
CodeCompletionStringPrinter printer(Builder);
auto TL = TypeLoc::withoutLoc(AnnotationTy);
printer.printTypePre(TL);
if (IsImplicitlyCurriedInstanceMethod) {
auto *FnType = AnnotationTy->castTo<AnyFunctionType>();
AnyFunctionType::printParams(FnType->getParams(), printer,
PrintOptions());
AnnotationTy = FnType->getResult();
printer.printText(" -> ");
}
// What's left is the result type.
if (AnnotationTy->isVoid())
AnnotationTy = Ctx.getVoidDecl()->getDeclaredInterfaceType();
AnnotationTy.print(printer, PO);
printer.printTypePost(TL);
});
} else {
llvm::SmallString<32> TypeStr;
llvm::raw_svector_ostream OS(TypeStr);
if (IsImplicitlyCurriedInstanceMethod) {
auto *FnType = AnnotationTy->castTo<AnyFunctionType>();
AnyFunctionType::printParams(FnType->getParams(), OS);
AnnotationTy = FnType->getResult();
OS << " -> ";
}
// What's left is the result type.
if (AnnotationTy->isVoid())
AnnotationTy = Ctx.getVoidDecl()->getDeclaredInterfaceType();
AnnotationTy.print(OS, PO);
Builder.addTypeAnnotation(TypeStr);
}
Builder.setResultTypes(ResultType);
Builder.setTypeContext(expectedTypeContext, CurrDeclContext);
if (isUnresolvedMemberIdealType(ResultType))
Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific);
};
// Do not add imported C++ methods that are treated as unsafe in Swift.
if (Importer->isUnsafeCXXMethod(FD))
return;
if (!AFT || IsImplicitlyCurriedInstanceMethod) {
addMethodImpl();
} else {
if (trivialTrailingClosure)
addMethodImpl(/*includeDefaultArgs=*/false,
/*trivialTrailingClosure=*/true);
if (shouldAddItemWithoutDefaultArgs(FD))
addMethodImpl(/*includeDefaultArgs=*/false);
addMethodImpl(/*includeDefaultArgs=*/true);
}
}
void CompletionLookup::addConstructorCall(const ConstructorDecl *CD,
DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo,
std::optional<Type> BaseType,
std::optional<Type> Result,
bool IsOnType, Identifier addName) {
foundFunction(CD);
Type MemberType = getTypeOfMember(CD, BaseType.value_or(ExprType));
AnyFunctionType *ConstructorType = nullptr;
if (auto MemberFuncType = MemberType->getAs<AnyFunctionType>())
ConstructorType = MemberFuncType->getResult()->castTo<AnyFunctionType>();
bool needInit = false;
if (!IsOnType) {
assert(addName.empty());
needInit = true;
} else if (addName.empty() && HaveDot) {
needInit = true;
}
// If we won't be able to provide a result, bail out.
if (!ConstructorType && addName.empty() && !needInit)
return;
// Add the constructor, possibly including any default arguments.
auto addConstructorImpl = [&](bool includeDefaultArgs = true) {
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(CD, Reason, dynamicLookupInfo));
Builder.setAssociatedDecl(CD);
if (IsSuperRefExpr && CurrentMethod &&
CurrentMethod->getOverriddenDecl() == CD)
Builder.addFlair(CodeCompletionFlairBit::SuperChain);
if (needInit) {
assert(addName.empty());
addLeadingDot(Builder);
Builder.addBaseName("init");
} else if (!addName.empty()) {
addIdentifier(Builder, addName);
} else {
Builder.addFlair(CodeCompletionFlairBit::ArgumentLabels);
}
if (!ConstructorType) {
addTypeAnnotation(Builder, MemberType,
CD->getGenericSignatureOfContext());
return;
}
if (!HaveLParen)
Builder.addLeftParen();
else
Builder.addAnnotatedLeftParen();
addCallArgumentPatterns(Builder, ConstructorType, CD->getParameters(),
CD->getGenericSignatureOfContext(),
includeDefaultArgs);
// The rparen matches the lparen here so that we insert both or neither.
if (!HaveLParen)
Builder.addRightParen();
else
Builder.addAnnotatedRightParen();
addEffectsSpecifiers(Builder, ConstructorType, CD);
if (!Result.has_value())
Result = ConstructorType->getResult();
if (CD->isImplicitlyUnwrappedOptional()) {
addTypeAnnotationForImplicitlyUnwrappedOptional(
Builder, *Result, CD->getGenericSignatureOfContext());
} else {
addTypeAnnotation(Builder, *Result, CD->getGenericSignatureOfContext());
}
Builder.setCanCurrDeclContextHandleAsync(CanCurrDeclContextHandleAsync);
};
if (ConstructorType && shouldAddItemWithoutDefaultArgs(CD))
addConstructorImpl(/*includeDefaultArgs=*/false);
addConstructorImpl();
}
void CompletionLookup::addConstructorCallsForType(
Type type, Identifier name, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
if (!Sink.addInitsToTopLevel)
return;
// Existential types cannot be instantiated. e.g. 'MyProtocol()'.
if (type->isExistentialType())
return;
// 'AnyObject' is not initializable.
// FIXME: Should we do this in 'AnyObjectLookupRequest'?
if (type->isAnyObject())
return;
assert(CurrDeclContext);
auto results =
swift::lookupSemanticMember(const_cast<DeclContext *>(CurrDeclContext),
type, DeclBaseName::createConstructor());
for (const auto &entry : results.allResults()) {
auto *init = cast<ConstructorDecl>(entry.getValueDecl());
if (init->shouldHideFromEditor())
continue;
addConstructorCall(cast<ConstructorDecl>(init), Reason, dynamicLookupInfo,
type, std::nullopt,
/*IsOnType=*/true, name);
}
}
void CompletionLookup::addSubscriptCall(const SubscriptDecl *SD,
DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
// Don't add subscript call to unqualified completion.
if (!ExprType)
return;
// Subscript after '.' is valid only after type part of Swift keypath
// expression. (e.g. '\TyName.SubTy.[0])
if (HaveDot && !IsAfterSwiftKeyPathRoot)
return;
auto subscriptType =
getTypeOfMember(SD, dynamicLookupInfo)->getAs<AnyFunctionType>();
if (!subscriptType)
return;
std::optional<ContextualNotRecommendedReason> NotRecommended;
bool implictlyAsync = false;
analyzeActorIsolation(SD, subscriptType, implictlyAsync, NotRecommended);
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(SD, Reason, dynamicLookupInfo));
Builder.setCanCurrDeclContextHandleAsync(CanCurrDeclContextHandleAsync);
Builder.setAssociatedDecl(SD);
if (NotRecommended)
Builder.setContextualNotRecommended(*NotRecommended);
// '\TyName#^TOKEN^#' requires leading dot.
if (!HaveDot && IsAfterSwiftKeyPathRoot)
Builder.addLeadingDot();
if (NeedOptionalUnwrap) {
Builder.setNumBytesToErase(NumBytesToEraseForOptionalUnwrap);
Builder.addQuestionMark();
}
Builder.addLeftBracket();
addCallArgumentPatterns(Builder, subscriptType, SD->getIndices(),
SD->getGenericSignatureOfContext(), true);
Builder.addRightBracket();
// Add a type annotation.
Type resultTy = subscriptType->getResult();
if (IsDynamicLookup) {
// Values of properties that were found on a AnyObject have
// std::optional<T> type.
resultTy = OptionalType::get(resultTy);
}
if (implictlyAsync)
Builder.addAnnotatedAsync();
addTypeAnnotation(Builder, resultTy, SD->getGenericSignatureOfContext());
}
static StringRef getTypeAnnotationString(const NominalTypeDecl *NTD,
SmallVectorImpl<char> &stash) {
SmallVector<StringRef, 1> attrRoleStrs;
if (NTD->getAttrs().hasAttribute<PropertyWrapperAttr>())
attrRoleStrs.push_back("Property Wrapper");
if (NTD->getAttrs().hasAttribute<ResultBuilderAttr>())
attrRoleStrs.push_back("Result Builder");
if (NTD->isGlobalActor())
attrRoleStrs.push_back("Global Actor");
if (attrRoleStrs.empty())
return StringRef();
if (attrRoleStrs.size() == 1)
return attrRoleStrs[0];
assert(stash.empty());
llvm::raw_svector_ostream OS(stash);
llvm::interleave(attrRoleStrs, OS, ", ");
return {stash.data(), stash.size()};
}
void CompletionLookup::addNominalTypeRef(const NominalTypeDecl *NTD,
DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(NTD, Reason, dynamicLookupInfo));
Builder.setAssociatedDecl(NTD);
addLeadingDot(Builder);
addValueBaseName(Builder, NTD->getBaseName());
// Substitute the base type for a nested type if needed.
auto nominalTy = getTypeOfMember(NTD, dynamicLookupInfo);
// "Fake" annotation for custom attribute types.
SmallVector<char, 0> stash;
StringRef customAttributeAnnotation = getTypeAnnotationString(NTD, stash);
if (!customAttributeAnnotation.empty()) {
Builder.addTypeAnnotation(customAttributeAnnotation);
} else {
addTypeAnnotation(Builder, nominalTy);
}
// Override the type relation for NominalTypes. Use the better relation
// for the metatypes and the instance type. For example,
//
// func receiveInstance(_: Int) {}
// func receiveMetatype(_: Int.Type) {}
//
// We want to suggest 'Int' as 'Identical' for both arguments.
Builder.setResultTypes({MetatypeType::get(nominalTy), nominalTy});
Builder.setTypeContext(expectedTypeContext, CurrDeclContext);
}
Type CompletionLookup::getTypeAliasType(const TypeAliasDecl *TAD,
DynamicLookupInfo dynamicLookupInfo) {
// Substitute the base type for a nested typealias if needed.
auto ty = getTypeOfMember(TAD, dynamicLookupInfo);
auto *typeAliasTy = dyn_cast<TypeAliasType>(ty.getPointer());
if (!typeAliasTy)
return ty;
// If the underlying type has an error, prefer to print the full typealias,
// otherwise get the underlying type. We only want the direct underlying type,
// not the full desugared type, since that more faithfully reflects what's
// written in source.
Type underlyingTy = typeAliasTy->getSinglyDesugaredType();
if (underlyingTy->hasError())
return ty;
// The underlying type might be unbound for e.g:
//
// struct S<T> {}
// typealias X = S
//
// Introduce type parameters such that we print the underlying type as
// 'S<T>'. We only expect unbound generics at the top-level of a type-alias,
// they are rejected by type resolution in any other position.
//
// FIXME: This is a hack using the declared interface type isn't correct
// since the generic parameters ought to be introduced at a higher depth,
// i.e we should be treating it as `typealias X<T> = S<T>`. Ideally this would
// be fixed by desugaring the unbound typealias during type resolution. For
// now this is fine though since we only use the resulting type for printing
// the type annotation; the type relation logic currently skips type
// parameters.
if (auto *UGT = underlyingTy->getAs<UnboundGenericType>())
underlyingTy = UGT->getDecl()->getDeclaredInterfaceType();
ASSERT(!underlyingTy->hasUnboundGenericType());
return underlyingTy;
}
void CompletionLookup::addTypeAliasRef(const TypeAliasDecl *TAD,
DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(TAD, Reason, dynamicLookupInfo));
Builder.setAssociatedDecl(TAD);
addLeadingDot(Builder);
addValueBaseName(Builder, TAD->getBaseName());
addTypeAnnotation(Builder, getTypeAliasType(TAD, dynamicLookupInfo));
}
void CompletionLookup::addGenericTypeParamRef(
const GenericTypeParamDecl *GP, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
assert(!GP->getName().empty());
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(GP, Reason, dynamicLookupInfo));
Builder.setAssociatedDecl(GP);
addLeadingDot(Builder);
addValueBaseName(Builder, GP->getBaseName());
addTypeAnnotation(Builder, GP->getDeclaredInterfaceType());
}
void CompletionLookup::addAssociatedTypeRef(
const AssociatedTypeDecl *AT, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(AT, Reason, dynamicLookupInfo));
Builder.setAssociatedDecl(AT);
addLeadingDot(Builder);
addValueBaseName(Builder, AT->getBaseName());
if (Type T = getAssociatedTypeType(AT))
addTypeAnnotation(Builder, T);
}
void CompletionLookup::addPrecedenceGroupRef(PrecedenceGroupDecl *PGD) {
auto semanticContext =
getSemanticContext(PGD, DeclVisibilityKind::VisibleAtTopLevel, {});
CodeCompletionResultBuilder builder =
makeResultBuilder(CodeCompletionResultKind::Declaration, semanticContext);
addIdentifier(builder, PGD->getName());
builder.setAssociatedDecl(PGD);
}
void CompletionLookup::addBuiltinMemberRef(StringRef Name,
Type TypeAnnotation) {
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Pattern, SemanticContextKind::CurrentNominal);
addLeadingDot(Builder);
Builder.addBaseName(Name);
addTypeAnnotation(Builder, TypeAnnotation);
}
void CompletionLookup::addEnumElementRef(const EnumElementDecl *EED,
DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo,
bool HasTypeContext) {
if (!EED->hasName() || !EED->isAccessibleFrom(CurrDeclContext) ||
EED->shouldHideFromEditor())
return;
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(EED, Reason, dynamicLookupInfo));
Builder.setAssociatedDecl(EED);
addLeadingDot(Builder);
addValueBaseName(Builder, EED->getBaseIdentifier());
// Enum element is of function type; (Self.type) -> Self or
// (Self.Type) -> (Args...) -> Self.
Type EnumType = getTypeOfMember(EED, dynamicLookupInfo);
if (EnumType->is<AnyFunctionType>())
EnumType = EnumType->castTo<AnyFunctionType>()->getResult();
if (EnumType->is<FunctionType>()) {
Builder.addLeftParen();
addCallArgumentPatterns(Builder, EnumType->castTo<FunctionType>(),
EED->getParameterList(),
EED->getGenericSignatureOfContext());
Builder.addRightParen();
// Extract result as the enum type.
EnumType = EnumType->castTo<FunctionType>()->getResult();
}
addTypeAnnotation(Builder, EnumType, EED->getGenericSignatureOfContext());
if (isUnresolvedMemberIdealType(EnumType))
Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific);
}
static StringRef getTypeAnnotationString(const MacroDecl *MD,
SmallVectorImpl<char> &stash) {
auto roles = MD->getMacroRoles();
SmallVector<StringRef, 1> roleStrs;
for (auto role : getAllMacroRoles()) {
if (!roles.contains(role))
continue;
switch (role) {
case MacroRole::Accessor:
roleStrs.push_back("Accessor Macro");
break;
case MacroRole::CodeItem:
roleStrs.push_back("Code Item Macro");
break;
case MacroRole::Conformance:
roleStrs.push_back("Conformance Macro");
break;
case MacroRole::Declaration:
roleStrs.push_back("Declaration Macro");
break;
case MacroRole::Expression:
roleStrs.push_back("Expression Macro");
break;
case MacroRole::Extension:
roleStrs.push_back("Extension Macro");
break;
case MacroRole::Member:
roleStrs.push_back("Member Macro");
break;
case MacroRole::MemberAttribute:
roleStrs.push_back("Member Attribute Macro");
break;
case MacroRole::Peer:
roleStrs.push_back("Peer Macro");
break;
case MacroRole::Preamble:
roleStrs.push_back("Preamble Macro");
break;
case MacroRole::Body:
roleStrs.push_back("Body Macro");
break;
}
}
if (roleStrs.empty())
return "Macro";
if (roleStrs.size() == 1)
return roleStrs[0];
assert(stash.empty());
llvm::raw_svector_ostream OS(stash);
llvm::interleave(roleStrs, OS, ", ");
return {stash.data(), stash.size()};
}
void CompletionLookup::addMacroExpansion(const MacroDecl *MD,
DeclVisibilityKind Reason) {
if (!MD->hasName() || !MD->isAccessibleFrom(CurrDeclContext) ||
MD->shouldHideFromEditor())
return;
OptionSet<CustomAttributeKind> expectedKinds =
expectedTypeContext.getExpectedCustomAttributeKinds();
if (expectedKinds) {
CodeCompletionMacroRoles expectedRoles =
getCompletionMacroRoles(expectedKinds);
CodeCompletionMacroRoles roles = getCompletionMacroRoles(MD);
if (!(roles & expectedRoles))
return;
}
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(MD, Reason, DynamicLookupInfo()));
Builder.setAssociatedDecl(MD);
addValueBaseName(Builder, MD->getBaseIdentifier());
Type macroType = MD->getInterfaceType();
if (MD->parameterList && MD->parameterList->size() > 0) {
Builder.addLeftParen();
addCallArgumentPatterns(Builder, macroType->castTo<AnyFunctionType>(),
MD->parameterList,
MD->getGenericSignature());
Builder.addRightParen();
}
auto roles = MD->getMacroRoles();
if (roles.containsOnly(MacroRole::Expression)) {
addTypeAnnotation(Builder, MD->getResultInterfaceType(),
MD->getGenericSignature());
} else {
llvm::SmallVector<char, 0> stash;
Builder.addTypeAnnotation(getTypeAnnotationString(MD, stash));
}
}
void CompletionLookup::addKeyword(StringRef Name, Type TypeAnnotation,
SemanticContextKind SK,
CodeCompletionKeywordKind KeyKind,
unsigned NumBytesToErase) {
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Keyword, SK);
addLeadingDot(Builder);
Builder.addKeyword(Name);
Builder.setKeywordKind(KeyKind);
if (TypeAnnotation)
addTypeAnnotation(Builder, TypeAnnotation);
if (NumBytesToErase > 0)
Builder.setNumBytesToErase(NumBytesToErase);
}
void CompletionLookup::addKeyword(StringRef Name, StringRef TypeAnnotation,
CodeCompletionKeywordKind KeyKind,
CodeCompletionFlair flair) {
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Keyword, SemanticContextKind::None);
Builder.addFlair(flair);
addLeadingDot(Builder);
Builder.addKeyword(Name);
Builder.setKeywordKind(KeyKind);
if (!TypeAnnotation.empty())
Builder.addTypeAnnotation(TypeAnnotation);
}
void CompletionLookup::addDeclAttrParamKeyword(StringRef Name,
ArrayRef<StringRef> Parameters,
StringRef Annotation,
bool NeedSpecify) {
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Keyword, SemanticContextKind::None);
Builder.addDeclAttrParamKeyword(Name, Parameters, Annotation, NeedSpecify);
}
void CompletionLookup::addDeclAttrKeyword(StringRef Name,
StringRef Annotation) {
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Keyword, SemanticContextKind::None);
Builder.addDeclAttrKeyword(Name, Annotation);
}
/// Add the compound function name for the given function.
/// Returns \c true if the compound function name is actually used.
bool CompletionLookup::addCompoundFunctionNameIfDesiable(
AbstractFunctionDecl *AFD, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
auto funcTy =
getTypeOfMember(AFD, dynamicLookupInfo)->getAs<AnyFunctionType>();
bool dropCurryLevel = funcTy && AFD->getDeclContext()->isTypeContext() &&
!isImplicitlyCurriedInstanceMethod(AFD);
if (dropCurryLevel)
funcTy = funcTy->getResult()->getAs<AnyFunctionType>();
bool useFunctionReference = PreferFunctionReferencesToCalls;
if (!useFunctionReference && funcTy) {
// We know that the CodeCompletionResultType is AST-based so we can pass
// nullptr for USRTypeContext.
auto maxFuncTyRel = CodeCompletionResultType(funcTy).calculateTypeRelation(
&expectedTypeContext, CurrDeclContext, /*USRTypeContext=*/nullptr);
auto maxResultTyRel = CodeCompletionResultType(funcTy->getResult()).calculateTypeRelation(
&expectedTypeContext, CurrDeclContext, /*USRTypeContext=*/nullptr);
useFunctionReference = maxFuncTyRel > maxResultTyRel;
}
if (!useFunctionReference)
return false;
// Check for duplicates with the adjusted type too.
if (dropCurryLevel && isDuplicate(AFD, funcTy))
return true;
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(AFD, Reason, dynamicLookupInfo));
Builder.setAssociatedDecl(AFD);
// Base name
addLeadingDot(Builder);
addValueBaseName(Builder, AFD->getBaseName());
// Add the argument labels.
const auto ArgLabels = AFD->getName().getArgumentNames();
if (!ArgLabels.empty()) {
if (!HaveLParen)
Builder.addLeftParen();
else
Builder.addAnnotatedLeftParen();
for (auto ArgLabel : ArgLabels) {
if (ArgLabel.empty())
Builder.addTextChunk("_");
else
Builder.addTextChunk(ArgLabel.str());
Builder.addTextChunk(":");
}
Builder.addRightParen();
}
if (funcTy)
addTypeAnnotation(Builder, funcTy, AFD->getGenericSignatureOfContext());
return true;
}
void CompletionLookup::onLookupNominalTypeMembers(NominalTypeDecl *NTD,
DeclVisibilityKind Reason) {
// Remember the decl name to
SmallString<32> buffer;
llvm::raw_svector_ostream OS(buffer);
PrintOptions PS = PrintOptions::printDocInterface();
PS.FullyQualifiedTypes = true;
NTD->getDeclaredType()->print(OS, PS);
NullTerminatedStringRef qualifiedName(
buffer, *CompletionContext->getResultSink().Allocator);
CompletionContext->LookedupNominalTypeNames.push_back(qualifiedName);
}
Type CompletionLookup::normalizeTypeForDuplicationCheck(Type Ty) {
return Ty.transformRec([](Type T) -> std::optional<Type> {
if (auto opaque = T->getAs<OpaqueTypeArchetypeType>()) {
/// Opaque type has a _invisible_ substitution map. Since IDE can't
/// differentiate them, replace it with empty substitution map.
return Type(OpaqueTypeArchetypeType::get(opaque->getDecl(),
opaque->getInterfaceType(),
/*Substitutions=*/{}));
}
return std::nullopt;
});
}
void CompletionLookup::foundDecl(ValueDecl *D, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
assert(Reason !=
DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal &&
"Including derived requirement in non-override lookup");
if (D->shouldHideFromEditor())
return;
if (IsKeyPathExpr && !KeyPathFilter(D, Reason, dynamicLookupInfo))
return;
if (IsSwiftKeyPathExpr && !SwiftKeyPathFilter(D, Reason))
return;
// If we've seen this decl+type before (possible when multiple lookups are
// performed e.g. because of ambiguous base types), bail.
if (isDuplicate(D, dynamicLookupInfo))
return;
// FIXME(InterfaceTypeRequest): Remove this.
(void)D->getInterfaceType();
switch (Kind) {
case LookupKind::ValueExpr:
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
// Do we want compound function names here?
if (addCompoundFunctionNameIfDesiable(CD, Reason, dynamicLookupInfo))
return;
if (auto MT = ExprType->getAs<AnyMetatypeType>()) {
Type Ty = MT->getInstanceType();
assert(Ty && "Cannot find instance type.");
// If instance type is type alias, show users that the constructed
// type is the typealias instead of the underlying type of the alias.
std::optional<Type> Result = std::nullopt;
if (!CD->getInterfaceType()->is<ErrorType>() &&
isa<TypeAliasType>(Ty.getPointer()) &&
Ty->getDesugaredType() ==
CD->getResultInterfaceType().getPointer()) {
Result = Ty;
}
// If the expression type is not a static metatype or an archetype, the
// base is not a type. Direct call syntax is illegal on values, so we
// only add initializer completions if we do not have a left parenthesis
// and either the initializer is required, the base type's instance type
// is not a class, or this is a 'self' or 'super' reference.
if (IsStaticMetatype || IsUnresolvedMember || Ty->is<ArchetypeType>())
addConstructorCall(CD, Reason, dynamicLookupInfo, std::nullopt,
Result,
/*isOnType*/ true);
else if ((IsSelfRefExpr || IsSuperRefExpr || !Ty->is<ClassType>() ||
CD->isRequired()) &&
!HaveLParen)
addConstructorCall(CD, Reason, dynamicLookupInfo, std::nullopt,
Result,
/*isOnType*/ false);
return;
}
if (!HaveLParen) {
auto CDC = dyn_cast<ConstructorDecl>(CurrDeclContext);
if (!CDC)
return;
// For classes, we do not want 'init' completions for 'self' in
// non-convenience initializers and for 'super' in convenience
// initializers.
if (ExprType->is<ClassType>()) {
if ((IsSelfRefExpr && !CDC->isConvenienceInit()) ||
(IsSuperRefExpr && CDC->isConvenienceInit()))
return;
}
if (IsSelfRefExpr || IsSuperRefExpr)
addConstructorCall(CD, Reason, dynamicLookupInfo, std::nullopt,
std::nullopt,
/*IsOnType=*/false);
}
return;
}
if (HaveLParen)
return;
LLVM_FALLTHROUGH;
case LookupKind::ValueInDeclContext:
case LookupKind::ImportFromModule:
if (auto *VD = dyn_cast<VarDecl>(D)) {
addVarDeclRef(VD, Reason, dynamicLookupInfo);
return;
}
if (auto *FD = dyn_cast<FuncDecl>(D)) {
// We cannot call operators with a postfix parenthesis syntax.
if (FD->isBinaryOperator() || FD->isUnaryOperator())
return;
// We cannot call accessors. We use VarDecls and SubscriptDecls to
// produce completions that refer to getters and setters.
if (isa<AccessorDecl>(FD))
return;
// Do we want compound function names here?
if (addCompoundFunctionNameIfDesiable(FD, Reason, dynamicLookupInfo))
return;
addMethodCall(FD, Reason, dynamicLookupInfo);
// SE-0253: Callable values of user-defined nominal types.
if (FD->isCallAsFunctionMethod() && !HaveDot &&
(!ExprType || !ExprType->is<AnyMetatypeType>())) {
Type funcType = getTypeOfMember(FD, dynamicLookupInfo)
->castTo<AnyFunctionType>()
->getResult();
// Check for duplicates with the adjusted type too.
if (isDuplicate(FD, funcType))
return;
addFunctionCallPattern(
funcType->castTo<AnyFunctionType>(), FD,
getSemanticContext(FD, Reason, dynamicLookupInfo));
}
return;
}
if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
addNominalTypeRef(NTD, Reason, dynamicLookupInfo);
addConstructorCallsForType(NTD->getDeclaredInterfaceType(),
NTD->getName(), Reason, dynamicLookupInfo);
return;
}
if (auto *TAD = dyn_cast<TypeAliasDecl>(D)) {
addTypeAliasRef(TAD, Reason, dynamicLookupInfo);
auto type = TAD->mapTypeIntoContext(TAD->getDeclaredInterfaceType());
if (type->mayHaveMembers())
addConstructorCallsForType(type, TAD->getName(), Reason,
dynamicLookupInfo);
return;
}
if (auto *GP = dyn_cast<GenericTypeParamDecl>(D)) {
addGenericTypeParamRef(GP, Reason, dynamicLookupInfo);
auto type =
CurrDeclContext->mapTypeIntoContext(GP->getDeclaredInterfaceType());
addConstructorCallsForType(type, GP->getName(), Reason,
dynamicLookupInfo);
return;
}
if (auto *AT = dyn_cast<AssociatedTypeDecl>(D)) {
addAssociatedTypeRef(AT, Reason, dynamicLookupInfo);
return;
}
if (auto *EED = dyn_cast<EnumElementDecl>(D)) {
addEnumElementRef(EED, Reason, dynamicLookupInfo,
/*HasTypeContext=*/false);
return;
}
// Swift key path allows .[0]
if (auto *SD = dyn_cast<SubscriptDecl>(D)) {
addSubscriptCall(SD, Reason, dynamicLookupInfo);
return;
}
if (auto *MD = dyn_cast<MacroDecl>(D)) {
addMacroExpansion(MD, Reason);
return;
}
return;
case LookupKind::EnumElement:
handleEnumElement(D, Reason, dynamicLookupInfo);
return;
case LookupKind::Type:
case LookupKind::TypeInDeclContext:
if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
addNominalTypeRef(NTD, Reason, dynamicLookupInfo);
return;
}
if (auto *GP = dyn_cast<GenericTypeParamDecl>(D)) {
addGenericTypeParamRef(GP, Reason, dynamicLookupInfo);
return;
}
LLVM_FALLTHROUGH;
case LookupKind::GenericRequirement:
if (TypeAliasDecl *TAD = dyn_cast<TypeAliasDecl>(D)) {
if (Kind == LookupKind::GenericRequirement &&
!canBeUsedAsRequirementFirstType(BaseType, TAD))
return;
addTypeAliasRef(TAD, Reason, dynamicLookupInfo);
return;
}
if (auto *AT = dyn_cast<AssociatedTypeDecl>(D)) {
addAssociatedTypeRef(AT, Reason, dynamicLookupInfo);
return;
}
return;
case LookupKind::StoredProperty:
if (auto *VD = dyn_cast<VarDecl>(D)) {
if (VD->hasStorage()) {
addVarDeclRef(VD, Reason, dynamicLookupInfo);
}
return;
}
}
}
bool CompletionLookup::handleEnumElement(ValueDecl *D,
DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
if (auto *EED = dyn_cast<EnumElementDecl>(D)) {
addEnumElementRef(EED, Reason, dynamicLookupInfo,
/*HasTypeContext=*/true);
return true;
} else if (auto *ED = dyn_cast<EnumDecl>(D)) {
for (auto *Ele : ED->getAllElements()) {
addEnumElementRef(Ele, Reason, dynamicLookupInfo,
/*HasTypeContext=*/true);
}
return true;
}
return false;
}
bool CompletionLookup::tryTupleExprCompletions(Type ExprType) {
auto *TT = ExprType->getAs<TupleType>();
if (!TT)
return false;
unsigned Index = 0;
for (auto TupleElt : TT->getElements()) {
auto Ty = TupleElt.getType();
if (TupleElt.hasName()) {
addBuiltinMemberRef(TupleElt.getName().str(), Ty);
} else {
llvm::SmallString<4> IndexStr;
{
llvm::raw_svector_ostream OS(IndexStr);
OS << Index;
}
addBuiltinMemberRef(IndexStr, Ty);
}
++Index;
}
return true;
}
void CompletionLookup::tryFunctionIsolationCompletion(Type ExprType) {
auto *FT = ExprType->getAs<FunctionType>();
if (!FT || !FT->getIsolation().isErased())
return;
// The type of `.isolation` is `(any Actor)?`
auto *actorProto = Ctx.getProtocol(KnownProtocolKind::Actor);
auto memberTy = OptionalType::get(actorProto->getDeclaredExistentialType());
addBuiltinMemberRef(Ctx.Id_isolation.str(), memberTy);
}
bool CompletionLookup::tryFunctionCallCompletions(
Type ExprType, const ValueDecl *VD,
std::optional<SemanticContextKind> SemanticContext) {
ExprType = ExprType->getRValueType();
if (auto AFT = ExprType->getAs<AnyFunctionType>()) {
if (auto *AFD = dyn_cast_or_null<AbstractFunctionDecl>(VD)) {
addFunctionCallPattern(AFT, AFD, SemanticContext);
} else {
addFunctionCallPattern(AFT);
}
return true;
}
return false;
}
bool CompletionLookup::tryModuleCompletions(Type ExprType,
CodeCompletionFilter Filter) {
if (auto MT = ExprType->getAs<ModuleType>()) {
ModuleDecl *M = MT->getModule();
// Only lookup this module's symbols from the cache if it is not the
// current module.
if (M == CurrModule)
return false;
// If the module is shadowed by a separately imported overlay(s), look up
// the symbols from the overlay(s) instead.
SmallVector<ModuleDecl *, 1> ShadowingOrOriginal;
if (auto *SF = CurrDeclContext->getParentSourceFile()) {
SF->getSeparatelyImportedOverlays(M, ShadowingOrOriginal);
if (ShadowingOrOriginal.empty())
ShadowingOrOriginal.push_back(M);
}
for (ModuleDecl *M : ShadowingOrOriginal) {
RequestedResultsTy Request =
RequestedResultsTy::fromModule(M, Filter).needLeadingDot(needDot());
RequestedCachedResults.insert(Request);
}
return true;
}
return false;
}
bool CompletionLookup::tryUnwrappedCompletions(Type ExprType, bool isIUO) {
// FIXME: consider types convertible to T?.
ExprType = ExprType->getRValueType();
// FIXME: We don't always pass down whether a type is from an
// unforced IUO.
if (isIUO) {
if (Type Unwrapped = ExprType->getOptionalObjectType()) {
lookupVisibleMemberDecls(*this, Unwrapped, DotLoc, CurrDeclContext,
IncludeInstanceMembers,
/*includeDerivedRequirements*/ false,
/*includeProtocolExtensionMembers*/ true);
return true;
}
assert(IsUnwrappedOptional &&
"IUOs should be optional if not bound/forced");
return false;
}
if (Type Unwrapped = ExprType->getOptionalObjectType()) {
llvm::SaveAndRestore<bool> ChangeNeedOptionalUnwrap(NeedOptionalUnwrap,
true);
if (DotLoc.isValid()) {
// Let's not erase the dot if the completion is after a swift key path
// root because \A?.?.member is the correct way to access wrapped type
// member from an optional key path root.
auto loc = IsAfterSwiftKeyPathRoot ? DotLoc.getAdvancedLoc(1) : DotLoc;
NumBytesToEraseForOptionalUnwrap = Ctx.SourceMgr.getByteDistance(
loc, Ctx.SourceMgr.getIDEInspectionTargetLoc());
} else {
NumBytesToEraseForOptionalUnwrap = 0;
}
if (NumBytesToEraseForOptionalUnwrap <=
CodeCompletionResult::MaxNumBytesToErase) {
// Add '.isolation' to @isolated(any) functions.
tryFunctionIsolationCompletion(Unwrapped);
if (!tryTupleExprCompletions(Unwrapped)) {
lookupVisibleMemberDecls(*this, Unwrapped, DotLoc,
CurrDeclContext,
IncludeInstanceMembers,
/*includeDerivedRequirements*/ false,
/*includeProtocolExtensionMembers*/ true);
}
}
return true;
}
return false;
}
void CompletionLookup::getPostfixKeywordCompletions(Type ExprType,
Expr *ParsedExpr) {
if (IsSuperRefExpr)
return;
NeedLeadingDot = !HaveDot;
if (!ExprType->getAs<ModuleType>()) {
addKeyword(getTokenText(tok::kw_self), ExprType->getRValueType(),
SemanticContextKind::CurrentNominal,
CodeCompletionKeywordKind::kw_self);
}
if (isa<TypeExpr>(ParsedExpr)) {
if (auto *T = ExprType->getAs<AnyMetatypeType>()) {
auto instanceTy = T->getInstanceType();
if (instanceTy->isAnyExistentialType()) {
addKeyword("Protocol", MetatypeType::get(instanceTy),
SemanticContextKind::CurrentNominal);
addKeyword("Type", ExistentialMetatypeType::get(instanceTy),
SemanticContextKind::CurrentNominal);
} else {
addKeyword("Type", MetatypeType::get(instanceTy),
SemanticContextKind::CurrentNominal);
}
}
}
}
void CompletionLookup::getValueExprCompletions(Type ExprType, ValueDecl *VD,
bool IsDeclUnapplied) {
Kind = LookupKind::ValueExpr;
NeedLeadingDot = !HaveDot;
ExprType = ExprType->getRValueType();
assert(!ExprType->hasTypeParameter());
this->ExprType = ExprType;
// Open existential types, so that lookupVisibleMemberDecls() can properly
// substitute them.
bool WasOptional = false;
if (auto OptionalType = ExprType->getOptionalObjectType()) {
ExprType = OptionalType;
WasOptional = true;
}
if (!ExprType->getMetatypeInstanceType()->isAnyObject()) {
if (ExprType->isAnyExistentialType()) {
ExprType = ExistentialArchetypeType::getAny(ExprType->getCanonicalType());
}
}
if (!IsSelfRefExpr && !IsSuperRefExpr && ExprType->getAnyNominal() &&
ExprType->getAnyNominal()->isActor()) {
IsCrossActorReference = true;
}
if (WasOptional)
ExprType = OptionalType::get(ExprType);
// Handle special cases
// Add '.isolation' to @isolated(any) functions.
tryFunctionIsolationCompletion(ExprType);
bool isIUO = VD && VD->isImplicitlyUnwrappedOptional();
if (tryFunctionCallCompletions(ExprType, IsDeclUnapplied ? VD : nullptr))
return;
if (tryModuleCompletions(ExprType, {CodeCompletionFilterFlag::Expr,
CodeCompletionFilterFlag::Type}))
return;
if (tryTupleExprCompletions(ExprType))
return;
// Don't check/return so we still add the members of Optional itself below
tryUnwrappedCompletions(ExprType, isIUO);
lookupVisibleMemberDecls(*this, ExprType, DotLoc, CurrDeclContext,
IncludeInstanceMembers,
/*includeDerivedRequirements*/ false,
/*includeProtocolExtensionMembers*/ true);
}
void CompletionLookup::getStoredPropertyCompletions(const NominalTypeDecl *D) {
Kind = LookupKind::StoredProperty;
NeedLeadingDot = false;
lookupVisibleMemberDecls(*this, D->getDeclaredInterfaceType(),
/*DotLoc=*/SourceLoc(), CurrDeclContext,
/*IncludeInstanceMembers*/ true,
/*includeDerivedRequirements*/ false,
/*includeProtocolExtensionMembers*/ false);
}
void CompletionLookup::collectOperators(
SmallVectorImpl<OperatorDecl *> &results) {
assert(CurrDeclContext);
for (auto import : namelookup::getAllImports(CurrDeclContext))
import.importedModule->getOperatorDecls(results);
}
void CompletionLookup::addPostfixBang(Type resultType) {
CodeCompletionResultBuilder builder = makeResultBuilder(
CodeCompletionResultKind::BuiltinOperator, SemanticContextKind::None);
// FIXME: we can't use the exclamation mark chunk kind, or it isn't
// included in the completion name.
builder.addTextChunk("!");
assert(resultType);
addTypeAnnotation(builder, resultType);
}
void CompletionLookup::addPostfixOperatorCompletion(OperatorDecl *op,
Type resultType) {
// FIXME: we should get the semantic context of the function, not the
// operator decl.
auto semanticContext =
getSemanticContext(op, DeclVisibilityKind::VisibleAtTopLevel, {});
CodeCompletionResultBuilder builder =
makeResultBuilder(CodeCompletionResultKind::Declaration, semanticContext);
// FIXME: handle variable amounts of space.
if (HaveLeadingSpace)
builder.setNumBytesToErase(1);
builder.setAssociatedDecl(op);
builder.addBaseName(op->getName().str());
assert(resultType);
addTypeAnnotation(builder, resultType);
}
void CompletionLookup::addAssignmentOperator(Type RHSType) {
CodeCompletionResultBuilder builder = makeResultBuilder(
CodeCompletionResultKind::BuiltinOperator, SemanticContextKind::None);
if (HaveLeadingSpace)
builder.addAnnotatedWhitespace(" ");
else
builder.addWhitespace(" ");
builder.addEqual();
builder.addWhitespace(" ");
assert(RHSType);
Type contextTy;
if (auto typeContext = CurrDeclContext->getInnermostTypeContext())
contextTy = typeContext->getDeclaredTypeInContext();
builder.addCallArgument(Identifier(), RHSType, contextTy,
/*IsForOperator=*/true);
}
void CompletionLookup::addInfixOperatorCompletion(OperatorDecl *op,
Type resultType,
Type RHSType) {
// FIXME: we should get the semantic context of the function, not the
// operator decl.
auto semanticContext =
getSemanticContext(op, DeclVisibilityKind::VisibleAtTopLevel, {});
CodeCompletionResultBuilder builder =
makeResultBuilder(CodeCompletionResultKind::Declaration, semanticContext);
builder.setAssociatedDecl(op);
if (HaveLeadingSpace)
builder.addAnnotatedWhitespace(" ");
else
builder.addWhitespace(" ");
builder.addBaseName(op->getName().str());
builder.addWhitespace(" ");
if (RHSType) {
Type contextTy;
if (auto typeContext = CurrDeclContext->getInnermostTypeContext())
contextTy = typeContext->getDeclaredTypeInContext();
builder.addCallArgument(Identifier(), RHSType, contextTy,
/*IsForOperator=*/true);
}
if (resultType)
addTypeAnnotation(builder, resultType);
}
void CompletionLookup::addTypeRelationFromProtocol(
CodeCompletionResultBuilder &builder, CodeCompletionLiteralKind kind) {
Type literalType;
// The literal can produce any type that conforms to its ExpressibleBy
// protocol. Figure out as which type we want to show it in code completion.
auto *PD = Ctx.getProtocol(protocolForLiteralKind(kind));
for (auto T : expectedTypeContext.getPossibleTypes()) {
if (!T)
continue;
// Convert through optional types unless we're looking for a protocol
// that Optional itself conforms to.
if (kind != CodeCompletionLiteralKind::NilLiteral) {
if (auto optionalObjT = T->getOptionalObjectType()) {
T = optionalObjT;
}
}
// Check for conformance to the literal protocol.
if (T->getAnyNominal()) {
if (lookupConformance(T, PD)) {
literalType = T;
break;
}
}
}
// Fallback to showing the default type.
if (!literalType) {
literalType = defaultTypeLiteralKind(kind, Ctx);
}
if (literalType) {
addTypeAnnotation(builder, literalType);
builder.setResultTypes(literalType);
builder.setTypeContext(expectedTypeContext, CurrDeclContext);
}
}
void CompletionLookup::addValueLiteralCompletions() {
auto &context = CurrDeclContext->getASTContext();
CodeCompletionFlair flair;
if (isCodeCompletionAtTopLevelOfLibraryFile(CurrDeclContext))
flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope;
auto addFromProto =
[&](CodeCompletionLiteralKind kind,
llvm::function_ref<void(CodeCompletionResultBuilder &)> consumer,
bool isKeyword = false) {
CodeCompletionResultBuilder builder = makeResultBuilder(
CodeCompletionResultKind::Literal, SemanticContextKind::None);
builder.setLiteralKind(kind);
builder.addFlair(flair);
consumer(builder);
addTypeRelationFromProtocol(builder, kind);
};
// FIXME: the pedantically correct way is to resolve Swift.*LiteralType.
using LK = CodeCompletionLiteralKind;
using Builder = CodeCompletionResultBuilder;
// Add literal completions that conform to specific protocols.
addFromProto(LK::IntegerLiteral,
[](Builder &builder) { builder.addTextChunk("0"); });
addFromProto(
LK::BooleanLiteral, [](Builder &builder) { builder.addBaseName("true"); },
/*isKeyword=*/true);
addFromProto(
LK::BooleanLiteral,
[](Builder &builder) { builder.addBaseName("false"); },
/*isKeyword=*/true);
addFromProto(
LK::NilLiteral, [](Builder &builder) { builder.addBaseName("nil"); },
/*isKeyword=*/true);
addFromProto(LK::StringLiteral, [&](Builder &builder) {
builder.addTextChunk("\"");
builder.addSimpleNamedParameter("abc");
builder.addTextChunk("\"");
});
addFromProto(LK::ArrayLiteral, [&](Builder &builder) {
builder.addLeftBracket();
builder.addSimpleNamedParameter("values");
builder.addRightBracket();
});
addFromProto(LK::DictionaryLiteral, [&](Builder &builder) {
builder.addLeftBracket();
builder.addSimpleNamedParameter("key");
builder.addTextChunk(": ");
builder.addSimpleNamedParameter("value");
builder.addRightBracket();
});
// Optionally add object literals.
if (Sink.includeObjectLiterals) {
auto floatType = context.getFloatType();
addFromProto(LK::ColorLiteral, [&](Builder &builder) {
builder.addBaseName("#colorLiteral");
builder.addLeftParen();
builder.addCallArgument(context.getIdentifier("red"), floatType);
builder.addComma();
builder.addCallArgument(context.getIdentifier("green"), floatType);
builder.addComma();
builder.addCallArgument(context.getIdentifier("blue"), floatType);
builder.addComma();
builder.addCallArgument(context.getIdentifier("alpha"), floatType);
builder.addRightParen();
});
auto stringType = context.getStringType();
addFromProto(LK::ImageLiteral, [&](Builder &builder) {
builder.addBaseName("#imageLiteral");
builder.addLeftParen();
builder.addCallArgument(context.getIdentifier("resourceName"),
stringType);
builder.addRightParen();
});
}
// Add tuple completion (item, item).
{
CodeCompletionResultBuilder builder = makeResultBuilder(
CodeCompletionResultKind::Literal, SemanticContextKind::None);
builder.setLiteralKind(LK::Tuple);
builder.addFlair(flair);
builder.addLeftParen();
builder.addSimpleNamedParameter("values");
builder.addRightParen();
for (auto T : expectedTypeContext.getPossibleTypes()) {
if (T && T->is<TupleType>() && !T->isVoid()) {
addTypeAnnotation(builder, T);
builder.setResultTypes(T);
builder.setTypeContext(expectedTypeContext, CurrDeclContext);
break;
}
}
}
}
void CompletionLookup::addObjCPoundKeywordCompletions(bool needPound) {
if (!Ctx.LangOpts.EnableObjCInterop)
return;
// If the expected type is ObjectiveC.Selector, add #selector. If
// it's String, add #keyPath.
bool addedSelector = false;
bool addedKeyPath = false;
for (auto T : expectedTypeContext.getPossibleTypes()) {
T = T->lookThroughAllOptionalTypes();
if (auto structDecl = T->getStructOrBoundGenericStruct()) {
if (!addedSelector && structDecl->getName() == Ctx.Id_Selector &&
structDecl->getParentModule()->getName() == Ctx.Id_ObjectiveC) {
addPoundSelector(needPound);
if (addedKeyPath)
break;
addedSelector = true;
continue;
}
}
if (!addedKeyPath && T->isString()) {
addPoundKeyPath(needPound);
if (addedSelector)
break;
addedKeyPath = true;
continue;
}
}
}
void CompletionLookup::getMacroCompletions(CodeCompletionMacroRoles roles) {
RequestedCachedResults.insert(
RequestedResultsTy::topLevelResults(getCompletionFilter(roles)));
}
void CompletionLookup::getValueCompletionsInDeclContext(SourceLoc Loc,
DeclFilter Filter,
bool LiteralCompletions,
bool ModuleQualifier) {
ExprType = Type();
Kind = LookupKind::ValueInDeclContext;
NeedLeadingDot = false;
AccessFilteringDeclConsumer AccessFilteringConsumer(CurrDeclContext, *this);
FilteredDeclConsumer FilteringConsumer(AccessFilteringConsumer, Filter);
lookupVisibleDecls(FilteringConsumer, Loc, CurrDeclContext,
/*IncludeTopLevel=*/false);
CodeCompletionFilter filter{CodeCompletionFilterFlag::Expr,
CodeCompletionFilterFlag::Type};
if (ModuleQualifier) {
filter |= CodeCompletionFilterFlag::Module;
}
RequestedCachedResults.insert(RequestedResultsTy::topLevelResults(filter));
if (CompletionContext) {
// FIXME: this is an awful simplification that says all and only enums can
// use implicit member syntax (leading dot). Computing the accurate answer
// using lookup (e.g. getUnresolvedMemberCompletions) is too expensive,
// and for some clients this approximation is good enough.
CompletionContext->MayUseImplicitMemberExpr =
llvm::any_of(expectedTypeContext.getPossibleTypes(), [](Type T) {
if (auto *NTD = T->getAnyNominal())
return isa<EnumDecl>(NTD);
return false;
});
}
if (LiteralCompletions) {
addValueLiteralCompletions();
}
addObjCPoundKeywordCompletions(/*needPound=*/true);
}
bool CompletionLookup::isInitializerOnOptional(Type T, ValueDecl *VD) {
bool IsOptionalType = false;
IsOptionalType |= static_cast<bool>(T->getOptionalObjectType());
if (auto *NTD = T->getAnyNominal()) {
IsOptionalType |= NTD->getBaseIdentifier() ==
VD->getASTContext().Id_OptionalNilComparisonType;
}
if (IsOptionalType && VD->getModuleContext()->isStdlibModule() &&
isa<ConstructorDecl>(VD)) {
return true;
} else {
return false;
}
}
void CompletionLookup::getUnresolvedMemberCompletions(Type T) {
if (!T->mayHaveMembers())
return;
NeedLeadingDot = !HaveDot;
if (auto objT = T->getOptionalObjectType()) {
// Add 'nil' keyword with erasing '.' instruction.
unsigned bytesToErase = 0;
auto &SM = CurrDeclContext->getASTContext().SourceMgr;
if (DotLoc.isValid())
bytesToErase = SM.getByteDistance(DotLoc, SM.getIDEInspectionTargetLoc());
addKeyword("nil", T, SemanticContextKind::None,
CodeCompletionKeywordKind::kw_nil, bytesToErase);
}
// We can only say .foo where foo is a static member of the contextual
// type and has the same type (or if the member is a function, then the
// same result type) as the contextual type.
FilteredDeclConsumer consumer(*this,
[=](ValueDecl *VD, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
// In optional context, ignore
// '.init(<some>)', 'init(nilLiteral:)',
return !isInitializerOnOptional(T, VD);
});
auto baseType = MetatypeType::get(T);
llvm::SaveAndRestore<LookupKind> SaveLook(Kind, LookupKind::ValueExpr);
llvm::SaveAndRestore<Type> SaveType(ExprType, baseType);
llvm::SaveAndRestore<bool> SaveUnresolved(IsUnresolvedMember, true);
lookupVisibleMemberDecls(consumer, baseType, DotLoc, CurrDeclContext,
/*includeInstanceMembers=*/false,
/*includeDerivedRequirements*/ false,
/*includeProtocolExtensionMembers*/ true);
}
void CompletionLookup::getEnumElementPatternCompletions(Type T) {
if (!isa_and_nonnull<EnumDecl>(T->getAnyNominal()))
return;
auto baseType = MetatypeType::get(T);
llvm::SaveAndRestore<LookupKind> SaveLook(Kind, LookupKind::EnumElement);
llvm::SaveAndRestore<Type> SaveType(ExprType, baseType);
llvm::SaveAndRestore<bool> SaveUnresolved(IsUnresolvedMember, true);
lookupVisibleMemberDecls(*this, baseType, DotLoc, CurrDeclContext,
/*includeInstanceMembers=*/false,
/*includeDerivedRequirements=*/false,
/*includeProtocolExtensionMembers=*/true);
}
void CompletionLookup::getUnresolvedMemberCompletions(ArrayRef<Type> Types) {
NeedLeadingDot = !HaveDot;
SmallPtrSet<CanType, 4> seenTypes;
for (auto T : Types) {
if (!T || !seenTypes.insert(T->getCanonicalType()).second)
continue;
if (auto objT = T->getOptionalObjectType()) {
// If this is optional type, perform completion for the object type.
// i.e. 'let _: Enum??? = .enumMember' is legal.
objT = objT->lookThroughAllOptionalTypes();
if (seenTypes.insert(objT->getCanonicalType()).second)
getUnresolvedMemberCompletions(objT);
}
getUnresolvedMemberCompletions(T);
}
}
void CompletionLookup::addCallArgumentCompletionResults(
ArrayRef<PossibleParamInfo> ParamInfos, bool isLabeledTrailingClosure) {
Type ContextType;
if (auto typeContext = CurrDeclContext->getInnermostTypeContext())
ContextType = typeContext->getDeclaredTypeInContext();
for (auto Info : ParamInfos) {
const auto *Arg = Info.Param;
if (!Arg)
continue;
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Pattern,
// FIXME: SemanticContextKind::Local is not correct.
// Use 'None' (and fix prioritization) or introduce a new context.
SemanticContextKind::Local);
Builder.addCallArgument(Arg->getLabel(), Identifier(), Arg->getPlainType(),
ContextType, Arg->isVariadic(), Arg->isInOut(),
/*IsIUO=*/false, Arg->isAutoClosure(),
isLabeledTrailingClosure,
/*IsForOperator=*/false,
/*HasDefault=*/false);
Builder.addFlair(CodeCompletionFlairBit::ArgumentLabels);
auto Ty = Arg->getPlainType();
if (Arg->isInOut()) {
Ty = InOutType::get(Ty);
} else if (Arg->isAutoClosure()) {
// 'Ty' may be ErrorType.
if (auto funcTy = Ty->getAs<FunctionType>())
Ty = funcTy->getResult();
}
// The type annotation is the argument type. But the argument label itself
// does not produce an expression with a result type so we set the result
// type as being not applicable.
addTypeAnnotation(Builder, Ty);
Builder.setResultTypeNotApplicable();
}
}
void CompletionLookup::getTypeCompletions(Type BaseType) {
if (tryModuleCompletions(BaseType, CodeCompletionFilterFlag::Type))
return;
Kind = LookupKind::Type;
this->BaseType = BaseType;
NeedLeadingDot = !HaveDot;
lookupVisibleMemberDecls(*this, MetatypeType::get(BaseType), DotLoc,
CurrDeclContext, IncludeInstanceMembers,
/*includeDerivedRequirements*/ false,
/*includeProtocolExtensionMembers*/ false);
if (BaseType->isAnyExistentialType()) {
addKeyword("Protocol", MetatypeType::get(BaseType));
addKeyword("Type", ExistentialMetatypeType::get(BaseType));
} else if (!BaseType->is<ModuleType>()) {
addKeyword("Type", MetatypeType::get(BaseType));
}
}
void CompletionLookup::getInvertedTypeCompletions() {
Kind = LookupKind::Type;
auto addCompletion = [&](InvertibleProtocolKind invertableKind) {
auto *P = Ctx.getProtocol(getKnownProtocolKind(invertableKind));
if (!P)
return;
addNominalTypeRef(P, DeclVisibilityKind::VisibleAtTopLevel,
DynamicLookupInfo());
};
#define INVERTIBLE_PROTOCOL(Name, Bit) \
addCompletion(InvertibleProtocolKind::Name);
#include "swift/ABI/InvertibleProtocols.def"
}
void CompletionLookup::getGenericRequirementCompletions(
DeclContext *DC, SourceLoc CodeCompletionLoc) {
auto genericSig = DC->getGenericSignatureOfContext();
if (!genericSig)
return;
for (auto GPT : genericSig.getGenericParams()) {
addGenericTypeParamRef(GPT->getDecl(), DeclVisibilityKind::GenericParameter,
{});
}
// For non-protocol nominal type decls, only suggest generic parameters.
if (auto D = DC->getAsDecl())
if (isa<NominalTypeDecl>(D) && !isa<ProtocolDecl>(D))
return;
auto typeContext = DC->getInnermostTypeContext();
if (!typeContext)
return;
auto selfTy = typeContext->getSelfTypeInContext();
Kind = LookupKind::GenericRequirement;
this->BaseType = selfTy;
NeedLeadingDot = false;
lookupVisibleMemberDecls(*this, MetatypeType::get(selfTy), DotLoc,
CurrDeclContext, IncludeInstanceMembers,
/*includeDerivedRequirements*/ false,
/*includeProtocolExtensionMembers*/ true);
// We not only allow referencing nested types/typealiases directly, but also
// qualified by the current type, as long as it's a top-level type (nested
// types need to be qualified). Thus also suggest current self type so the
// user can do a memberwise lookup on it.
if (auto *NTD = typeContext->getSelfNominalTypeDecl()) {
if (!NTD->getDeclContext()->isTypeContext()) {
addNominalTypeRef(NTD, DeclVisibilityKind::LocalDecl,
DynamicLookupInfo());
}
}
// Self is also valid in all cases in which it can be used in function
// bodies. Suggest it if applicable.
getSelfTypeCompletionInDeclContext(CodeCompletionLoc,
/*isForResultType=*/false);
}
bool CompletionLookup::canUseAttributeOnDecl(DeclAttrKind DAK, bool IsInSil,
const LangOptions &langOpts,
std::optional<DeclKind> DK,
StringRef Name) {
if (DeclAttribute::isUserInaccessible(DAK))
return false;
if (DeclAttribute::isDeclModifier(DAK))
return false;
if (DeclAttribute::shouldBeRejectedByParser(DAK))
return false;
if (!IsInSil && DeclAttribute::isSilOnly(DAK))
return false;
if (!langOpts.EnableExperimentalConcurrency
&& DeclAttribute::isConcurrencyOnly(DAK))
return false;
if (auto feature = DeclAttribute::getRequiredFeature(DAK))
if (!langOpts.hasFeature(*feature))
return false;
if (!DK.has_value())
return true;
// Hide underscored attributes even if they are not marked as user
// inaccessible. This can happen for attributes that are an underscored
// variant of a user-accessible attribute (like @_backDeployed)
if (Name.empty() || Name[0] == '_')
return false;
return DeclAttribute::canAttributeAppearOnDeclKind(DAK, DK.value());
}
void CompletionLookup::getAttributeDeclCompletions(bool IsInSil,
std::optional<DeclKind> DK) {
// FIXME: also include user-defined attribute keywords
StringRef TargetName = "Declaration";
if (DK.has_value()) {
switch (DK.value()) {
#define DECL(Id, ...) \
case DeclKind::Id: \
TargetName = #Id; \
break;
#include "swift/AST/DeclNodes.def"
}
}
std::string Description = TargetName.str() + " Attribute";
#define DECL_ATTR_ALIAS(KEYWORD, NAME) DECL_ATTR(KEYWORD, NAME, 0, 0)
#define DECL_ATTR(KEYWORD, NAME, ...) \
if (canUseAttributeOnDecl(DeclAttrKind::NAME, IsInSil, Ctx.LangOpts, \
DK, #KEYWORD)) \
addDeclAttrKeyword(#KEYWORD, Description);
#include "swift/AST/DeclAttr.def"
}
void CompletionLookup::getAttributeDeclParamCompletions(
ParameterizedDeclAttributeKind AttrKind, int ParamIndex, bool HasLabel) {
switch (AttrKind) {
case ParameterizedDeclAttributeKind::Unowned:
addDeclAttrParamKeyword("safe", /*Parameters=*/{}, "", false);
addDeclAttrParamKeyword("unsafe", /*Parameters=*/{}, "", false);
break;
case ParameterizedDeclAttributeKind::Nonisolated:
addDeclAttrParamKeyword("unsafe", /*Parameters=*/{}, "", false);
addDeclAttrParamKeyword("nonsending", /*Parameters=*/{}, "", false);
break;
case ParameterizedDeclAttributeKind::AccessControl:
addDeclAttrParamKeyword("set", /*Parameters=*/{}, "", false);
break;
case ParameterizedDeclAttributeKind::Available:
if (ParamIndex == 0) {
addDeclAttrParamKeyword("*", /*Parameters=*/{}, "Platform", false);
#define AVAILABILITY_PLATFORM(X, PrettyName) \
addDeclAttrParamKeyword(swift::platformString(PlatformKind::X), \
/*Parameters=*/{}, "Platform", false);
#include "swift/AST/PlatformKinds.def"
} else {
addDeclAttrParamKeyword("unavailable", /*Parameters=*/{}, "", false);
addDeclAttrParamKeyword("message", /*Parameters=*/{}, "Specify message",
true);
addDeclAttrParamKeyword("renamed", /*Parameters=*/{},
"Specify replacing name", true);
addDeclAttrParamKeyword("introduced", /*Parameters=*/{},
"Specify version number", true);
addDeclAttrParamKeyword("deprecated", /*Parameters=*/{},
"Specify version number", true);
}
break;
case ParameterizedDeclAttributeKind::FreestandingMacro:
case ParameterizedDeclAttributeKind::AttachedMacro:
switch (ParamIndex) {
case 0:
for (auto role : getAllMacroRoles()) {
bool isRoleSupported = isMacroSupported(role, Ctx);
if (AttrKind == ParameterizedDeclAttributeKind::FreestandingMacro) {
isRoleSupported &= isFreestandingMacro(role);
} else if (AttrKind == ParameterizedDeclAttributeKind::AttachedMacro) {
isRoleSupported &= isAttachedMacro(role);
}
if (isRoleSupported) {
addDeclAttrParamKeyword(getMacroRoleString(role), /*Parameters=*/{},
/*Annotation=*/"", /*NeedsSpecify=*/false);
}
}
break;
case 1:
if (HasLabel) {
for (auto kind : getAllMacroIntroducedDeclNameKinds()) {
auto name = getMacroIntroducedDeclNameString(kind);
SmallVector<StringRef, 1> Parameters;
if (macroIntroducedNameRequiresArgument(kind)) {
Parameters = {"name"};
}
addDeclAttrParamKeyword(name, Parameters, /*Annotation=*/"",
/*NeedsSpecify=*/false);
}
} else {
addDeclAttrParamKeyword("names", /*Parameters=*/{},
"Specify declared names",
/*NeedsSpecify=*/true);
}
break;
}
break;
case ParameterizedDeclAttributeKind::StorageRestrictions: {
bool suggestInitializesLabel = false;
bool suggestAccessesLabel = false;
bool suggestArgument = false;
switch (static_cast<StorageRestrictionsCompletionKind>(ParamIndex)) {
case StorageRestrictionsCompletionKind::Label:
suggestAccessesLabel = true;
suggestInitializesLabel = true;
break;
case StorageRestrictionsCompletionKind::Argument:
suggestArgument = true;
break;
case StorageRestrictionsCompletionKind::ArgumentOrInitializesLabel:
suggestArgument = true;
suggestInitializesLabel = true;
break;
case StorageRestrictionsCompletionKind::ArgumentOrAccessesLabel:
suggestArgument = true;
suggestAccessesLabel = true;
break;
}
if (suggestInitializesLabel) {
addDeclAttrParamKeyword(
"initializes", /*Parameters=*/{},
"Specify stored properties initialized by the accessor", true);
}
if (suggestAccessesLabel) {
addDeclAttrParamKeyword(
"accesses", /*Parameters=*/{},
"Specify stored properties accessed by the accessor", true);
}
if (suggestArgument) {
if (auto NT = dyn_cast<NominalTypeDecl>(CurrDeclContext)) {
getStoredPropertyCompletions(NT);
}
}
}
}
}
void CompletionLookup::getTypeAttributeKeywordCompletions(
CompletionKind completionKind) {
auto addTypeAttr = [&](TypeAttrKind Kind, StringRef Name) {
if (completionKind != CompletionKind::TypeAttrInheritanceBeginning) {
switch (Kind) {
case TypeAttrKind::Retroactive:
case TypeAttrKind::Preconcurrency:
case TypeAttrKind::Nonisolated:
case TypeAttrKind::Unchecked:
case TypeAttrKind::Unsafe:
// These attributes are only available in inheritance clasuses.
return;
default:
break;
}
}
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Keyword, SemanticContextKind::None);
Builder.addAttributeKeyword(Name, "Type Attribute");
};
// Add simple user-accessible attributes.
#define SIL_TYPE_ATTR(SPELLING, C)
#define SIMPLE_SIL_TYPE_ATTR(SPELLING, C)
#define SIMPLE_TYPE_ATTR(SPELLING, C) \
if (!TypeAttribute::isUserInaccessible(TypeAttrKind::C)) \
addTypeAttr(TypeAttrKind::C, #SPELLING);
#include "swift/AST/TypeAttr.def"
// Add non-simple cases.
addTypeAttr(TypeAttrKind::Convention, "convention(swift)");
addTypeAttr(TypeAttrKind::Convention, "convention(block)");
addTypeAttr(TypeAttrKind::Convention, "convention(c)");
addTypeAttr(TypeAttrKind::Convention, "convention(thin)");
addTypeAttr(TypeAttrKind::Isolated, "isolated(any)");
}
void CompletionLookup::collectPrecedenceGroups() {
assert(CurrDeclContext);
if (CurrModule) {
for (auto FU : CurrModule->getFiles()) {
// We are looking through the current module,
// inspect only source files.
if (FU->getKind() != FileUnitKind::Source)
continue;
llvm::SmallVector<PrecedenceGroupDecl *, 4> results;
cast<SourceFile>(FU)->getPrecedenceGroups(results);
for (auto PG : results)
addPrecedenceGroupRef(PG);
}
}
for (auto Import : namelookup::getAllImports(CurrDeclContext)) {
auto Module = Import.importedModule;
if (Module == CurrModule)
continue;
RequestedCachedResults.insert(RequestedResultsTy::fromModule(
Module, CodeCompletionFilterFlag::PrecedenceGroup));
}
}
void CompletionLookup::getPrecedenceGroupCompletions(
CodeCompletionCallbacks::PrecedenceGroupCompletionKind SK) {
switch (SK) {
case CodeCompletionCallbacks::PrecedenceGroupCompletionKind::Associativity:
addKeyword(getAssociativitySpelling(Associativity::None));
addKeyword(getAssociativitySpelling(Associativity::Left));
addKeyword(getAssociativitySpelling(Associativity::Right));
return;
case CodeCompletionCallbacks::PrecedenceGroupCompletionKind::Assignment:
addKeyword(getTokenText(tok::kw_false), Type(), SemanticContextKind::None,
CodeCompletionKeywordKind::kw_false);
addKeyword(getTokenText(tok::kw_true), Type(), SemanticContextKind::None,
CodeCompletionKeywordKind::kw_true);
return;
case CodeCompletionCallbacks::PrecedenceGroupCompletionKind::AttributeList:
addKeyword("associativity");
addKeyword("higherThan");
addKeyword("lowerThan");
addKeyword("assignment");
return;
case CodeCompletionCallbacks::PrecedenceGroupCompletionKind::Relation:
collectPrecedenceGroups();
return;
}
llvm_unreachable("not a precedencegroup SyntaxKind");
}
void CompletionLookup::getPoundAvailablePlatformCompletions() {
// The platform names should be identical to those in @available.
getAttributeDeclParamCompletions(ParameterizedDeclAttributeKind::Available, 0,
/*HasLabel=*/false);
}
void CompletionLookup::getSelfTypeCompletionInDeclContext(
SourceLoc Loc, bool isForDeclResult) {
const DeclContext *typeDC = CurrDeclContext->getInnermostTypeContext();
if (!typeDC)
return;
// For protocols, there's a 'Self' generic parameter.
if (typeDC->getSelfProtocolDecl())
return;
Type selfType =
CurrDeclContext->mapTypeIntoContext(typeDC->getSelfInterfaceType());
if (typeDC->getSelfClassDecl()) {
// In classes, 'Self' can be used in result type of func, subscript and
// computed property, or inside function bodies.
bool canUseDynamicSelf = false;
if (isForDeclResult) {
canUseDynamicSelf = true;
} else {
const auto *checkDC = CurrDeclContext;
if (isa<TypeAliasDecl>(checkDC))
checkDC = checkDC->getParent();
if (const auto *AFD = checkDC->getInnermostMethodContext()) {
canUseDynamicSelf =
Ctx.SourceMgr.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc);
}
}
if (!canUseDynamicSelf)
return;
// 'Self' in class is a dynamic type.
selfType = DynamicSelfType::get(selfType, Ctx);
} else {
// In enums and structs, 'Self' is just an alias for the nominal type,
// and can be used anywhere.
}
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Keyword, SemanticContextKind::CurrentNominal);
Builder.addKeyword("Self");
Builder.setKeywordKind(CodeCompletionKeywordKind::kw_Self);
addTypeAnnotation(Builder, selfType);
}
void CompletionLookup::getTypeCompletionsInDeclContext(SourceLoc Loc,
bool ModuleQualifier) {
Kind = LookupKind::TypeInDeclContext;
AccessFilteringDeclConsumer AccessFilteringConsumer(CurrDeclContext, *this);
lookupVisibleDecls(AccessFilteringConsumer, Loc, CurrDeclContext,
/*IncludeTopLevel=*/false);
CodeCompletionFilter filter{CodeCompletionFilterFlag::Type};
if (ModuleQualifier) {
filter |= CodeCompletionFilterFlag::Module;
}
RequestedCachedResults.insert(RequestedResultsTy::topLevelResults(filter));
}
namespace {
/// A \c VisibleDeclConsumer that stores all decls that are found and is able
/// to forward the to another \c VisibleDeclConsumer later.
class StoringDeclConsumer : public VisibleDeclConsumer {
struct FoundDecl {
ValueDecl *VD;
DeclVisibilityKind Reason;
DynamicLookupInfo DynamicLookupInfo;
};
std::vector<FoundDecl> FoundDecls;
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
DynamicLookupInfo DynamicLookupInfo = {}) override {
FoundDecls.push_back({VD, Reason, DynamicLookupInfo});
}
public:
/// Call \c foundDecl for every declaration that this consumer has found.
void forward(VisibleDeclConsumer &Consumer) {
for (auto &FoundDecl : FoundDecls) {
Consumer.foundDecl(FoundDecl.VD, FoundDecl.Reason,
FoundDecl.DynamicLookupInfo);
}
}
};
} // namespace
void CompletionLookup::getToplevelCompletions(CodeCompletionFilter Filter) {
Kind = (Filter - CodeCompletionFilterFlag::Module)
.containsOnly(CodeCompletionFilterFlag::Type)
? LookupKind::TypeInDeclContext
: LookupKind::ValueInDeclContext;
NeedLeadingDot = false;
// If we have 'addinitstotoplevel' enabled, calling `foundDecl` on `this`
// can cause macros to get expanded, which can then cause new members ot get
// added to 'TopLevelValues', invalidating iterator over `TopLevelDecls` in
// `SourceLookupCache::lookupVisibleDecls`.
//
// Technically `foundDecl` should not expand macros or discover new top level
// members in any way because those newly discovered decls will not be added
// to the code completion results. However, it's preferrable to miss results
// than to silently invalidate a collection, resulting in hard-to-diagnose
// crashes.
// Thus, store all the decls found by `CurrModule->lookupVisibleDecls` in a
// vector first and only call `this->foundDecl` once we have left the
// iteration loop over `TopLevelDecls`.
StoringDeclConsumer StoringConsumer;
UsableFilteringDeclConsumer UsableFilteringConsumer(
Ctx.SourceMgr, CurrDeclContext, Ctx.SourceMgr.getIDEInspectionTargetLoc(),
StoringConsumer);
AccessFilteringDeclConsumer AccessFilteringConsumer(CurrDeclContext,
UsableFilteringConsumer);
CodeCompletionMacroRoles ExpectedRoles = getCompletionMacroRoles(Filter);
DeclFilter VisibleFilter =
[ExpectedRoles](ValueDecl *VD, DeclVisibilityKind Kind,
DynamicLookupInfo DynamicLookupInfo) {
CodeCompletionMacroRoles Roles = getCompletionMacroRoles(VD);
if (!ExpectedRoles)
return !Roles;
return (bool)(Roles & ExpectedRoles);
};
FilteredDeclConsumer FilteringConsumer(AccessFilteringConsumer,
VisibleFilter);
CurrModule->lookupVisibleDecls({}, FilteringConsumer,
NLKind::UnqualifiedLookup);
StoringConsumer.forward(*this);
}
void CompletionLookup::lookupExternalModuleDecls(
const ModuleDecl *TheModule, ArrayRef<std::string> AccessPath,
bool ResultsHaveLeadingDot) {
assert(CurrModule != TheModule && "requested module should be external");
Kind = LookupKind::ImportFromModule;
NeedLeadingDot = ResultsHaveLeadingDot;
ImportPath::Access::Builder builder;
for (auto Piece : AccessPath) {
builder.push_back(Ctx.getIdentifier(Piece));
}
AccessFilteringDeclConsumer FilteringConsumer(CurrDeclContext, *this);
TheModule->lookupVisibleDecls(builder.get(), FilteringConsumer,
NLKind::UnqualifiedLookup);
llvm::SmallVector<PrecedenceGroupDecl *, 16> precedenceGroups;
TheModule->getPrecedenceGroups(precedenceGroups);
for (auto PGD : precedenceGroups)
addPrecedenceGroupRef(PGD);
}
void CompletionLookup::getStmtLabelCompletions(SourceLoc Loc, bool isContinue) {
auto *SF = CurrDeclContext->getParentSourceFile();
llvm::SmallPtrSet<Identifier, 4> labels;
for (auto *LS : ASTScope::lookupLabeledStmts(SF, Loc)) {
if (isContinue && !LS->isPossibleContinueTarget())
continue;
auto labelInfo = LS->getLabelInfo();
if (!labelInfo)
continue;
auto label = labelInfo.Name;
if (!labels.insert(label).second)
continue;
CodeCompletionResultBuilder Builder = makeResultBuilder(
CodeCompletionResultKind::Pattern, SemanticContextKind::Local);
Builder.addTextChunk(label.str());
}
}
void CompletionLookup::getOptionalBindingCompletions(SourceLoc Loc) {
ExprType = Type();
Kind = LookupKind::ValueInDeclContext;
NeedLeadingDot = false;
AccessFilteringDeclConsumer AccessFilteringConsumer(CurrDeclContext, *this);
// Suggest only 'Optional' type var decls (incl. parameters)
FilteredDeclConsumer FilteringConsumer(
AccessFilteringConsumer,
[&](ValueDecl *VD, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) -> bool {
auto *VarD = dyn_cast<VarDecl>(VD);
if (!VarD)
return false;
auto Ty = getTypeOfMember(VD, dynamicLookupInfo);
return Ty->isOptional();
});
// FIXME: Currently, it doesn't include top level decls for performance
// reason. Enabling 'IncludeTopLevel' pulls everything including imported
// modules. For suggesting top level results, we need a way to filter cached
// results.
lookupVisibleDecls(FilteringConsumer, Loc, CurrDeclContext,
/*IncludeTopLevel=*/false);
}