mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
3588 lines
129 KiB
C++
3588 lines
129 KiB
C++
//===--- 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 the provided type has a single argument (not including defaulted
|
||
/// arguments) that is of type () -> ().
|
||
static bool hasTrivialTrailingClosure(const ValueDecl *VD, Type type) {
|
||
if (!VD->hasParameterList())
|
||
return false;
|
||
|
||
auto *funcType = type->getAs<AnyFunctionType>();
|
||
if (!funcType)
|
||
return false;
|
||
|
||
ParameterListInfo paramInfo(funcType->getParams(), VD,
|
||
/*skipCurriedSelf*/ VD->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);
|
||
}
|
||
}
|
||
|
||
void CompletionLookup::addUsingSpecifiers() {
|
||
for (unsigned i = 0,
|
||
n = static_cast<unsigned>(UsingSpecifier::LastSpecifier) + 1;
|
||
i != n; ++i) {
|
||
CodeCompletionResultBuilder Builder = makeResultBuilder(
|
||
CodeCompletionResultKind::Keyword, SemanticContextKind::None);
|
||
switch (static_cast<UsingSpecifier>(i)) {
|
||
case UsingSpecifier::MainActor:
|
||
Builder.addTextChunk("@MainActor");
|
||
break;
|
||
case UsingSpecifier::Nonisolated:
|
||
Builder.addTextChunk("nonisolated");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
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 ¶m : 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::addMacroCallArguments(const MacroDecl *MD,
|
||
DeclVisibilityKind Reason,
|
||
bool forTrivialTrailingClosure) {
|
||
CodeCompletionResultBuilder Builder =
|
||
makeResultBuilder(CodeCompletionResultKind::Declaration,
|
||
getSemanticContext(MD, Reason, DynamicLookupInfo()));
|
||
Builder.setAssociatedDecl(MD);
|
||
|
||
addValueBaseName(Builder, MD->getBaseIdentifier());
|
||
|
||
if (forTrivialTrailingClosure) {
|
||
Builder.addBraceStmtWithCursor(" { code }");
|
||
} else if (MD->parameterList && MD->parameterList->size() > 0) {
|
||
auto *macroTy = MD->getInterfaceType()->castTo<AnyFunctionType>();
|
||
Builder.addLeftParen();
|
||
addCallArgumentPatterns(Builder, macroTy, 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::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;
|
||
}
|
||
|
||
if (hasTrivialTrailingClosure(MD, MD->getInterfaceType()))
|
||
addMacroCallArguments(MD, Reason, /*forTrivialTrailingClosure*/ true);
|
||
|
||
addMacroCallArguments(MD, Reason);
|
||
}
|
||
|
||
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::InheritActorContext:
|
||
addDeclAttrParamKeyword("always", /*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);
|
||
}
|