mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
3736 lines
136 KiB
C++
3736 lines
136 KiB
C++
//===--- CodeCompletion.cpp - Code completion implementation --------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 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/CodeCompletion.h"
|
|
#include "CodeCompletionDiagnostics.h"
|
|
#include "CodeCompletionResultBuilder.h"
|
|
#include "ExprContextAnalysis.h"
|
|
#include "swift/AST/ASTPrinter.h"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/Comment.h"
|
|
#include "swift/AST/GenericSignature.h"
|
|
#include "swift/AST/ImportCache.h"
|
|
#include "swift/AST/Initializer.h"
|
|
#include "swift/AST/LazyResolver.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/ProtocolConformance.h"
|
|
#include "swift/AST/SourceFile.h"
|
|
#include "swift/AST/SubstitutionMap.h"
|
|
#include "swift/AST/USRGeneration.h"
|
|
#include "swift/Basic/Defer.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/ClangImporter/ClangImporter.h"
|
|
#include "swift/ClangImporter/ClangModule.h"
|
|
#include "swift/Frontend/FrontendOptions.h"
|
|
#include "swift/IDE/CodeCompletionStringPrinter.h"
|
|
#include "swift/IDE/CompletionLookup.h"
|
|
#include "swift/IDE/CodeCompletionCache.h"
|
|
#include "swift/IDE/CodeCompletionResultPrinter.h"
|
|
#include "swift/IDE/Utils.h"
|
|
#include "swift/Parse/CodeCompletionCallbacks.h"
|
|
#include "swift/Sema/CodeCompletionTypeChecking.h"
|
|
#include "swift/Sema/IDETypeChecking.h"
|
|
#include "swift/Strings.h"
|
|
#include "swift/Subsystems.h"
|
|
#include "swift/Syntax/SyntaxKind.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Attr.h"
|
|
#include "clang/AST/Comment.h"
|
|
#include "clang/AST/CommentVisitor.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/Basic/Module.h"
|
|
#include "clang/Index/USRGeneration.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <algorithm>
|
|
#include <string>
|
|
|
|
using namespace swift;
|
|
using namespace ide;
|
|
|
|
std::string swift::ide::removeCodeCompletionTokens(
|
|
StringRef Input, StringRef TokenName, unsigned *CompletionOffset) {
|
|
assert(TokenName.size() >= 1);
|
|
|
|
*CompletionOffset = ~0U;
|
|
|
|
std::string CleanFile;
|
|
CleanFile.reserve(Input.size());
|
|
const std::string Token = std::string("#^") + TokenName.str() + "^#";
|
|
|
|
for (const char *Ptr = Input.begin(), *End = Input.end();
|
|
Ptr != End; ++Ptr) {
|
|
const char C = *Ptr;
|
|
if (C == '#' && Ptr <= End - Token.size() &&
|
|
StringRef(Ptr, Token.size()) == Token) {
|
|
Ptr += Token.size() - 1;
|
|
*CompletionOffset = CleanFile.size();
|
|
CleanFile += '\0';
|
|
continue;
|
|
}
|
|
if (C == '#' && Ptr <= End - 2 && Ptr[1] == '^') {
|
|
do {
|
|
++Ptr;
|
|
} while (Ptr < End && *Ptr != '#');
|
|
if (Ptr == End)
|
|
break;
|
|
continue;
|
|
}
|
|
CleanFile += C;
|
|
}
|
|
return CleanFile;
|
|
}
|
|
|
|
ContextFreeCodeCompletionResult *
|
|
ContextFreeCodeCompletionResult::createPatternOrBuiltInOperatorResult(
|
|
llvm::BumpPtrAllocator &Allocator, CodeCompletionResultKind Kind,
|
|
CodeCompletionString *CompletionString,
|
|
CodeCompletionOperatorKind KnownOperatorKind,
|
|
NullTerminatedStringRef BriefDocComment,
|
|
CodeCompletionResultType ResultType,
|
|
ContextFreeNotRecommendedReason NotRecommended,
|
|
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
|
|
NullTerminatedStringRef DiagnosticMessage) {
|
|
return new (Allocator) ContextFreeCodeCompletionResult(
|
|
Kind, /*AssociatedKind=*/0, KnownOperatorKind,
|
|
/*IsSystem=*/false, CompletionString, /*ModuleName=*/"", BriefDocComment,
|
|
/*AssociatedUSRs=*/{}, ResultType, NotRecommended, DiagnosticSeverity,
|
|
DiagnosticMessage,
|
|
getCodeCompletionResultFilterName(CompletionString, Allocator));
|
|
}
|
|
|
|
ContextFreeCodeCompletionResult *
|
|
ContextFreeCodeCompletionResult::createKeywordResult(
|
|
llvm::BumpPtrAllocator &Allocator, CodeCompletionKeywordKind Kind,
|
|
CodeCompletionString *CompletionString,
|
|
NullTerminatedStringRef BriefDocComment,
|
|
CodeCompletionResultType ResultType) {
|
|
return new (Allocator) ContextFreeCodeCompletionResult(
|
|
CodeCompletionResultKind::Keyword, static_cast<uint8_t>(Kind),
|
|
CodeCompletionOperatorKind::None, /*IsSystem=*/false, CompletionString,
|
|
/*ModuleName=*/"", BriefDocComment,
|
|
/*AssociatedUSRs=*/{}, ResultType, ContextFreeNotRecommendedReason::None,
|
|
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"",
|
|
getCodeCompletionResultFilterName(CompletionString, Allocator));
|
|
}
|
|
|
|
ContextFreeCodeCompletionResult *
|
|
ContextFreeCodeCompletionResult::createLiteralResult(
|
|
llvm::BumpPtrAllocator &Allocator, CodeCompletionLiteralKind LiteralKind,
|
|
CodeCompletionString *CompletionString,
|
|
CodeCompletionResultType ResultType) {
|
|
return new (Allocator) ContextFreeCodeCompletionResult(
|
|
CodeCompletionResultKind::Literal, static_cast<uint8_t>(LiteralKind),
|
|
CodeCompletionOperatorKind::None,
|
|
/*IsSystem=*/false, CompletionString, /*ModuleName=*/"",
|
|
/*BriefDocComment=*/"",
|
|
/*AssociatedUSRs=*/{}, ResultType, ContextFreeNotRecommendedReason::None,
|
|
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"",
|
|
getCodeCompletionResultFilterName(CompletionString, Allocator));
|
|
}
|
|
|
|
ContextFreeCodeCompletionResult *
|
|
ContextFreeCodeCompletionResult::createDeclResult(
|
|
llvm::BumpPtrAllocator &Allocator, CodeCompletionString *CompletionString,
|
|
const Decl *AssociatedDecl, NullTerminatedStringRef ModuleName,
|
|
NullTerminatedStringRef BriefDocComment,
|
|
ArrayRef<NullTerminatedStringRef> AssociatedUSRs,
|
|
CodeCompletionResultType ResultType,
|
|
ContextFreeNotRecommendedReason NotRecommended,
|
|
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
|
|
NullTerminatedStringRef DiagnosticMessage) {
|
|
assert(AssociatedDecl && "should have a decl");
|
|
return new (Allocator) ContextFreeCodeCompletionResult(
|
|
CodeCompletionResultKind::Declaration,
|
|
static_cast<uint8_t>(getCodeCompletionDeclKind(AssociatedDecl)),
|
|
CodeCompletionOperatorKind::None, getDeclIsSystem(AssociatedDecl),
|
|
CompletionString, ModuleName, BriefDocComment, AssociatedUSRs, ResultType,
|
|
NotRecommended, DiagnosticSeverity, DiagnosticMessage,
|
|
getCodeCompletionResultFilterName(CompletionString, Allocator));
|
|
}
|
|
|
|
CodeCompletionDeclKind
|
|
ContextFreeCodeCompletionResult::getCodeCompletionDeclKind(const Decl *D) {
|
|
switch (D->getKind()) {
|
|
case DeclKind::Import:
|
|
case DeclKind::Extension:
|
|
case DeclKind::PatternBinding:
|
|
case DeclKind::EnumCase:
|
|
case DeclKind::TopLevelCode:
|
|
case DeclKind::IfConfig:
|
|
case DeclKind::PoundDiagnostic:
|
|
case DeclKind::MissingMember:
|
|
case DeclKind::OpaqueType:
|
|
llvm_unreachable("not expecting such a declaration result");
|
|
case DeclKind::Module:
|
|
return CodeCompletionDeclKind::Module;
|
|
case DeclKind::TypeAlias:
|
|
return CodeCompletionDeclKind::TypeAlias;
|
|
case DeclKind::AssociatedType:
|
|
return CodeCompletionDeclKind::AssociatedType;
|
|
case DeclKind::GenericTypeParam:
|
|
return CodeCompletionDeclKind::GenericTypeParam;
|
|
case DeclKind::Enum:
|
|
return CodeCompletionDeclKind::Enum;
|
|
case DeclKind::Struct:
|
|
return CodeCompletionDeclKind::Struct;
|
|
case DeclKind::Class:
|
|
if (cast<ClassDecl>(D)->isActor()) {
|
|
return CodeCompletionDeclKind::Actor;
|
|
} else {
|
|
return CodeCompletionDeclKind::Class;
|
|
}
|
|
case DeclKind::Protocol:
|
|
return CodeCompletionDeclKind::Protocol;
|
|
case DeclKind::Var:
|
|
case DeclKind::Param: {
|
|
auto DC = D->getDeclContext();
|
|
if (DC->isTypeContext()) {
|
|
if (cast<VarDecl>(D)->isStatic())
|
|
return CodeCompletionDeclKind::StaticVar;
|
|
else
|
|
return CodeCompletionDeclKind::InstanceVar;
|
|
}
|
|
if (DC->isLocalContext())
|
|
return CodeCompletionDeclKind::LocalVar;
|
|
return CodeCompletionDeclKind::GlobalVar;
|
|
}
|
|
case DeclKind::Constructor:
|
|
return CodeCompletionDeclKind::Constructor;
|
|
case DeclKind::Destructor:
|
|
return CodeCompletionDeclKind::Destructor;
|
|
case DeclKind::Accessor:
|
|
case DeclKind::Func: {
|
|
auto DC = D->getDeclContext();
|
|
auto FD = cast<FuncDecl>(D);
|
|
if (DC->isTypeContext()) {
|
|
if (FD->isStatic())
|
|
return CodeCompletionDeclKind::StaticMethod;
|
|
return CodeCompletionDeclKind::InstanceMethod;
|
|
}
|
|
if (FD->isOperator()) {
|
|
if (auto op = FD->getOperatorDecl()) {
|
|
switch (op->getKind()) {
|
|
case DeclKind::PrefixOperator:
|
|
return CodeCompletionDeclKind::PrefixOperatorFunction;
|
|
case DeclKind::PostfixOperator:
|
|
return CodeCompletionDeclKind::PostfixOperatorFunction;
|
|
case DeclKind::InfixOperator:
|
|
return CodeCompletionDeclKind::InfixOperatorFunction;
|
|
default:
|
|
llvm_unreachable("unexpected operator kind");
|
|
}
|
|
} else {
|
|
return CodeCompletionDeclKind::InfixOperatorFunction;
|
|
}
|
|
}
|
|
return CodeCompletionDeclKind::FreeFunction;
|
|
}
|
|
case DeclKind::InfixOperator:
|
|
return CodeCompletionDeclKind::InfixOperatorFunction;
|
|
case DeclKind::PrefixOperator:
|
|
return CodeCompletionDeclKind::PrefixOperatorFunction;
|
|
case DeclKind::PostfixOperator:
|
|
return CodeCompletionDeclKind::PostfixOperatorFunction;
|
|
case DeclKind::PrecedenceGroup:
|
|
return CodeCompletionDeclKind::PrecedenceGroup;
|
|
case DeclKind::EnumElement:
|
|
return CodeCompletionDeclKind::EnumElement;
|
|
case DeclKind::Subscript:
|
|
return CodeCompletionDeclKind::Subscript;
|
|
}
|
|
llvm_unreachable("invalid DeclKind");
|
|
}
|
|
|
|
bool ContextFreeCodeCompletionResult::getDeclIsSystem(const Decl *D) {
|
|
return D->getModuleContext()->isSystemModule();
|
|
}
|
|
|
|
void CodeCompletionResult::printPrefix(raw_ostream &OS) const {
|
|
llvm::SmallString<64> Prefix;
|
|
switch (getKind()) {
|
|
case CodeCompletionResultKind::Declaration:
|
|
Prefix.append("Decl");
|
|
switch (getAssociatedDeclKind()) {
|
|
case CodeCompletionDeclKind::Class:
|
|
Prefix.append("[Class]");
|
|
break;
|
|
case CodeCompletionDeclKind::Actor:
|
|
Prefix.append("[Actor]");
|
|
break;
|
|
case CodeCompletionDeclKind::Struct:
|
|
Prefix.append("[Struct]");
|
|
break;
|
|
case CodeCompletionDeclKind::Enum:
|
|
Prefix.append("[Enum]");
|
|
break;
|
|
case CodeCompletionDeclKind::EnumElement:
|
|
Prefix.append("[EnumElement]");
|
|
break;
|
|
case CodeCompletionDeclKind::Protocol:
|
|
Prefix.append("[Protocol]");
|
|
break;
|
|
case CodeCompletionDeclKind::TypeAlias:
|
|
Prefix.append("[TypeAlias]");
|
|
break;
|
|
case CodeCompletionDeclKind::AssociatedType:
|
|
Prefix.append("[AssociatedType]");
|
|
break;
|
|
case CodeCompletionDeclKind::GenericTypeParam:
|
|
Prefix.append("[GenericTypeParam]");
|
|
break;
|
|
case CodeCompletionDeclKind::Constructor:
|
|
Prefix.append("[Constructor]");
|
|
break;
|
|
case CodeCompletionDeclKind::Destructor:
|
|
Prefix.append("[Destructor]");
|
|
break;
|
|
case CodeCompletionDeclKind::Subscript:
|
|
Prefix.append("[Subscript]");
|
|
break;
|
|
case CodeCompletionDeclKind::StaticMethod:
|
|
Prefix.append("[StaticMethod]");
|
|
break;
|
|
case CodeCompletionDeclKind::InstanceMethod:
|
|
Prefix.append("[InstanceMethod]");
|
|
break;
|
|
case CodeCompletionDeclKind::PrefixOperatorFunction:
|
|
Prefix.append("[PrefixOperatorFunction]");
|
|
break;
|
|
case CodeCompletionDeclKind::PostfixOperatorFunction:
|
|
Prefix.append("[PostfixOperatorFunction]");
|
|
break;
|
|
case CodeCompletionDeclKind::InfixOperatorFunction:
|
|
Prefix.append("[InfixOperatorFunction]");
|
|
break;
|
|
case CodeCompletionDeclKind::FreeFunction:
|
|
Prefix.append("[FreeFunction]");
|
|
break;
|
|
case CodeCompletionDeclKind::StaticVar:
|
|
Prefix.append("[StaticVar]");
|
|
break;
|
|
case CodeCompletionDeclKind::InstanceVar:
|
|
Prefix.append("[InstanceVar]");
|
|
break;
|
|
case CodeCompletionDeclKind::LocalVar:
|
|
Prefix.append("[LocalVar]");
|
|
break;
|
|
case CodeCompletionDeclKind::GlobalVar:
|
|
Prefix.append("[GlobalVar]");
|
|
break;
|
|
case CodeCompletionDeclKind::Module:
|
|
Prefix.append("[Module]");
|
|
break;
|
|
case CodeCompletionDeclKind::PrecedenceGroup:
|
|
Prefix.append("[PrecedenceGroup]");
|
|
break;
|
|
}
|
|
break;
|
|
case CodeCompletionResultKind::Keyword:
|
|
Prefix.append("Keyword");
|
|
switch (getKeywordKind()) {
|
|
case CodeCompletionKeywordKind::None:
|
|
break;
|
|
#define KEYWORD(X) case CodeCompletionKeywordKind::kw_##X: \
|
|
Prefix.append("[" #X "]"); \
|
|
break;
|
|
#define POUND_KEYWORD(X) case CodeCompletionKeywordKind::pound_##X: \
|
|
Prefix.append("[#" #X "]"); \
|
|
break;
|
|
#include "swift/Syntax/TokenKinds.def"
|
|
}
|
|
break;
|
|
case CodeCompletionResultKind::Pattern:
|
|
Prefix.append("Pattern");
|
|
break;
|
|
case CodeCompletionResultKind::Literal:
|
|
Prefix.append("Literal");
|
|
switch (getLiteralKind()) {
|
|
case CodeCompletionLiteralKind::ArrayLiteral:
|
|
Prefix.append("[Array]");
|
|
break;
|
|
case CodeCompletionLiteralKind::BooleanLiteral:
|
|
Prefix.append("[Boolean]");
|
|
break;
|
|
case CodeCompletionLiteralKind::ColorLiteral:
|
|
Prefix.append("[_Color]");
|
|
break;
|
|
case CodeCompletionLiteralKind::ImageLiteral:
|
|
Prefix.append("[_Image]");
|
|
break;
|
|
case CodeCompletionLiteralKind::DictionaryLiteral:
|
|
Prefix.append("[Dictionary]");
|
|
break;
|
|
case CodeCompletionLiteralKind::IntegerLiteral:
|
|
Prefix.append("[Integer]");
|
|
break;
|
|
case CodeCompletionLiteralKind::NilLiteral:
|
|
Prefix.append("[Nil]");
|
|
break;
|
|
case CodeCompletionLiteralKind::StringLiteral:
|
|
Prefix.append("[String]");
|
|
break;
|
|
case CodeCompletionLiteralKind::Tuple:
|
|
Prefix.append("[Tuple]");
|
|
break;
|
|
}
|
|
break;
|
|
case CodeCompletionResultKind::BuiltinOperator:
|
|
Prefix.append("BuiltinOperator");
|
|
break;
|
|
}
|
|
Prefix.append("/");
|
|
switch (getSemanticContext()) {
|
|
case SemanticContextKind::None:
|
|
Prefix.append("None");
|
|
break;
|
|
case SemanticContextKind::Local:
|
|
Prefix.append("Local");
|
|
break;
|
|
case SemanticContextKind::CurrentNominal:
|
|
Prefix.append("CurrNominal");
|
|
break;
|
|
case SemanticContextKind::Super:
|
|
Prefix.append("Super");
|
|
break;
|
|
case SemanticContextKind::OutsideNominal:
|
|
Prefix.append("OutNominal");
|
|
break;
|
|
case SemanticContextKind::CurrentModule:
|
|
Prefix.append("CurrModule");
|
|
break;
|
|
case SemanticContextKind::OtherModule:
|
|
Prefix.append("OtherModule");
|
|
if (!getModuleName().empty())
|
|
Prefix.append((Twine("[") + StringRef(getModuleName()) + "]").str());
|
|
break;
|
|
}
|
|
if (getFlair().toRaw()) {
|
|
Prefix.append("/Flair[");
|
|
bool isFirstFlair = true;
|
|
#define PRINT_FLAIR(KIND, NAME) \
|
|
if (getFlair().contains(CodeCompletionFlairBit::KIND)) { \
|
|
if (isFirstFlair) { isFirstFlair = false; } \
|
|
else { Prefix.append(","); } \
|
|
Prefix.append(NAME); \
|
|
}
|
|
PRINT_FLAIR(ExpressionSpecific, "ExprSpecific");
|
|
PRINT_FLAIR(SuperChain, "SuperChain");
|
|
PRINT_FLAIR(ArgumentLabels, "ArgLabels");
|
|
PRINT_FLAIR(CommonKeywordAtCurrentPosition, "CommonKeyword")
|
|
PRINT_FLAIR(RareKeywordAtCurrentPosition, "RareKeyword")
|
|
PRINT_FLAIR(RareTypeAtCurrentPosition, "RareType")
|
|
PRINT_FLAIR(ExpressionAtNonScriptOrMainFileScope, "ExprAtFileScope")
|
|
Prefix.append("]");
|
|
}
|
|
if (isNotRecommended())
|
|
Prefix.append("/NotRecommended");
|
|
if (isSystem())
|
|
Prefix.append("/IsSystem");
|
|
if (NumBytesToErase != 0) {
|
|
Prefix.append("/Erase[");
|
|
Prefix.append(Twine(NumBytesToErase).str());
|
|
Prefix.append("]");
|
|
}
|
|
switch (getExpectedTypeRelation()) {
|
|
case CodeCompletionResultTypeRelation::Invalid:
|
|
Prefix.append("/TypeRelation[Invalid]");
|
|
break;
|
|
case CodeCompletionResultTypeRelation::Identical:
|
|
Prefix.append("/TypeRelation[Identical]");
|
|
break;
|
|
case CodeCompletionResultTypeRelation::Convertible:
|
|
Prefix.append("/TypeRelation[Convertible]");
|
|
break;
|
|
case CodeCompletionResultTypeRelation::NotApplicable:
|
|
case CodeCompletionResultTypeRelation::Unknown:
|
|
case CodeCompletionResultTypeRelation::Unrelated:
|
|
break;
|
|
}
|
|
|
|
Prefix.append(": ");
|
|
while (Prefix.size() < 36) {
|
|
Prefix.append(" ");
|
|
}
|
|
OS << Prefix;
|
|
}
|
|
|
|
void CodeCompletionResult::dump() const {
|
|
printPrefix(llvm::errs());
|
|
getCompletionString()->print(llvm::errs());
|
|
llvm::errs() << "\n";
|
|
}
|
|
|
|
CodeCompletionResult *
|
|
CodeCompletionResult::withFlair(CodeCompletionFlair NewFlair,
|
|
CodeCompletionResultSink &Sink) const {
|
|
return new (*Sink.Allocator) CodeCompletionResult(
|
|
ContextFree, SemanticContext, NewFlair, NumBytesToErase, TypeDistance,
|
|
NotRecommended, DiagnosticSeverity, DiagnosticMessage);
|
|
}
|
|
|
|
CodeCompletionResult *
|
|
CodeCompletionResult::withContextFreeResultSemanticContextAndFlair(
|
|
const ContextFreeCodeCompletionResult &NewContextFree,
|
|
SemanticContextKind NewSemanticContext, CodeCompletionFlair NewFlair,
|
|
CodeCompletionResultSink &Sink) const {
|
|
return new (*Sink.Allocator) CodeCompletionResult(
|
|
NewContextFree, NewSemanticContext, NewFlair, NumBytesToErase,
|
|
TypeDistance, NotRecommended, DiagnosticSeverity, DiagnosticMessage);
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::withNestedGroup(
|
|
CodeCompletionString::Chunk::ChunkKind Kind,
|
|
llvm::function_ref<void()> body) {
|
|
++CurrentNestingLevel;
|
|
addSimpleChunk(Kind);
|
|
body();
|
|
--CurrentNestingLevel;
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind Kind, StringRef Text) {
|
|
addChunkWithTextNoCopy(Kind, Text.copy(*Sink.Allocator));
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::setAssociatedDecl(const Decl *D) {
|
|
assert(Kind == CodeCompletionResultKind::Declaration);
|
|
|
|
AssociatedDecl = D;
|
|
|
|
if (auto *ClangD = D->getClangDecl())
|
|
CurrentModule = ClangD->getImportedOwningModule();
|
|
// FIXME: macros
|
|
// FIXME: imported header module
|
|
|
|
if (!CurrentModule) {
|
|
ModuleDecl *MD = D->getModuleContext();
|
|
|
|
// If this is an underscored cross-import overlay, map it to the underlying
|
|
// module that declares it instead.
|
|
if (ModuleDecl *Declaring = MD->getDeclaringModuleIfCrossImportOverlay())
|
|
MD = Declaring;
|
|
|
|
CurrentModule = MD;
|
|
}
|
|
|
|
if (D->getAttrs().getDeprecated(D->getASTContext()))
|
|
setContextFreeNotRecommended(ContextFreeNotRecommendedReason::Deprecated);
|
|
else if (D->getAttrs().getSoftDeprecated(D->getASTContext()))
|
|
setContextFreeNotRecommended(
|
|
ContextFreeNotRecommendedReason::SoftDeprecated);
|
|
|
|
if (D->getClangNode()) {
|
|
if (auto *ClangD = D->getClangDecl()) {
|
|
const auto &ClangContext = ClangD->getASTContext();
|
|
if (const clang::RawComment *RC =
|
|
ClangContext.getRawCommentForAnyRedecl(ClangD)) {
|
|
setBriefDocComment(RC->getBriefText(ClangContext));
|
|
}
|
|
}
|
|
} else {
|
|
setBriefDocComment(AssociatedDecl->getBriefComment());
|
|
}
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::addCallArgument(
|
|
Identifier Name, Identifier LocalName, Type Ty, Type ContextTy,
|
|
bool IsVarArg, bool IsInOut, bool IsIUO, bool IsAutoClosure,
|
|
bool UseUnderscoreLabel, bool IsLabeledTrailingClosure, bool HasDefault) {
|
|
++CurrentNestingLevel;
|
|
using ChunkKind = CodeCompletionString::Chunk::ChunkKind;
|
|
|
|
addSimpleChunk(ChunkKind::CallArgumentBegin);
|
|
|
|
if (shouldAnnotateResults()) {
|
|
if (!Name.empty() || !LocalName.empty()) {
|
|
llvm::SmallString<16> EscapedKeyword;
|
|
|
|
if (!Name.empty()) {
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::CallArgumentName,
|
|
escapeKeyword(Name.str(), false, EscapedKeyword));
|
|
|
|
if (!LocalName.empty() && Name != LocalName) {
|
|
addChunkWithTextNoCopy(ChunkKind::Text, " ");
|
|
getLastChunk().setIsAnnotation();
|
|
addChunkWithText(ChunkKind::CallArgumentInternalName,
|
|
escapeKeyword(LocalName.str(), false, EscapedKeyword));
|
|
getLastChunk().setIsAnnotation();
|
|
}
|
|
} else {
|
|
assert(!LocalName.empty());
|
|
addChunkWithTextNoCopy(ChunkKind::CallArgumentName, "_");
|
|
getLastChunk().setIsAnnotation();
|
|
addChunkWithTextNoCopy(ChunkKind::Text, " ");
|
|
getLastChunk().setIsAnnotation();
|
|
addChunkWithText(ChunkKind::CallArgumentInternalName,
|
|
escapeKeyword(LocalName.str(), false, EscapedKeyword));
|
|
}
|
|
addChunkWithTextNoCopy(ChunkKind::CallArgumentColon, ": ");
|
|
}
|
|
} else {
|
|
if (!Name.empty()) {
|
|
llvm::SmallString<16> EscapedKeyword;
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::CallArgumentName,
|
|
escapeKeyword(Name.str(), false, EscapedKeyword));
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::CallArgumentColon, ": ");
|
|
} else if (UseUnderscoreLabel) {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::CallArgumentName, "_");
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::CallArgumentColon, ": ");
|
|
} else if (!LocalName.empty()) {
|
|
// Use local (non-API) parameter name if we have nothing else.
|
|
llvm::SmallString<16> EscapedKeyword;
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::CallArgumentInternalName,
|
|
escapeKeyword(LocalName.str(), false, EscapedKeyword));
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::CallArgumentColon, ": ");
|
|
}
|
|
}
|
|
|
|
// 'inout' arguments are printed specially.
|
|
if (IsInOut) {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::Ampersand, "&");
|
|
Ty = Ty->getInOutObjectType();
|
|
}
|
|
|
|
// If the parameter is of the type @autoclosure ()->output, then the
|
|
// code completion should show the parameter of the output type
|
|
// instead of the function type ()->output.
|
|
if (IsAutoClosure) {
|
|
// 'Ty' may be ErrorType.
|
|
if (auto funcTy = Ty->getAs<FunctionType>())
|
|
Ty = funcTy->getResult();
|
|
}
|
|
|
|
PrintOptions PO;
|
|
PO.SkipAttributes = true;
|
|
PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO;
|
|
PO.OpaqueReturnTypePrinting =
|
|
PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword;
|
|
if (ContextTy)
|
|
PO.setBaseType(ContextTy);
|
|
if (shouldAnnotateResults()) {
|
|
withNestedGroup(ChunkKind::CallArgumentTypeBegin, [&]() {
|
|
CodeCompletionStringPrinter printer(*this);
|
|
auto TL = TypeLoc::withoutLoc(Ty);
|
|
printer.printTypePre(TL);
|
|
Ty->print(printer, PO);
|
|
printer.printTypePost(TL);
|
|
});
|
|
} else {
|
|
std::string TypeName = Ty->getString(PO);
|
|
addChunkWithText(ChunkKind::CallArgumentType, TypeName);
|
|
}
|
|
|
|
if (HasDefault) {
|
|
withNestedGroup(ChunkKind::CallArgumentDefaultBegin, []() {
|
|
// Possibly add the actual value in the future
|
|
});
|
|
}
|
|
|
|
// Look through optional types and type aliases to find out if we have
|
|
// function type.
|
|
Ty = Ty->lookThroughAllOptionalTypes();
|
|
if (auto AFT = Ty->getAs<AnyFunctionType>()) {
|
|
// If this is a closure type, add ChunkKind::CallArgumentClosureType or
|
|
// ChunkKind::CallArgumentClosureExpr for labeled trailing closures.
|
|
PrintOptions PO;
|
|
PO.PrintFunctionRepresentationAttrs =
|
|
PrintOptions::FunctionRepresentationMode::None;
|
|
PO.SkipAttributes = true;
|
|
PO.OpaqueReturnTypePrinting =
|
|
PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword;
|
|
PO.AlwaysTryPrintParameterLabels = true;
|
|
if (ContextTy)
|
|
PO.setBaseType(ContextTy);
|
|
|
|
if (IsLabeledTrailingClosure) {
|
|
// Expand the closure body.
|
|
SmallString<32> buffer;
|
|
llvm::raw_svector_ostream OS(buffer);
|
|
|
|
bool firstParam = true;
|
|
for (const auto ¶m : AFT->getParams()) {
|
|
if (!firstParam)
|
|
OS << ", ";
|
|
firstParam = false;
|
|
|
|
if (param.hasLabel()) {
|
|
OS << param.getLabel();
|
|
} else if (param.hasInternalLabel()) {
|
|
OS << param.getInternalLabel();
|
|
} else {
|
|
OS << "<#";
|
|
if (param.isInOut())
|
|
OS << "inout ";
|
|
OS << param.getPlainType()->getString(PO);
|
|
if (param.isVariadic())
|
|
OS << "...";
|
|
OS << "#>";
|
|
}
|
|
}
|
|
|
|
if (!firstParam)
|
|
OS << " in";
|
|
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::CallArgumentClosureExpr,
|
|
OS.str());
|
|
} else {
|
|
// Add the closure type.
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::CallArgumentClosureType,
|
|
AFT->getString(PO));
|
|
}
|
|
}
|
|
|
|
if (IsVarArg)
|
|
addEllipsis();
|
|
|
|
--CurrentNestingLevel;
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::addTypeAnnotation(Type T, PrintOptions PO,
|
|
StringRef suffix) {
|
|
T = T->getReferenceStorageReferent();
|
|
|
|
// Replace '()' with 'Void'.
|
|
if (T->isVoid())
|
|
T = T->getASTContext().getVoidDecl()->getDeclaredInterfaceType();
|
|
|
|
if (shouldAnnotateResults()) {
|
|
withNestedGroup(CodeCompletionString::Chunk::ChunkKind::TypeAnnotationBegin,
|
|
[&]() {
|
|
CodeCompletionStringPrinter printer(*this);
|
|
auto TL = TypeLoc::withoutLoc(T);
|
|
printer.printTypePre(TL);
|
|
T->print(printer, PO);
|
|
printer.printTypePost(TL);
|
|
if (!suffix.empty())
|
|
printer.printText(suffix);
|
|
});
|
|
} else {
|
|
auto str = T.getString(PO);
|
|
if (!suffix.empty())
|
|
str += suffix.str();
|
|
addTypeAnnotation(str);
|
|
}
|
|
}
|
|
|
|
StringRef CodeCompletionContext::copyString(StringRef Str) {
|
|
return Str.copy(*CurrentResults.Allocator);
|
|
}
|
|
|
|
bool shouldCopyAssociatedUSRForDecl(const ValueDecl *VD) {
|
|
// Avoid trying to generate a USR for some declaration types.
|
|
if (isa<AbstractTypeParamDecl>(VD) && !isa<AssociatedTypeDecl>(VD))
|
|
return false;
|
|
if (isa<ParamDecl>(VD))
|
|
return false;
|
|
if (isa<ModuleDecl>(VD))
|
|
return false;
|
|
if (VD->hasClangNode() && !VD->getClangDecl())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename FnTy>
|
|
static void walkValueDeclAndOverriddenDecls(const Decl *D, const FnTy &Fn) {
|
|
if (auto *VD = dyn_cast<ValueDecl>(D)) {
|
|
Fn(VD);
|
|
walkOverriddenDecls(VD, Fn);
|
|
}
|
|
}
|
|
|
|
ArrayRef<NullTerminatedStringRef>
|
|
copyAssociatedUSRs(llvm::BumpPtrAllocator &Allocator, const Decl *D) {
|
|
llvm::SmallVector<NullTerminatedStringRef, 4> USRs;
|
|
walkValueDeclAndOverriddenDecls(D, [&](llvm::PointerUnion<const ValueDecl*,
|
|
const clang::NamedDecl*> OD) {
|
|
llvm::SmallString<128> SS;
|
|
bool Ignored = true;
|
|
if (auto *OVD = OD.dyn_cast<const ValueDecl*>()) {
|
|
if (shouldCopyAssociatedUSRForDecl(OVD)) {
|
|
llvm::raw_svector_ostream OS(SS);
|
|
Ignored = printValueDeclUSR(OVD, OS);
|
|
}
|
|
} else if (auto *OND = OD.dyn_cast<const clang::NamedDecl*>()) {
|
|
Ignored = clang::index::generateUSRForDecl(OND, SS);
|
|
}
|
|
|
|
if (!Ignored)
|
|
USRs.emplace_back(SS, Allocator);
|
|
});
|
|
|
|
if (!USRs.empty())
|
|
return makeArrayRef(USRs).copy(Allocator);
|
|
|
|
return {};
|
|
}
|
|
|
|
CodeCompletionResult::CodeCompletionResult(
|
|
const ContextFreeCodeCompletionResult &ContextFree,
|
|
SemanticContextKind SemanticContext, CodeCompletionFlair Flair,
|
|
uint8_t NumBytesToErase, const ExpectedTypeContext *TypeContext,
|
|
const DeclContext *DC, ContextualNotRecommendedReason NotRecommended,
|
|
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
|
|
NullTerminatedStringRef DiagnosticMessage)
|
|
: ContextFree(ContextFree), SemanticContext(SemanticContext),
|
|
Flair(Flair.toRaw()), NotRecommended(NotRecommended),
|
|
DiagnosticSeverity(DiagnosticSeverity),
|
|
DiagnosticMessage(DiagnosticMessage), NumBytesToErase(NumBytesToErase),
|
|
TypeDistance(
|
|
ContextFree.getResultType().calculateTypeRelation(TypeContext, DC)) {}
|
|
|
|
CodeCompletionOperatorKind
|
|
ContextFreeCodeCompletionResult::getCodeCompletionOperatorKind(
|
|
const CodeCompletionString *str) {
|
|
StringRef name = str->getFirstTextChunk(/*includeLeadingPunctuation=*/true);
|
|
using CCOK = CodeCompletionOperatorKind;
|
|
using OpPair = std::pair<StringRef, CCOK>;
|
|
|
|
// This list must be kept in alphabetical order.
|
|
static OpPair ops[] = {
|
|
std::make_pair("!", CCOK::Bang),
|
|
std::make_pair("!=", CCOK::NotEq),
|
|
std::make_pair("!==", CCOK::NotEqEq),
|
|
std::make_pair("%", CCOK::Modulo),
|
|
std::make_pair("%=", CCOK::ModuloEq),
|
|
std::make_pair("&", CCOK::Amp),
|
|
std::make_pair("&&", CCOK::AmpAmp),
|
|
std::make_pair("&*", CCOK::AmpStar),
|
|
std::make_pair("&+", CCOK::AmpPlus),
|
|
std::make_pair("&-", CCOK::AmpMinus),
|
|
std::make_pair("&=", CCOK::AmpEq),
|
|
std::make_pair("(", CCOK::LParen),
|
|
std::make_pair("*", CCOK::Star),
|
|
std::make_pair("*=", CCOK::StarEq),
|
|
std::make_pair("+", CCOK::Plus),
|
|
std::make_pair("+=", CCOK::PlusEq),
|
|
std::make_pair("-", CCOK::Minus),
|
|
std::make_pair("-=", CCOK::MinusEq),
|
|
std::make_pair(".", CCOK::Dot),
|
|
std::make_pair("...", CCOK::DotDotDot),
|
|
std::make_pair("..<", CCOK::DotDotLess),
|
|
std::make_pair("/", CCOK::Slash),
|
|
std::make_pair("/=", CCOK::SlashEq),
|
|
std::make_pair("<", CCOK::Less),
|
|
std::make_pair("<<", CCOK::LessLess),
|
|
std::make_pair("<<=", CCOK::LessLessEq),
|
|
std::make_pair("<=", CCOK::LessEq),
|
|
std::make_pair("=", CCOK::Eq),
|
|
std::make_pair("==", CCOK::EqEq),
|
|
std::make_pair("===", CCOK::EqEqEq),
|
|
std::make_pair(">", CCOK::Greater),
|
|
std::make_pair(">=", CCOK::GreaterEq),
|
|
std::make_pair(">>", CCOK::GreaterGreater),
|
|
std::make_pair(">>=", CCOK::GreaterGreaterEq),
|
|
std::make_pair("?.", CCOK::QuestionDot),
|
|
std::make_pair("^", CCOK::Caret),
|
|
std::make_pair("^=", CCOK::CaretEq),
|
|
std::make_pair("|", CCOK::Pipe),
|
|
std::make_pair("|=", CCOK::PipeEq),
|
|
std::make_pair("||", CCOK::PipePipe),
|
|
std::make_pair("~=", CCOK::TildeEq),
|
|
};
|
|
static auto opsSize = sizeof(ops) / sizeof(ops[0]);
|
|
|
|
auto I = std::lower_bound(
|
|
ops, &ops[opsSize], std::make_pair(name, CCOK::None),
|
|
[](const OpPair &a, const OpPair &b) { return a.first < b.first; });
|
|
|
|
if (I == &ops[opsSize] || I->first != name)
|
|
return CCOK::Unknown;
|
|
return I->second;
|
|
}
|
|
|
|
CodeCompletionResult *CodeCompletionResultBuilder::takeResult() {
|
|
auto &Allocator = *Sink.Allocator;
|
|
auto *CCS = CodeCompletionString::create(Allocator, Chunks);
|
|
|
|
CodeCompletionDiagnosticSeverity ContextFreeDiagnosticSeverity =
|
|
CodeCompletionDiagnosticSeverity::None;
|
|
NullTerminatedStringRef ContextFreeDiagnosticMessage;
|
|
if (ContextFreeNotRecReason != ContextFreeNotRecommendedReason::None) {
|
|
// FIXME: We should generate the message lazily.
|
|
if (const auto *VD = dyn_cast<ValueDecl>(AssociatedDecl)) {
|
|
CodeCompletionDiagnosticSeverity severity;
|
|
SmallString<256> message;
|
|
llvm::raw_svector_ostream messageOS(message);
|
|
if (!getContextFreeCompletionDiagnostics(ContextFreeNotRecReason, VD,
|
|
severity, messageOS)) {
|
|
ContextFreeDiagnosticSeverity = severity;
|
|
ContextFreeDiagnosticMessage =
|
|
NullTerminatedStringRef(message, Allocator);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This variable should be initialized by all the switch cases below.
|
|
ContextFreeCodeCompletionResult *ContextFreeResult = nullptr;
|
|
|
|
switch (Kind) {
|
|
case CodeCompletionResultKind::Declaration: {
|
|
NullTerminatedStringRef ModuleName;
|
|
if (CurrentModule) {
|
|
if (Sink.LastModule.first == CurrentModule.getOpaqueValue()) {
|
|
ModuleName = Sink.LastModule.second;
|
|
} else {
|
|
if (auto *C = CurrentModule.dyn_cast<const clang::Module *>()) {
|
|
ModuleName =
|
|
NullTerminatedStringRef(C->getFullModuleName(), Allocator);
|
|
} else {
|
|
ModuleName = NullTerminatedStringRef(
|
|
CurrentModule.get<const swift::ModuleDecl *>()->getName().str(),
|
|
Allocator);
|
|
}
|
|
Sink.LastModule.first = CurrentModule.getOpaqueValue();
|
|
Sink.LastModule.second = ModuleName;
|
|
}
|
|
}
|
|
|
|
ContextFreeResult = ContextFreeCodeCompletionResult::createDeclResult(
|
|
Allocator, CCS, AssociatedDecl, ModuleName,
|
|
NullTerminatedStringRef(BriefDocComment, Allocator),
|
|
copyAssociatedUSRs(Allocator, AssociatedDecl), ResultType,
|
|
ContextFreeNotRecReason, ContextFreeDiagnosticSeverity,
|
|
ContextFreeDiagnosticMessage);
|
|
break;
|
|
}
|
|
|
|
case CodeCompletionResultKind::Keyword:
|
|
ContextFreeResult = ContextFreeCodeCompletionResult::createKeywordResult(
|
|
Allocator, KeywordKind, CCS,
|
|
NullTerminatedStringRef(BriefDocComment, Allocator), ResultType);
|
|
break;
|
|
case CodeCompletionResultKind::BuiltinOperator:
|
|
case CodeCompletionResultKind::Pattern:
|
|
ContextFreeResult =
|
|
ContextFreeCodeCompletionResult::createPatternOrBuiltInOperatorResult(
|
|
Allocator, Kind, CCS, CodeCompletionOperatorKind::None,
|
|
NullTerminatedStringRef(BriefDocComment, Allocator), ResultType,
|
|
ContextFreeNotRecReason, ContextFreeDiagnosticSeverity,
|
|
ContextFreeDiagnosticMessage);
|
|
break;
|
|
case CodeCompletionResultKind::Literal:
|
|
assert(LiteralKind.hasValue());
|
|
ContextFreeResult = ContextFreeCodeCompletionResult::createLiteralResult(
|
|
Allocator, *LiteralKind, CCS, ResultType);
|
|
break;
|
|
}
|
|
|
|
CodeCompletionDiagnosticSeverity ContextualDiagnosticSeverity =
|
|
CodeCompletionDiagnosticSeverity::None;
|
|
NullTerminatedStringRef ContextualDiagnosticMessage;
|
|
if (ContextualNotRecReason != ContextualNotRecommendedReason::None) {
|
|
// FIXME: We should generate the message lazily.
|
|
if (const auto *VD = dyn_cast<ValueDecl>(AssociatedDecl)) {
|
|
CodeCompletionDiagnosticSeverity severity;
|
|
SmallString<256> message;
|
|
llvm::raw_svector_ostream messageOS(message);
|
|
if (!getContextualCompletionDiagnostics(ContextualNotRecReason, VD,
|
|
severity, messageOS)) {
|
|
ContextualDiagnosticSeverity = severity;
|
|
ContextualDiagnosticMessage =
|
|
NullTerminatedStringRef(message, Allocator);
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(ContextFreeResult != nullptr &&
|
|
"ContextFreeResult should have been constructed by the switch above");
|
|
CodeCompletionResult *result = new (Allocator) CodeCompletionResult(
|
|
*ContextFreeResult, SemanticContext, Flair, NumBytesToErase, TypeContext,
|
|
DC, ContextualNotRecReason, ContextualDiagnosticSeverity,
|
|
ContextualDiagnosticMessage);
|
|
return result;
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::finishResult() {
|
|
if (!Cancelled)
|
|
Sink.Results.push_back(takeResult());
|
|
}
|
|
|
|
std::vector<CodeCompletionResult *>
|
|
CodeCompletionContext::sortCompletionResults(
|
|
ArrayRef<CodeCompletionResult *> Results) {
|
|
std::vector<CodeCompletionResult *> SortedResults(Results.begin(),
|
|
Results.end());
|
|
|
|
std::sort(SortedResults.begin(), SortedResults.end(),
|
|
[](const auto &LHS, const auto &RHS) {
|
|
int Result = StringRef(LHS->getFilterName())
|
|
.compare_insensitive(RHS->getFilterName());
|
|
// If the case insensitive comparison is equal, then secondary
|
|
// sort order should be case sensitive.
|
|
if (Result == 0)
|
|
Result = LHS->getFilterName().compare(RHS->getFilterName());
|
|
return Result < 0;
|
|
});
|
|
|
|
return SortedResults;
|
|
}
|
|
|
|
namespace {
|
|
|
|
class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
|
|
CodeCompletionContext &CompletionContext;
|
|
CodeCompletionConsumer &Consumer;
|
|
CodeCompletionExpr *CodeCompleteTokenExpr = nullptr;
|
|
CompletionKind Kind = CompletionKind::None;
|
|
Expr *ParsedExpr = nullptr;
|
|
SourceLoc DotLoc;
|
|
TypeLoc ParsedTypeLoc;
|
|
DeclContext *CurDeclContext = nullptr;
|
|
DeclAttrKind AttrKind;
|
|
|
|
/// In situations when \c SyntaxKind hints or determines
|
|
/// completions, i.e. a precedence group attribute, this
|
|
/// can be set and used to control the code completion scenario.
|
|
SyntaxKind SyntxKind;
|
|
|
|
int AttrParamIndex;
|
|
bool IsInSil = false;
|
|
bool HasSpace = false;
|
|
bool ShouldCompleteCallPatternAfterParen = true;
|
|
bool PreferFunctionReferencesToCalls = false;
|
|
bool AttTargetIsIndependent = false;
|
|
bool IsAtStartOfLine = false;
|
|
Optional<DeclKind> AttTargetDK;
|
|
Optional<StmtKind> ParentStmtKind;
|
|
|
|
SmallVector<StringRef, 3> ParsedKeywords;
|
|
SourceLoc introducerLoc;
|
|
|
|
std::vector<std::pair<std::string, bool>> SubModuleNameVisibilityPairs;
|
|
|
|
void addSuperKeyword(CodeCompletionResultSink &Sink) {
|
|
auto *DC = CurDeclContext->getInnermostTypeContext();
|
|
if (!DC)
|
|
return;
|
|
auto *CD = DC->getSelfClassDecl();
|
|
if (!CD)
|
|
return;
|
|
Type ST = CD->getSuperclass();
|
|
if (ST.isNull() || ST->is<ErrorType>())
|
|
return;
|
|
|
|
CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Keyword,
|
|
SemanticContextKind::CurrentNominal,
|
|
{});
|
|
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(CurDeclContext)) {
|
|
if (AFD->getOverriddenDecl() != nullptr) {
|
|
Builder.addFlair(CodeCompletionFlairBit::CommonKeywordAtCurrentPosition);
|
|
}
|
|
}
|
|
|
|
Builder.setKeywordKind(CodeCompletionKeywordKind::kw_super);
|
|
Builder.addKeyword("super");
|
|
Builder.addTypeAnnotation(ST, PrintOptions());
|
|
}
|
|
|
|
Optional<std::pair<Type, ConcreteDeclRef>> typeCheckParsedExpr() {
|
|
assert(ParsedExpr && "should have an expression");
|
|
|
|
// Figure out the kind of type-check we'll be performing.
|
|
auto CheckKind = CompletionTypeCheckKind::Normal;
|
|
if (Kind == CompletionKind::KeyPathExprObjC)
|
|
CheckKind = CompletionTypeCheckKind::KeyPath;
|
|
|
|
// If we've already successfully type-checked the expression for some
|
|
// reason, just return the type.
|
|
// FIXME: if it's ErrorType but we've already typechecked we shouldn't
|
|
// typecheck again. rdar://21466394
|
|
if (CheckKind == CompletionTypeCheckKind::Normal &&
|
|
ParsedExpr->getType() && !ParsedExpr->getType()->is<ErrorType>()) {
|
|
return getReferencedDecl(ParsedExpr);
|
|
}
|
|
|
|
ConcreteDeclRef ReferencedDecl = nullptr;
|
|
Expr *ModifiedExpr = ParsedExpr;
|
|
if (auto T = getTypeOfCompletionContextExpr(P.Context, CurDeclContext,
|
|
CheckKind, ModifiedExpr,
|
|
ReferencedDecl)) {
|
|
// FIXME: even though we don't apply the solution, the type checker may
|
|
// modify the original expression. We should understand what effect that
|
|
// may have on code completion.
|
|
ParsedExpr = ModifiedExpr;
|
|
|
|
return std::make_pair(*T, ReferencedDecl);
|
|
}
|
|
return None;
|
|
}
|
|
|
|
/// \returns true on success, false on failure.
|
|
bool typecheckParsedType() {
|
|
assert(ParsedTypeLoc.getTypeRepr() && "should have a TypeRepr");
|
|
if (ParsedTypeLoc.wasValidated() && !ParsedTypeLoc.isError()) {
|
|
return true;
|
|
}
|
|
|
|
const auto ty = swift::performTypeResolution(
|
|
ParsedTypeLoc.getTypeRepr(), P.Context,
|
|
/*isSILMode=*/false,
|
|
/*isSILType=*/false,
|
|
CurDeclContext->getGenericEnvironmentOfContext(),
|
|
/*GenericParams=*/nullptr,
|
|
CurDeclContext,
|
|
/*ProduceDiagnostics=*/false);
|
|
if (!ty->hasError()) {
|
|
ParsedTypeLoc.setType(CurDeclContext->mapTypeIntoContext(ty));
|
|
return true;
|
|
}
|
|
|
|
ParsedTypeLoc.setType(ty);
|
|
|
|
// It doesn't type check as a type, so see if it's a qualifying module name.
|
|
if (auto *ITR = dyn_cast<IdentTypeRepr>(ParsedTypeLoc.getTypeRepr())) {
|
|
const auto &componentRange = ITR->getComponentRange();
|
|
// If it has more than one component, it can't be a module name.
|
|
if (std::distance(componentRange.begin(), componentRange.end()) != 1)
|
|
return false;
|
|
|
|
const auto &component = componentRange.front();
|
|
ImportPath::Module::Builder builder(
|
|
component->getNameRef().getBaseIdentifier(),
|
|
component->getLoc());
|
|
|
|
if (auto Module = Context.getLoadedModule(builder.get()))
|
|
ParsedTypeLoc.setType(ModuleType::get(Module));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
CodeCompletionCallbacksImpl(Parser &P,
|
|
CodeCompletionContext &CompletionContext,
|
|
CodeCompletionConsumer &Consumer)
|
|
: CodeCompletionCallbacks(P), CompletionContext(CompletionContext),
|
|
Consumer(Consumer) {
|
|
}
|
|
|
|
void setAttrTargetDeclKind(Optional<DeclKind> DK) override {
|
|
if (DK == DeclKind::PatternBinding)
|
|
DK = DeclKind::Var;
|
|
else if (DK == DeclKind::Param)
|
|
// For params, consider the attribute is always for the decl.
|
|
AttTargetIsIndependent = false;
|
|
|
|
if (!AttTargetIsIndependent)
|
|
AttTargetDK = DK;
|
|
}
|
|
|
|
void completeDotExpr(CodeCompletionExpr *E, SourceLoc DotLoc) override;
|
|
void completeStmtOrExpr(CodeCompletionExpr *E) override;
|
|
void completePostfixExprBeginning(CodeCompletionExpr *E) override;
|
|
void completeForEachSequenceBeginning(CodeCompletionExpr *E) override;
|
|
void completePostfixExpr(Expr *E, bool hasSpace) override;
|
|
void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) override;
|
|
void completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) override;
|
|
|
|
void completeTypeDeclResultBeginning() override;
|
|
void completeTypeSimpleBeginning() override;
|
|
void completeTypeIdentifierWithDot(IdentTypeRepr *ITR) override;
|
|
void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) override;
|
|
|
|
void completeCaseStmtKeyword() override;
|
|
void completeCaseStmtBeginning(CodeCompletionExpr *E) override;
|
|
void completeDeclAttrBeginning(bool Sil, bool isIndependent) override;
|
|
void completeDeclAttrParam(DeclAttrKind DK, int Index) override;
|
|
void completeEffectsSpecifier(bool hasAsync, bool hasThrows) override;
|
|
void completeInPrecedenceGroup(SyntaxKind SK) override;
|
|
void completeNominalMemberBeginning(
|
|
SmallVectorImpl<StringRef> &Keywords, SourceLoc introducerLoc) override;
|
|
void completeAccessorBeginning(CodeCompletionExpr *E) override;
|
|
|
|
void completePoundAvailablePlatform() override;
|
|
void completeImportDecl(ImportPath::Builder &Path) override;
|
|
void completeUnresolvedMember(CodeCompletionExpr *E,
|
|
SourceLoc DotLoc) override;
|
|
void completeCallArg(CodeCompletionExpr *E, bool isFirst) override;
|
|
void completeLabeledTrailingClosure(CodeCompletionExpr *E,
|
|
bool isAtStartOfLine) override;
|
|
|
|
bool canPerformCompleteLabeledTrailingClosure() const override {
|
|
return true;
|
|
}
|
|
|
|
void completeReturnStmt(CodeCompletionExpr *E) override;
|
|
void completeYieldStmt(CodeCompletionExpr *E,
|
|
Optional<unsigned> yieldIndex) override;
|
|
void completeAfterPoundExpr(CodeCompletionExpr *E,
|
|
Optional<StmtKind> ParentKind) override;
|
|
void completeAfterPoundDirective() override;
|
|
void completePlatformCondition() override;
|
|
void completeGenericRequirement() override;
|
|
void completeAfterIfStmt(bool hasElse) override;
|
|
void completeStmtLabel(StmtKind ParentKind) override;
|
|
void completeForEachPatternBeginning(bool hasTry, bool hasAwait) override;
|
|
void completeTypeAttrBeginning() override;
|
|
|
|
void doneParsing() override;
|
|
|
|
private:
|
|
void addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody);
|
|
bool trySolverCompletion(bool MaybeFuncBody);
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
namespace {
|
|
|
|
class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
|
|
CodeCompletionResultSink &Sink;
|
|
ASTContext &Ctx;
|
|
const DeclContext *CurrDeclContext;
|
|
SmallVectorImpl<StringRef> &ParsedKeywords;
|
|
SourceLoc introducerLoc;
|
|
|
|
bool hasFuncIntroducer = false;
|
|
bool hasVarIntroducer = false;
|
|
bool hasTypealiasIntroducer = false;
|
|
bool hasInitializerModifier = false;
|
|
bool hasAccessModifier = false;
|
|
bool hasOverride = false;
|
|
bool hasOverridabilityModifier = false;
|
|
bool hasStaticOrClass = false;
|
|
|
|
public:
|
|
CompletionOverrideLookup(CodeCompletionResultSink &Sink, ASTContext &Ctx,
|
|
const DeclContext *CurrDeclContext,
|
|
SmallVectorImpl<StringRef> &ParsedKeywords,
|
|
SourceLoc introducerLoc)
|
|
: Sink(Sink), Ctx(Ctx), CurrDeclContext(CurrDeclContext),
|
|
ParsedKeywords(ParsedKeywords), introducerLoc(introducerLoc) {
|
|
hasFuncIntroducer = isKeywordSpecified("func");
|
|
hasVarIntroducer = isKeywordSpecified("var") ||
|
|
isKeywordSpecified("let");
|
|
hasTypealiasIntroducer = isKeywordSpecified("typealias");
|
|
hasInitializerModifier = isKeywordSpecified("required") ||
|
|
isKeywordSpecified("convenience");
|
|
hasAccessModifier = isKeywordSpecified("private") ||
|
|
isKeywordSpecified("fileprivate") ||
|
|
isKeywordSpecified("internal") ||
|
|
isKeywordSpecified("public") ||
|
|
isKeywordSpecified("open");
|
|
hasOverride = isKeywordSpecified("override");
|
|
hasOverridabilityModifier = isKeywordSpecified("final") ||
|
|
isKeywordSpecified("open");
|
|
hasStaticOrClass = isKeywordSpecified(getTokenText(tok::kw_class)) ||
|
|
isKeywordSpecified(getTokenText(tok::kw_static));
|
|
}
|
|
|
|
bool isKeywordSpecified(StringRef Word) {
|
|
return std::find(ParsedKeywords.begin(), ParsedKeywords.end(), Word)
|
|
!= ParsedKeywords.end();
|
|
}
|
|
|
|
bool missingOverride(DeclVisibilityKind Reason) {
|
|
return !hasOverride && Reason == DeclVisibilityKind::MemberOfSuper &&
|
|
!CurrDeclContext->getSelfProtocolDecl();
|
|
}
|
|
|
|
/// Add an access modifier (i.e. `public`) to \p Builder is necessary.
|
|
/// Returns \c true if the modifier is actually added, \c false otherwise.
|
|
bool addAccessControl(const ValueDecl *VD,
|
|
CodeCompletionResultBuilder &Builder) {
|
|
auto CurrentNominal = CurrDeclContext->getSelfNominalTypeDecl();
|
|
assert(CurrentNominal);
|
|
|
|
auto AccessOfContext = CurrentNominal->getFormalAccess();
|
|
if (AccessOfContext < AccessLevel::Public)
|
|
return false;
|
|
|
|
auto Access = VD->getFormalAccess();
|
|
// Use the greater access between the protocol requirement and the witness.
|
|
// In case of:
|
|
//
|
|
// public protocol P { func foo() }
|
|
// public class B { func foo() {} }
|
|
// public class C: B, P {
|
|
// <complete>
|
|
// }
|
|
//
|
|
// 'VD' is 'B.foo()' which is implicitly 'internal'. But as the overriding
|
|
// declaration, the user needs to write both 'public' and 'override':
|
|
//
|
|
// public class C: B {
|
|
// public override func foo() {}
|
|
// }
|
|
if (Access < AccessLevel::Public &&
|
|
!isa<ProtocolDecl>(VD->getDeclContext())) {
|
|
for (auto Conformance : CurrentNominal->getAllConformances()) {
|
|
Conformance->getRootConformance()->forEachValueWitness(
|
|
[&](ValueDecl *req, Witness witness) {
|
|
if (witness.getDecl() == VD)
|
|
Access = std::max(
|
|
Access, Conformance->getProtocol()->getFormalAccess());
|
|
});
|
|
}
|
|
}
|
|
|
|
Access = std::min(Access, AccessOfContext);
|
|
// Only emit 'public', not needed otherwise.
|
|
if (Access < AccessLevel::Public)
|
|
return false;
|
|
|
|
Builder.addAccessControlKeyword(Access);
|
|
return true;
|
|
}
|
|
|
|
/// Return type if the result type if \p VD should be represented as opaque
|
|
/// result type.
|
|
Type getOpaqueResultType(const ValueDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo) {
|
|
if (Reason !=
|
|
DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal)
|
|
return nullptr;
|
|
|
|
auto currTy = CurrDeclContext->getDeclaredTypeInContext();
|
|
if (!currTy)
|
|
return nullptr;
|
|
|
|
Type ResultT;
|
|
if (auto *FD = dyn_cast<FuncDecl>(VD)) {
|
|
if (FD->getGenericParams()) {
|
|
// Generic function cannot have opaque result type.
|
|
return nullptr;
|
|
}
|
|
ResultT = FD->getResultInterfaceType();
|
|
} else if (auto *SD = dyn_cast<SubscriptDecl>(VD)) {
|
|
if (SD->getGenericParams()) {
|
|
// Generic subscript cannot have opaque result type.
|
|
return nullptr;
|
|
}
|
|
ResultT = SD->getElementInterfaceType();
|
|
} else if (auto *VarD = dyn_cast<VarDecl>(VD)) {
|
|
ResultT = VarD->getInterfaceType();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!ResultT->is<DependentMemberType>() ||
|
|
!ResultT->castTo<DependentMemberType>()->getAssocType())
|
|
// The result is not a valid associatedtype.
|
|
return nullptr;
|
|
|
|
// Try substitution to see if the associated type is resolved to concrete
|
|
// type.
|
|
auto substMap = currTy->getMemberSubstitutionMap(
|
|
CurrDeclContext->getParentModule(), VD);
|
|
if (!ResultT.subst(substMap)->is<DependentMemberType>())
|
|
// If resolved print it.
|
|
return nullptr;
|
|
|
|
auto genericSig = VD->getDeclContext()->getGenericSignatureOfContext();
|
|
|
|
if (genericSig->isConcreteType(ResultT))
|
|
// If it has same type requrement, we will emit the concrete type.
|
|
return nullptr;
|
|
|
|
// Collect requirements on the associatedtype.
|
|
SmallVector<Type, 2> opaqueTypes;
|
|
bool hasExplicitAnyObject = false;
|
|
if (auto superTy = genericSig->getSuperclassBound(ResultT))
|
|
opaqueTypes.push_back(superTy);
|
|
for (const auto proto : genericSig->getRequiredProtocols(ResultT))
|
|
opaqueTypes.push_back(proto->getDeclaredInterfaceType());
|
|
if (auto layout = genericSig->getLayoutConstraint(ResultT))
|
|
hasExplicitAnyObject = layout->isClass();
|
|
|
|
if (!hasExplicitAnyObject) {
|
|
if (opaqueTypes.empty())
|
|
return nullptr;
|
|
if (opaqueTypes.size() == 1)
|
|
return opaqueTypes.front();
|
|
}
|
|
return ProtocolCompositionType::get(
|
|
VD->getASTContext(), opaqueTypes, hasExplicitAnyObject);
|
|
}
|
|
|
|
void addValueOverride(const ValueDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo,
|
|
CodeCompletionResultBuilder &Builder,
|
|
bool hasDeclIntroducer) {
|
|
Type opaqueResultType = getOpaqueResultType(VD, Reason, dynamicLookupInfo);
|
|
|
|
class DeclPrinter : public CodeCompletionStringPrinter {
|
|
Type OpaqueBaseTy;
|
|
|
|
public:
|
|
DeclPrinter(CodeCompletionResultBuilder &Builder, Type OpaqueBaseTy)
|
|
: CodeCompletionStringPrinter(Builder), OpaqueBaseTy(OpaqueBaseTy) { }
|
|
|
|
// As for FuncDecl, SubscriptDecl, and VarDecl, substitute the result type
|
|
// with 'OpaqueBaseTy' if specified.
|
|
void printDeclResultTypePre(ValueDecl *VD, TypeLoc &TL) override {
|
|
if (!OpaqueBaseTy.isNull()) {
|
|
setNextChunkKind(ChunkKind::Keyword);
|
|
printText("some ");
|
|
setNextChunkKind(ChunkKind::Text);
|
|
TL = TypeLoc::withoutLoc(OpaqueBaseTy);
|
|
}
|
|
CodeCompletionStringPrinter::printDeclResultTypePre(VD, TL);
|
|
}
|
|
};
|
|
|
|
DeclPrinter Printer(Builder, opaqueResultType);
|
|
Printer.startPreamble();
|
|
|
|
bool modifierAdded = false;
|
|
|
|
// 'public' if needed.
|
|
modifierAdded |= !hasAccessModifier && addAccessControl(VD, Builder);
|
|
|
|
// 'override' if needed
|
|
if (missingOverride(Reason)) {
|
|
Builder.addOverrideKeyword();
|
|
modifierAdded |= true;
|
|
}
|
|
|
|
// Erase existing introducer (e.g. 'func') if any modifiers are added.
|
|
if (hasDeclIntroducer && modifierAdded) {
|
|
auto dist = Ctx.SourceMgr.getByteDistance(
|
|
introducerLoc, Ctx.SourceMgr.getCodeCompletionLoc());
|
|
if (dist <= CodeCompletionResult::MaxNumBytesToErase) {
|
|
Builder.setNumBytesToErase(dist);
|
|
hasDeclIntroducer = false;
|
|
}
|
|
}
|
|
|
|
PrintOptions PO;
|
|
if (auto transformType = CurrDeclContext->getDeclaredTypeInContext())
|
|
PO.setBaseType(transformType);
|
|
PO.PrintPropertyAccessors = false;
|
|
PO.PrintSubscriptAccessors = false;
|
|
|
|
PO.SkipUnderscoredKeywords = true;
|
|
PO.PrintImplicitAttrs = false;
|
|
PO.ExclusiveAttrList.push_back(TAK_escaping);
|
|
PO.ExclusiveAttrList.push_back(TAK_autoclosure);
|
|
// Print certain modifiers only when the introducer is not written.
|
|
// Otherwise, the user can add it after the completion.
|
|
if (!hasDeclIntroducer) {
|
|
PO.ExclusiveAttrList.push_back(DAK_Nonisolated);
|
|
}
|
|
|
|
PO.PrintAccess = false;
|
|
PO.PrintOverrideKeyword = false;
|
|
PO.PrintSelfAccessKindKeyword = false;
|
|
|
|
PO.PrintStaticKeyword = !hasStaticOrClass && !hasDeclIntroducer;
|
|
PO.SkipIntroducerKeywords = hasDeclIntroducer;
|
|
VD->print(Printer, PO);
|
|
}
|
|
|
|
void addMethodOverride(const FuncDecl *FD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo) {
|
|
CodeCompletionResultBuilder Builder(Sink,
|
|
CodeCompletionResultKind::Declaration,
|
|
SemanticContextKind::Super, {});
|
|
Builder.setResultTypeNotApplicable();
|
|
Builder.setAssociatedDecl(FD);
|
|
addValueOverride(FD, Reason, dynamicLookupInfo, Builder, hasFuncIntroducer);
|
|
Builder.addBraceStmtWithCursor();
|
|
}
|
|
|
|
void addVarOverride(const VarDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo) {
|
|
// Overrides cannot use 'let', but if the 'override' keyword is specified
|
|
// then the intention is clear, so provide the results anyway. The compiler
|
|
// can then provide an error telling you to use 'var' instead.
|
|
// If we don't need override then it's a protocol requirement, so show it.
|
|
if (missingOverride(Reason) && hasVarIntroducer &&
|
|
isKeywordSpecified("let"))
|
|
return;
|
|
|
|
CodeCompletionResultBuilder Builder(Sink,
|
|
CodeCompletionResultKind::Declaration,
|
|
SemanticContextKind::Super, {});
|
|
Builder.setAssociatedDecl(VD);
|
|
addValueOverride(VD, Reason, dynamicLookupInfo, Builder, hasVarIntroducer);
|
|
}
|
|
|
|
void addSubscriptOverride(const SubscriptDecl *SD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo) {
|
|
CodeCompletionResultBuilder Builder(Sink,
|
|
CodeCompletionResultKind::Declaration,
|
|
SemanticContextKind::Super, {});
|
|
Builder.setResultTypeNotApplicable();
|
|
Builder.setAssociatedDecl(SD);
|
|
addValueOverride(SD, Reason, dynamicLookupInfo, Builder, false);
|
|
Builder.addBraceStmtWithCursor();
|
|
}
|
|
|
|
void addTypeAlias(const AssociatedTypeDecl *ATD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo) {
|
|
CodeCompletionResultBuilder Builder(Sink,
|
|
CodeCompletionResultKind::Declaration,
|
|
SemanticContextKind::Super, {});
|
|
Builder.setResultTypeNotApplicable();
|
|
Builder.setAssociatedDecl(ATD);
|
|
if (!hasTypealiasIntroducer && !hasAccessModifier)
|
|
(void)addAccessControl(ATD, Builder);
|
|
if (!hasTypealiasIntroducer)
|
|
Builder.addDeclIntroducer("typealias ");
|
|
Builder.addBaseName(ATD->getName().str());
|
|
Builder.addTextChunk(" = ");
|
|
Builder.addSimpleNamedParameter("Type");
|
|
}
|
|
|
|
void addConstructor(const ConstructorDecl *CD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo) {
|
|
CodeCompletionResultBuilder Builder(Sink,
|
|
CodeCompletionResultKind::Declaration,
|
|
SemanticContextKind::Super, {});
|
|
Builder.setResultTypeNotApplicable();
|
|
Builder.setAssociatedDecl(CD);
|
|
|
|
CodeCompletionStringPrinter printer(Builder);
|
|
printer.startPreamble();
|
|
|
|
if (!hasAccessModifier)
|
|
(void)addAccessControl(CD, Builder);
|
|
|
|
if (missingOverride(Reason) && CD->isDesignatedInit() && !CD->isRequired())
|
|
Builder.addOverrideKeyword();
|
|
|
|
// Emit 'required' if we're in class context, 'required' is not specified,
|
|
// and 1) this is a protocol conformance and the class is not final, or 2)
|
|
// this is subclass and the initializer is marked as required.
|
|
bool needRequired = false;
|
|
auto C = CurrDeclContext->getSelfClassDecl();
|
|
if (C && !isKeywordSpecified("required")) {
|
|
switch (Reason) {
|
|
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
|
|
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
|
|
if (!C->isSemanticallyFinal())
|
|
needRequired = true;
|
|
break;
|
|
case DeclVisibilityKind::MemberOfSuper:
|
|
if (CD->isRequired())
|
|
needRequired = true;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
if (needRequired)
|
|
Builder.addRequiredKeyword();
|
|
|
|
{
|
|
PrintOptions Options;
|
|
if (auto transformType = CurrDeclContext->getDeclaredTypeInContext())
|
|
Options.setBaseType(transformType);
|
|
Options.PrintImplicitAttrs = false;
|
|
Options.SkipAttributes = true;
|
|
CD->print(printer, Options);
|
|
}
|
|
printer.flush();
|
|
|
|
Builder.addBraceStmtWithCursor();
|
|
}
|
|
|
|
// Implement swift::VisibleDeclConsumer.
|
|
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo) override {
|
|
if (Reason == DeclVisibilityKind::MemberOfCurrentNominal)
|
|
return;
|
|
|
|
if (D->shouldHideFromEditor())
|
|
return;
|
|
|
|
if (D->isSemanticallyFinal())
|
|
return;
|
|
|
|
bool hasIntroducer = hasFuncIntroducer ||
|
|
hasVarIntroducer ||
|
|
hasTypealiasIntroducer;
|
|
|
|
if (hasStaticOrClass && !D->isStatic())
|
|
return;
|
|
|
|
// As per the current convention, only instance members are
|
|
// suggested if an introducer is not accompanied by a 'static' or
|
|
// 'class' modifier.
|
|
if (hasIntroducer && !hasStaticOrClass && D->isStatic())
|
|
return;
|
|
|
|
if (auto *FD = dyn_cast<FuncDecl>(D)) {
|
|
// We cannot override operators as members.
|
|
if (FD->isBinaryOperator() || FD->isUnaryOperator())
|
|
return;
|
|
|
|
// We cannot override individual accessors.
|
|
if (isa<AccessorDecl>(FD))
|
|
return;
|
|
|
|
if (hasFuncIntroducer || (!hasIntroducer && !hasInitializerModifier))
|
|
addMethodOverride(FD, Reason, dynamicLookupInfo);
|
|
return;
|
|
}
|
|
|
|
if (auto *VD = dyn_cast<VarDecl>(D)) {
|
|
if (hasVarIntroducer || (!hasIntroducer && !hasInitializerModifier))
|
|
addVarOverride(VD, Reason, dynamicLookupInfo);
|
|
return;
|
|
}
|
|
|
|
if (auto *SD = dyn_cast<SubscriptDecl>(D)) {
|
|
if (!hasIntroducer && !hasInitializerModifier)
|
|
addSubscriptOverride(SD, Reason, dynamicLookupInfo);
|
|
}
|
|
|
|
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
|
|
if (!isa<ProtocolDecl>(CD->getDeclContext()))
|
|
return;
|
|
if (hasIntroducer || hasOverride || hasOverridabilityModifier ||
|
|
hasStaticOrClass)
|
|
return;
|
|
if (CD->isRequired() || CD->isDesignatedInit())
|
|
addConstructor(CD, Reason, dynamicLookupInfo);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void addDesignatedInitializers(NominalTypeDecl *NTD) {
|
|
if (hasFuncIntroducer || hasVarIntroducer || hasTypealiasIntroducer ||
|
|
hasOverridabilityModifier || hasStaticOrClass)
|
|
return;
|
|
|
|
const auto *CD = dyn_cast<ClassDecl>(NTD);
|
|
if (!CD)
|
|
return;
|
|
if (!CD->hasSuperclass())
|
|
return;
|
|
CD = CD->getSuperclassDecl();
|
|
for (const auto *Member : CD->getMembers()) {
|
|
const auto *Constructor = dyn_cast<ConstructorDecl>(Member);
|
|
if (!Constructor)
|
|
continue;
|
|
if (Constructor->hasStubImplementation())
|
|
continue;
|
|
if (Constructor->isDesignatedInit())
|
|
addConstructor(Constructor, DeclVisibilityKind::MemberOfSuper, {});
|
|
}
|
|
}
|
|
|
|
void addAssociatedTypes(NominalTypeDecl *NTD) {
|
|
if (!hasTypealiasIntroducer &&
|
|
(hasFuncIntroducer || hasVarIntroducer || hasInitializerModifier ||
|
|
hasOverride || hasOverridabilityModifier || hasStaticOrClass))
|
|
return;
|
|
|
|
for (auto Conformance : NTD->getAllConformances()) {
|
|
auto Proto = Conformance->getProtocol();
|
|
if (!Proto->isAccessibleFrom(CurrDeclContext))
|
|
continue;
|
|
for (auto *ATD : Proto->getAssociatedTypeMembers()) {
|
|
// FIXME: Also exclude the type alias that has already been specified.
|
|
if (!Conformance->hasTypeWitness(ATD) ||
|
|
ATD->hasDefaultDefinitionType())
|
|
continue;
|
|
addTypeAlias(
|
|
ATD,
|
|
DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal,
|
|
{});
|
|
}
|
|
}
|
|
}
|
|
|
|
static StringRef getResultBuilderDocComment(
|
|
ResultBuilderBuildFunction function) {
|
|
switch (function) {
|
|
case ResultBuilderBuildFunction::BuildArray:
|
|
return "Enables support for..in loops in a result builder by "
|
|
"combining the results of all iterations into a single result";
|
|
|
|
case ResultBuilderBuildFunction::BuildBlock:
|
|
return "Required by every result builder to build combined results "
|
|
"from statement blocks";
|
|
|
|
case ResultBuilderBuildFunction::BuildEitherFirst:
|
|
return "With buildEither(second:), enables support for 'if-else' and "
|
|
"'switch' statements by folding conditional results into a single "
|
|
"result";
|
|
|
|
case ResultBuilderBuildFunction::BuildEitherSecond:
|
|
return "With buildEither(first:), enables support for 'if-else' and "
|
|
"'switch' statements by folding conditional results into a single "
|
|
"result";
|
|
|
|
case ResultBuilderBuildFunction::BuildExpression:
|
|
return "If declared, provides contextual type information for statement "
|
|
"expressions to translate them into partial results";
|
|
|
|
case ResultBuilderBuildFunction::BuildFinalResult:
|
|
return "If declared, this will be called on the partial result from the "
|
|
"outermost block statement to produce the final returned result";
|
|
|
|
case ResultBuilderBuildFunction::BuildLimitedAvailability:
|
|
return "If declared, this will be called on the partial result of "
|
|
"an 'if #available' block to allow the result builder to erase "
|
|
"type information";
|
|
|
|
case ResultBuilderBuildFunction::BuildOptional:
|
|
return "Enables support for `if` statements that do not have an `else`";
|
|
}
|
|
}
|
|
|
|
void addResultBuilderBuildCompletion(
|
|
NominalTypeDecl *builder, Type componentType,
|
|
ResultBuilderBuildFunction function) {
|
|
CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Pattern,
|
|
SemanticContextKind::CurrentNominal,
|
|
{});
|
|
Builder.setResultTypeNotApplicable();
|
|
|
|
if (!hasFuncIntroducer) {
|
|
if (!hasAccessModifier &&
|
|
builder->getFormalAccess() >= AccessLevel::Public)
|
|
Builder.addAccessControlKeyword(AccessLevel::Public);
|
|
|
|
if (!hasStaticOrClass)
|
|
Builder.addTextChunk("static ");
|
|
|
|
Builder.addTextChunk("func ");
|
|
}
|
|
|
|
std::string declStringWithoutFunc;
|
|
{
|
|
llvm::raw_string_ostream out(declStringWithoutFunc);
|
|
printResultBuilderBuildFunction(
|
|
builder, componentType, function, None, out);
|
|
}
|
|
Builder.addTextChunk(declStringWithoutFunc);
|
|
Builder.addBraceStmtWithCursor();
|
|
Builder.setBriefDocComment(getResultBuilderDocComment(function));
|
|
}
|
|
|
|
/// Add completions for the various "build" functions in a result builder.
|
|
void addResultBuilderBuildCompletions(NominalTypeDecl *builder) {
|
|
Type componentType = inferResultBuilderComponentType(builder);
|
|
|
|
addResultBuilderBuildCompletion(
|
|
builder, componentType, ResultBuilderBuildFunction::BuildBlock);
|
|
addResultBuilderBuildCompletion(
|
|
builder, componentType, ResultBuilderBuildFunction::BuildExpression);
|
|
addResultBuilderBuildCompletion(
|
|
builder, componentType, ResultBuilderBuildFunction::BuildOptional);
|
|
addResultBuilderBuildCompletion(
|
|
builder, componentType, ResultBuilderBuildFunction::BuildEitherFirst);
|
|
addResultBuilderBuildCompletion(
|
|
builder, componentType, ResultBuilderBuildFunction::BuildEitherSecond);
|
|
addResultBuilderBuildCompletion(
|
|
builder, componentType, ResultBuilderBuildFunction::BuildArray);
|
|
addResultBuilderBuildCompletion(
|
|
builder, componentType,
|
|
ResultBuilderBuildFunction::BuildLimitedAvailability);
|
|
addResultBuilderBuildCompletion(
|
|
builder, componentType, ResultBuilderBuildFunction::BuildFinalResult);
|
|
}
|
|
|
|
void getOverrideCompletions(SourceLoc Loc) {
|
|
if (!CurrDeclContext->isTypeContext())
|
|
return;
|
|
if (isa<ProtocolDecl>(CurrDeclContext))
|
|
return;
|
|
|
|
Type CurrTy = CurrDeclContext->getSelfTypeInContext();
|
|
auto *NTD = CurrDeclContext->getSelfNominalTypeDecl();
|
|
if (CurrTy && !CurrTy->is<ErrorType>()) {
|
|
// Look for overridable static members too.
|
|
Type Meta = MetatypeType::get(CurrTy);
|
|
lookupVisibleMemberDecls(*this, Meta, CurrDeclContext,
|
|
/*includeInstanceMembers=*/true,
|
|
/*includeDerivedRequirements*/true,
|
|
/*includeProtocolExtensionMembers*/false);
|
|
addDesignatedInitializers(NTD);
|
|
addAssociatedTypes(NTD);
|
|
}
|
|
|
|
if (NTD && NTD->getAttrs().hasAttribute<ResultBuilderAttr>()) {
|
|
addResultBuilderBuildCompletions(NTD);
|
|
}
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static void addSelectorModifierKeywords(CodeCompletionResultSink &sink) {
|
|
auto addKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
|
|
CodeCompletionResultBuilder Builder(sink, CodeCompletionResultKind::Keyword,
|
|
SemanticContextKind::None, {});
|
|
Builder.setKeywordKind(Kind);
|
|
Builder.addTextChunk(Name);
|
|
Builder.addCallParameterColon();
|
|
Builder.addSimpleTypedParameter("@objc property", /*IsVarArg=*/false);
|
|
};
|
|
|
|
addKeyword("getter", CodeCompletionKeywordKind::None);
|
|
addKeyword("setter", CodeCompletionKeywordKind::None);
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeDotExpr(CodeCompletionExpr *E,
|
|
SourceLoc DotLoc) {
|
|
assert(P.Tok.is(tok::code_complete));
|
|
|
|
// Don't produce any results in an enum element.
|
|
if (InEnumElementRawValue)
|
|
return;
|
|
|
|
Kind = CompletionKind::DotExpr;
|
|
if (ParseExprSelectorContext != ObjCSelectorContext::None) {
|
|
PreferFunctionReferencesToCalls = true;
|
|
CompleteExprSelectorContext = ParseExprSelectorContext;
|
|
}
|
|
|
|
ParsedExpr = E->getBase();
|
|
this->DotLoc = DotLoc;
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeStmtOrExpr(CodeCompletionExpr *E) {
|
|
assert(P.Tok.is(tok::code_complete));
|
|
Kind = CompletionKind::StmtOrExpr;
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completePostfixExprBeginning(CodeCompletionExpr *E) {
|
|
assert(P.Tok.is(tok::code_complete));
|
|
|
|
// Don't produce any results in an enum element.
|
|
if (InEnumElementRawValue)
|
|
return;
|
|
|
|
Kind = CompletionKind::PostfixExprBeginning;
|
|
if (ParseExprSelectorContext != ObjCSelectorContext::None) {
|
|
PreferFunctionReferencesToCalls = true;
|
|
CompleteExprSelectorContext = ParseExprSelectorContext;
|
|
if (CompleteExprSelectorContext == ObjCSelectorContext::MethodSelector) {
|
|
addSelectorModifierKeywords(CompletionContext.getResultSink());
|
|
}
|
|
}
|
|
|
|
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeForEachSequenceBeginning(
|
|
CodeCompletionExpr *E) {
|
|
assert(P.Tok.is(tok::code_complete));
|
|
Kind = CompletionKind::ForEachSequence;
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) {
|
|
assert(P.Tok.is(tok::code_complete));
|
|
|
|
// Don't produce any results in an enum element.
|
|
if (InEnumElementRawValue)
|
|
return;
|
|
|
|
HasSpace = hasSpace;
|
|
Kind = CompletionKind::PostfixExpr;
|
|
if (ParseExprSelectorContext != ObjCSelectorContext::None) {
|
|
PreferFunctionReferencesToCalls = true;
|
|
CompleteExprSelectorContext = ParseExprSelectorContext;
|
|
}
|
|
|
|
ParsedExpr = E;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completePostfixExprParen(Expr *E,
|
|
Expr *CodeCompletionE) {
|
|
assert(P.Tok.is(tok::code_complete));
|
|
|
|
// Don't produce any results in an enum element.
|
|
if (InEnumElementRawValue)
|
|
return;
|
|
|
|
Kind = CompletionKind::PostfixExprParen;
|
|
ParsedExpr = E;
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = static_cast<CodeCompletionExpr*>(CodeCompletionE);
|
|
|
|
ShouldCompleteCallPatternAfterParen = true;
|
|
if (CompletionContext.getCallPatternHeuristics()) {
|
|
// Lookahead one token to decide what kind of call completions to provide.
|
|
// When it appears that there is already code for the call present, just
|
|
// complete values and/or argument labels. Otherwise give the entire call
|
|
// pattern.
|
|
Token next = P.peekToken();
|
|
if (!next.isAtStartOfLine() && !next.is(tok::eof) && !next.is(tok::r_paren)) {
|
|
ShouldCompleteCallPatternAfterParen = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeExprKeyPath(KeyPathExpr *KPE,
|
|
SourceLoc DotLoc) {
|
|
Kind = (!KPE || KPE->isObjC()) ? CompletionKind::KeyPathExprObjC
|
|
: CompletionKind::KeyPathExprSwift;
|
|
ParsedExpr = KPE;
|
|
this->DotLoc = DotLoc;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completePoundAvailablePlatform() {
|
|
Kind = CompletionKind::PoundAvailablePlatform;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeTypeDeclResultBeginning() {
|
|
Kind = CompletionKind::TypeDeclResultBeginning;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeTypeSimpleBeginning() {
|
|
Kind = CompletionKind::TypeSimpleBeginning;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeDeclAttrParam(DeclAttrKind DK,
|
|
int Index) {
|
|
Kind = CompletionKind::AttributeDeclParen;
|
|
AttrKind = DK;
|
|
AttrParamIndex = Index;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeEffectsSpecifier(bool hasAsync,
|
|
bool hasThrows) {
|
|
Kind = CompletionKind::EffectsSpecifier;
|
|
CurDeclContext = P.CurDeclContext;
|
|
ParsedKeywords.clear();
|
|
if (hasAsync)
|
|
ParsedKeywords.emplace_back("async");
|
|
if (hasThrows)
|
|
ParsedKeywords.emplace_back("throws");
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeDeclAttrBeginning(
|
|
bool Sil, bool isIndependent) {
|
|
Kind = CompletionKind::AttributeBegin;
|
|
IsInSil = Sil;
|
|
CurDeclContext = P.CurDeclContext;
|
|
AttTargetIsIndependent = isIndependent;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeInPrecedenceGroup(SyntaxKind SK) {
|
|
assert(P.Tok.is(tok::code_complete));
|
|
|
|
SyntxKind = SK;
|
|
Kind = CompletionKind::PrecedenceGroup;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeTypeIdentifierWithDot(
|
|
IdentTypeRepr *ITR) {
|
|
if (!ITR) {
|
|
completeTypeSimpleBeginning();
|
|
return;
|
|
}
|
|
Kind = CompletionKind::TypeIdentifierWithDot;
|
|
ParsedTypeLoc = TypeLoc(ITR);
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeTypeIdentifierWithoutDot(
|
|
IdentTypeRepr *ITR) {
|
|
assert(ITR);
|
|
Kind = CompletionKind::TypeIdentifierWithoutDot;
|
|
ParsedTypeLoc = TypeLoc(ITR);
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeCaseStmtKeyword() {
|
|
Kind = CompletionKind::CaseStmtKeyword;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeCaseStmtBeginning(CodeCompletionExpr *E) {
|
|
assert(!InEnumElementRawValue);
|
|
|
|
Kind = CompletionKind::CaseStmtBeginning;
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeImportDecl(
|
|
ImportPath::Builder &Path) {
|
|
Kind = CompletionKind::Import;
|
|
CurDeclContext = P.CurDeclContext;
|
|
DotLoc = Path.empty() ? SourceLoc() : Path.back().Loc;
|
|
if (DotLoc.isInvalid())
|
|
return;
|
|
auto Importer = static_cast<ClangImporter *>(CurDeclContext->getASTContext().
|
|
getClangModuleLoader());
|
|
std::vector<std::string> SubNames;
|
|
Importer->collectSubModuleNames(Path.get().getModulePath(false), SubNames);
|
|
ASTContext &Ctx = CurDeclContext->getASTContext();
|
|
Path.push_back(Identifier());
|
|
for (StringRef Sub : SubNames) {
|
|
Path.back().Item = Ctx.getIdentifier(Sub);
|
|
SubModuleNameVisibilityPairs.push_back(
|
|
std::make_pair(Sub.str(),
|
|
Ctx.getLoadedModule(Path.get().getModulePath(false))));
|
|
}
|
|
Path.pop_back();
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeUnresolvedMember(CodeCompletionExpr *E,
|
|
SourceLoc DotLoc) {
|
|
Kind = CompletionKind::UnresolvedMember;
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
this->DotLoc = DotLoc;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeCallArg(CodeCompletionExpr *E,
|
|
bool isFirst) {
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
Kind = CompletionKind::CallArg;
|
|
|
|
ShouldCompleteCallPatternAfterParen = false;
|
|
if (isFirst) {
|
|
ShouldCompleteCallPatternAfterParen = true;
|
|
if (CompletionContext.getCallPatternHeuristics()) {
|
|
// Lookahead one token to decide what kind of call completions to provide.
|
|
// When it appears that there is already code for the call present, just
|
|
// complete values and/or argument labels. Otherwise give the entire call
|
|
// pattern.
|
|
Token next = P.peekToken();
|
|
if (!next.isAtStartOfLine() && !next.is(tok::eof) && !next.is(tok::r_paren)) {
|
|
ShouldCompleteCallPatternAfterParen = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeLabeledTrailingClosure(
|
|
CodeCompletionExpr *E, bool isAtStartOfLine) {
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
Kind = CompletionKind::LabeledTrailingClosure;
|
|
IsAtStartOfLine = isAtStartOfLine;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeReturnStmt(CodeCompletionExpr *E) {
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
Kind = CompletionKind::ReturnStmtExpr;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeYieldStmt(CodeCompletionExpr *E,
|
|
Optional<unsigned> index) {
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
// TODO: use a different completion kind when completing without an index
|
|
// in a multiple-value context.
|
|
Kind = CompletionKind::YieldStmtExpr;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeAfterPoundExpr(
|
|
CodeCompletionExpr *E, Optional<StmtKind> ParentKind) {
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
Kind = CompletionKind::AfterPoundExpr;
|
|
ParentStmtKind = ParentKind;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeAfterPoundDirective() {
|
|
CurDeclContext = P.CurDeclContext;
|
|
Kind = CompletionKind::AfterPoundDirective;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completePlatformCondition() {
|
|
CurDeclContext = P.CurDeclContext;
|
|
Kind = CompletionKind::PlatformConditon;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeAfterIfStmt(bool hasElse) {
|
|
CurDeclContext = P.CurDeclContext;
|
|
if (hasElse) {
|
|
Kind = CompletionKind::AfterIfStmtElse;
|
|
} else {
|
|
Kind = CompletionKind::StmtOrExpr;
|
|
}
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeGenericRequirement() {
|
|
CurDeclContext = P.CurDeclContext;
|
|
Kind = CompletionKind::GenericRequirement;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeNominalMemberBeginning(
|
|
SmallVectorImpl<StringRef> &Keywords, SourceLoc introducerLoc) {
|
|
assert(!InEnumElementRawValue);
|
|
this->introducerLoc = introducerLoc;
|
|
ParsedKeywords.clear();
|
|
ParsedKeywords.append(Keywords.begin(), Keywords.end());
|
|
Kind = CompletionKind::NominalMemberBeginning;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeAccessorBeginning(
|
|
CodeCompletionExpr *E) {
|
|
Kind = CompletionKind::AccessorBeginning;
|
|
CurDeclContext = P.CurDeclContext;
|
|
CodeCompleteTokenExpr = E;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeStmtLabel(StmtKind ParentKind) {
|
|
CurDeclContext = P.CurDeclContext;
|
|
Kind = CompletionKind::StmtLabel;
|
|
ParentStmtKind = ParentKind;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeForEachPatternBeginning(
|
|
bool hasTry, bool hasAwait) {
|
|
CurDeclContext = P.CurDeclContext;
|
|
Kind = CompletionKind::ForEachPatternBeginning;
|
|
ParsedKeywords.clear();
|
|
if (hasTry)
|
|
ParsedKeywords.emplace_back("try");
|
|
if (hasAwait)
|
|
ParsedKeywords.emplace_back("await");
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeTypeAttrBeginning() {
|
|
CurDeclContext = P.CurDeclContext;
|
|
Kind = CompletionKind::TypeAttrBeginning;
|
|
}
|
|
|
|
static bool isDynamicLookup(Type T) {
|
|
return T->getRValueType()->isAnyObject();
|
|
}
|
|
|
|
static bool isClangSubModule(ModuleDecl *TheModule) {
|
|
if (auto ClangMod = TheModule->findUnderlyingClangModule())
|
|
return ClangMod->isSubModule();
|
|
return false;
|
|
}
|
|
|
|
static void addKeyword(CodeCompletionResultSink &Sink, StringRef Name,
|
|
CodeCompletionKeywordKind Kind,
|
|
StringRef TypeAnnotation = "",
|
|
CodeCompletionFlair Flair = {}) {
|
|
CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Keyword,
|
|
SemanticContextKind::None, {});
|
|
Builder.setKeywordKind(Kind);
|
|
Builder.addKeyword(Name);
|
|
Builder.addFlair(Flair);
|
|
if (!TypeAnnotation.empty())
|
|
Builder.addTypeAnnotation(TypeAnnotation);
|
|
Builder.setResultTypeNotApplicable();
|
|
}
|
|
|
|
static void addDeclKeywords(CodeCompletionResultSink &Sink, DeclContext *DC,
|
|
bool IsConcurrencyEnabled,
|
|
bool IsDistributedEnabled) {
|
|
auto isTypeDeclIntroducer = [](CodeCompletionKeywordKind Kind,
|
|
Optional<DeclAttrKind> DAK) -> bool {
|
|
switch (Kind) {
|
|
case CodeCompletionKeywordKind::kw_protocol:
|
|
case CodeCompletionKeywordKind::kw_class:
|
|
case CodeCompletionKeywordKind::kw_struct:
|
|
case CodeCompletionKeywordKind::kw_enum:
|
|
case CodeCompletionKeywordKind::kw_extension:
|
|
return true;
|
|
case CodeCompletionKeywordKind::None:
|
|
if (DAK && *DAK == DeclAttrKind::DAK_Actor) {
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
};
|
|
auto isTopLevelOnlyDeclIntroducer = [](CodeCompletionKeywordKind Kind,
|
|
Optional<DeclAttrKind> DAK) -> bool {
|
|
switch (Kind) {
|
|
case CodeCompletionKeywordKind::kw_operator:
|
|
case CodeCompletionKeywordKind::kw_precedencegroup:
|
|
case CodeCompletionKeywordKind::kw_import:
|
|
case CodeCompletionKeywordKind::kw_protocol:
|
|
case CodeCompletionKeywordKind::kw_extension:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
};
|
|
|
|
auto getFlair = [&](CodeCompletionKeywordKind Kind,
|
|
Optional<DeclAttrKind> DAK) -> CodeCompletionFlair {
|
|
if (isCodeCompletionAtTopLevelOfLibraryFile(DC)) {
|
|
// Type decls are common in library file top-level.
|
|
if (isTypeDeclIntroducer(Kind, DAK))
|
|
return CodeCompletionFlairBit::CommonKeywordAtCurrentPosition;
|
|
}
|
|
if (isa<ProtocolDecl>(DC)) {
|
|
// Protocols cannot have nested type decls (other than 'typealias').
|
|
if (isTypeDeclIntroducer(Kind, DAK))
|
|
return CodeCompletionFlairBit::RareKeywordAtCurrentPosition;
|
|
}
|
|
if (DC->isTypeContext()) {
|
|
// Top-level only decls are invalid in type context.
|
|
if (isTopLevelOnlyDeclIntroducer(Kind, DAK))
|
|
return CodeCompletionFlairBit::RareKeywordAtCurrentPosition;
|
|
}
|
|
if (isCompletionDeclContextLocalContext(DC)) {
|
|
// Local type decl are valid, but not common.
|
|
if (isTypeDeclIntroducer(Kind, DAK))
|
|
return CodeCompletionFlairBit::RareKeywordAtCurrentPosition;
|
|
|
|
// Top-level only decls are invalid in function body.
|
|
if (isTopLevelOnlyDeclIntroducer(Kind, DAK))
|
|
return CodeCompletionFlairBit::RareKeywordAtCurrentPosition;
|
|
|
|
// 'init', 'deinit' and 'subscript' are invalid in function body.
|
|
// Access control modifiers are invalid in function body.
|
|
switch (Kind) {
|
|
case CodeCompletionKeywordKind::kw_init:
|
|
case CodeCompletionKeywordKind::kw_deinit:
|
|
case CodeCompletionKeywordKind::kw_subscript:
|
|
case CodeCompletionKeywordKind::kw_private:
|
|
case CodeCompletionKeywordKind::kw_fileprivate:
|
|
case CodeCompletionKeywordKind::kw_internal:
|
|
case CodeCompletionKeywordKind::kw_public:
|
|
case CodeCompletionKeywordKind::kw_static:
|
|
return CodeCompletionFlairBit::RareKeywordAtCurrentPosition;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// These modifiers are invalid for decls in function body.
|
|
if (DAK) {
|
|
switch (*DAK) {
|
|
case DeclAttrKind::DAK_Lazy:
|
|
case DeclAttrKind::DAK_Final:
|
|
case DeclAttrKind::DAK_Infix:
|
|
case DeclAttrKind::DAK_Frozen:
|
|
case DeclAttrKind::DAK_Prefix:
|
|
case DeclAttrKind::DAK_Postfix:
|
|
case DeclAttrKind::DAK_Dynamic:
|
|
case DeclAttrKind::DAK_Override:
|
|
case DeclAttrKind::DAK_Optional:
|
|
case DeclAttrKind::DAK_Required:
|
|
case DeclAttrKind::DAK_Convenience:
|
|
case DeclAttrKind::DAK_AccessControl:
|
|
case DeclAttrKind::DAK_Nonisolated:
|
|
return CodeCompletionFlairBit::RareKeywordAtCurrentPosition;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return None;
|
|
};
|
|
|
|
auto AddDeclKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind,
|
|
Optional<DeclAttrKind> DAK) {
|
|
if (Name == "let" || Name == "var") {
|
|
// Treat keywords that could be the start of a pattern specially.
|
|
return;
|
|
}
|
|
|
|
// FIXME: This should use canUseAttributeOnDecl.
|
|
|
|
// Remove user inaccessible keywords.
|
|
if (DAK.hasValue() && DeclAttribute::isUserInaccessible(*DAK))
|
|
return;
|
|
|
|
// Remove keywords only available when concurrency is enabled.
|
|
if (DAK.hasValue() && !IsConcurrencyEnabled &&
|
|
DeclAttribute::isConcurrencyOnly(*DAK))
|
|
return;
|
|
|
|
// Remove keywords only available when distributed is enabled.
|
|
if (DAK.hasValue() && !IsDistributedEnabled &&
|
|
DeclAttribute::isDistributedOnly(*DAK))
|
|
return;
|
|
|
|
addKeyword(Sink, Name, Kind, /*TypeAnnotation=*/"", getFlair(Kind, DAK));
|
|
};
|
|
|
|
#define DECL_KEYWORD(kw) \
|
|
AddDeclKeyword(#kw, CodeCompletionKeywordKind::kw_##kw, None);
|
|
#include "swift/Syntax/TokenKinds.def"
|
|
|
|
// Context-sensitive keywords.
|
|
auto AddCSKeyword = [&](StringRef Name, DeclAttrKind Kind) {
|
|
AddDeclKeyword(Name, CodeCompletionKeywordKind::None, Kind);
|
|
};
|
|
|
|
#define CONTEXTUAL_CASE(KW, CLASS) AddCSKeyword(#KW, DAK_##CLASS);
|
|
#define CONTEXTUAL_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS)
|
|
#define CONTEXTUAL_DECL_ATTR_ALIAS(KW, CLASS) CONTEXTUAL_CASE(KW, CLASS)
|
|
#define CONTEXTUAL_SIMPLE_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS)
|
|
#include <swift/AST/Attr.def>
|
|
#undef CONTEXTUAL_CASE
|
|
}
|
|
|
|
static void addStmtKeywords(CodeCompletionResultSink &Sink, DeclContext *DC,
|
|
bool MaybeFuncBody) {
|
|
CodeCompletionFlair flair;
|
|
// Starting a statement at top-level in non-script files is invalid.
|
|
if (isCodeCompletionAtTopLevelOfLibraryFile(DC)) {
|
|
flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope;
|
|
}
|
|
|
|
auto AddStmtKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
|
|
if (!MaybeFuncBody && Kind == CodeCompletionKeywordKind::kw_return)
|
|
return;
|
|
addKeyword(Sink, Name, Kind, "", flair);
|
|
};
|
|
#define STMT_KEYWORD(kw) AddStmtKeyword(#kw, CodeCompletionKeywordKind::kw_##kw);
|
|
#include "swift/Syntax/TokenKinds.def"
|
|
}
|
|
|
|
static void addCaseStmtKeywords(CodeCompletionResultSink &Sink) {
|
|
addKeyword(Sink, "case", CodeCompletionKeywordKind::kw_case);
|
|
addKeyword(Sink, "default", CodeCompletionKeywordKind::kw_default);
|
|
}
|
|
|
|
static void addLetVarKeywords(CodeCompletionResultSink &Sink) {
|
|
addKeyword(Sink, "let", CodeCompletionKeywordKind::kw_let);
|
|
addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var);
|
|
}
|
|
|
|
static void addAccessorKeywords(CodeCompletionResultSink &Sink) {
|
|
addKeyword(Sink, "get", CodeCompletionKeywordKind::None);
|
|
addKeyword(Sink, "set", CodeCompletionKeywordKind::None);
|
|
}
|
|
|
|
static void addObserverKeywords(CodeCompletionResultSink &Sink) {
|
|
addKeyword(Sink, "willSet", CodeCompletionKeywordKind::None);
|
|
addKeyword(Sink, "didSet", CodeCompletionKeywordKind::None);
|
|
}
|
|
|
|
static void addExprKeywords(CodeCompletionResultSink &Sink, DeclContext *DC) {
|
|
// Expression is invalid at top-level of non-script files.
|
|
CodeCompletionFlair flair;
|
|
if (isCodeCompletionAtTopLevelOfLibraryFile(DC)) {
|
|
flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope;
|
|
}
|
|
|
|
// Expr keywords.
|
|
addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try, "", flair);
|
|
addKeyword(Sink, "try!", CodeCompletionKeywordKind::kw_try, "", flair);
|
|
addKeyword(Sink, "try?", CodeCompletionKeywordKind::kw_try, "", flair);
|
|
addKeyword(Sink, "await", CodeCompletionKeywordKind::None, "", flair);
|
|
}
|
|
|
|
static void addOpaqueTypeKeyword(CodeCompletionResultSink &Sink) {
|
|
addKeyword(Sink, "some", CodeCompletionKeywordKind::None, "some");
|
|
}
|
|
|
|
static void addAnyTypeKeyword(CodeCompletionResultSink &Sink, Type T) {
|
|
CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Keyword,
|
|
SemanticContextKind::None, {});
|
|
Builder.setKeywordKind(CodeCompletionKeywordKind::None);
|
|
Builder.addKeyword("Any");
|
|
Builder.addTypeAnnotation(T, PrintOptions());
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
|
|
bool MaybeFuncBody) {
|
|
switch (Kind) {
|
|
case CompletionKind::None:
|
|
case CompletionKind::DotExpr:
|
|
case CompletionKind::AttributeDeclParen:
|
|
case CompletionKind::AttributeBegin:
|
|
case CompletionKind::PoundAvailablePlatform:
|
|
case CompletionKind::Import:
|
|
case CompletionKind::UnresolvedMember:
|
|
case CompletionKind::LabeledTrailingClosure:
|
|
case CompletionKind::AfterPoundExpr:
|
|
case CompletionKind::AfterPoundDirective:
|
|
case CompletionKind::PlatformConditon:
|
|
case CompletionKind::GenericRequirement:
|
|
case CompletionKind::KeyPathExprObjC:
|
|
case CompletionKind::KeyPathExprSwift:
|
|
case CompletionKind::PrecedenceGroup:
|
|
case CompletionKind::StmtLabel:
|
|
case CompletionKind::TypeAttrBeginning:
|
|
break;
|
|
|
|
case CompletionKind::EffectsSpecifier: {
|
|
if (!llvm::is_contained(ParsedKeywords, "async"))
|
|
addKeyword(Sink, "async", CodeCompletionKeywordKind::None);
|
|
if (!llvm::is_contained(ParsedKeywords, "throws"))
|
|
addKeyword(Sink, "throws", CodeCompletionKeywordKind::kw_throws);
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::AccessorBeginning: {
|
|
// TODO: Omit already declared or mutally exclusive accessors.
|
|
// E.g. If 'get' is already declared, emit 'set' only.
|
|
addAccessorKeywords(Sink);
|
|
|
|
// Only 'var' for non-protocol context can have 'willSet' and 'didSet'.
|
|
assert(ParsedDecl);
|
|
VarDecl *var = dyn_cast<VarDecl>(ParsedDecl);
|
|
if (auto accessor = dyn_cast<AccessorDecl>(ParsedDecl))
|
|
var = dyn_cast<VarDecl>(accessor->getStorage());
|
|
if (var && !var->getDeclContext()->getSelfProtocolDecl())
|
|
addObserverKeywords(Sink);
|
|
|
|
if (!isa<AccessorDecl>(ParsedDecl))
|
|
break;
|
|
|
|
MaybeFuncBody = true;
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case CompletionKind::StmtOrExpr:
|
|
addDeclKeywords(Sink, CurDeclContext,
|
|
Context.LangOpts.EnableExperimentalConcurrency,
|
|
Context.LangOpts.EnableExperimentalDistributed);
|
|
addStmtKeywords(Sink, CurDeclContext, MaybeFuncBody);
|
|
LLVM_FALLTHROUGH;
|
|
case CompletionKind::ReturnStmtExpr:
|
|
case CompletionKind::YieldStmtExpr:
|
|
case CompletionKind::PostfixExprBeginning:
|
|
case CompletionKind::ForEachSequence:
|
|
addSuperKeyword(Sink);
|
|
addLetVarKeywords(Sink);
|
|
addExprKeywords(Sink, CurDeclContext);
|
|
addAnyTypeKeyword(Sink, CurDeclContext->getASTContext().TheAnyType);
|
|
break;
|
|
|
|
case CompletionKind::CallArg:
|
|
case CompletionKind::PostfixExprParen:
|
|
// Note that we don't add keywords here as the completion might be for
|
|
// an argument list pattern. We instead add keywords later in
|
|
// CodeCompletionCallbacksImpl::doneParsing when we know we're not
|
|
// completing for a argument list pattern.
|
|
break;
|
|
|
|
case CompletionKind::CaseStmtKeyword:
|
|
addCaseStmtKeywords(Sink);
|
|
break;
|
|
|
|
case CompletionKind::PostfixExpr:
|
|
case CompletionKind::CaseStmtBeginning:
|
|
case CompletionKind::TypeIdentifierWithDot:
|
|
case CompletionKind::TypeIdentifierWithoutDot:
|
|
break;
|
|
|
|
case CompletionKind::TypeDeclResultBeginning: {
|
|
auto DC = CurDeclContext;
|
|
if (ParsedDecl && ParsedDecl == CurDeclContext->getAsDecl())
|
|
DC = ParsedDecl->getDeclContext();
|
|
if (!isa<ProtocolDecl>(DC))
|
|
if (DC->isTypeContext() || isa_and_nonnull<FuncDecl>(ParsedDecl))
|
|
addOpaqueTypeKeyword(Sink);
|
|
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case CompletionKind::TypeSimpleBeginning:
|
|
addAnyTypeKeyword(Sink, CurDeclContext->getASTContext().TheAnyType);
|
|
break;
|
|
|
|
case CompletionKind::NominalMemberBeginning: {
|
|
bool HasDeclIntroducer = llvm::find_if(ParsedKeywords,
|
|
[this](const StringRef kw) {
|
|
return llvm::StringSwitch<bool>(kw)
|
|
.Case("associatedtype", true)
|
|
.Case("class", !CurDeclContext || !isa<ClassDecl>(CurDeclContext))
|
|
.Case("deinit", true)
|
|
.Case("enum", true)
|
|
.Case("extension", true)
|
|
.Case("func", true)
|
|
.Case("import", true)
|
|
.Case("init", true)
|
|
.Case("let", true)
|
|
.Case("operator", true)
|
|
.Case("precedencegroup", true)
|
|
.Case("protocol", true)
|
|
.Case("struct", true)
|
|
.Case("subscript", true)
|
|
.Case("typealias", true)
|
|
.Case("var", true)
|
|
.Default(false);
|
|
}) != ParsedKeywords.end();
|
|
if (!HasDeclIntroducer) {
|
|
addDeclKeywords(Sink, CurDeclContext,
|
|
Context.LangOpts.EnableExperimentalConcurrency,
|
|
Context.LangOpts.EnableExperimentalDistributed);
|
|
addLetVarKeywords(Sink);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::AfterIfStmtElse:
|
|
addKeyword(Sink, "if", CodeCompletionKeywordKind::kw_if);
|
|
break;
|
|
case CompletionKind::ForEachPatternBeginning:
|
|
if (!llvm::is_contained(ParsedKeywords, "try"))
|
|
addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try);
|
|
if (!llvm::is_contained(ParsedKeywords, "await"))
|
|
addKeyword(Sink, "await", CodeCompletionKeywordKind::None);
|
|
addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var);
|
|
addKeyword(Sink, "case", CodeCompletionKeywordKind::kw_case);
|
|
}
|
|
}
|
|
|
|
static void addPoundDirectives(CodeCompletionResultSink &Sink) {
|
|
auto addWithName =
|
|
[&](StringRef name, CodeCompletionKeywordKind K,
|
|
llvm::function_ref<void(CodeCompletionResultBuilder &)> consumer =
|
|
nullptr) {
|
|
CodeCompletionResultBuilder Builder(Sink,
|
|
CodeCompletionResultKind::Keyword,
|
|
SemanticContextKind::None, {});
|
|
Builder.addBaseName(name);
|
|
Builder.setKeywordKind(K);
|
|
if (consumer)
|
|
consumer(Builder);
|
|
};
|
|
|
|
addWithName("sourceLocation", CodeCompletionKeywordKind::pound_sourceLocation,
|
|
[&] (CodeCompletionResultBuilder &Builder) {
|
|
Builder.addLeftParen();
|
|
Builder.addTextChunk("file");
|
|
Builder.addCallParameterColon();
|
|
Builder.addSimpleTypedParameter("String");
|
|
Builder.addComma();
|
|
Builder.addTextChunk("line");
|
|
Builder.addCallParameterColon();
|
|
Builder.addSimpleTypedParameter("Int");
|
|
Builder.addRightParen();
|
|
});
|
|
addWithName("warning", CodeCompletionKeywordKind::pound_warning,
|
|
[&] (CodeCompletionResultBuilder &Builder) {
|
|
Builder.addLeftParen();
|
|
Builder.addTextChunk("\"");
|
|
Builder.addSimpleNamedParameter("message");
|
|
Builder.addTextChunk("\"");
|
|
Builder.addRightParen();
|
|
});
|
|
addWithName("error", CodeCompletionKeywordKind::pound_error,
|
|
[&] (CodeCompletionResultBuilder &Builder) {
|
|
Builder.addLeftParen();
|
|
Builder.addTextChunk("\"");
|
|
Builder.addSimpleNamedParameter("message");
|
|
Builder.addTextChunk("\"");
|
|
Builder.addRightParen();
|
|
});
|
|
|
|
addWithName("if ", CodeCompletionKeywordKind::pound_if,
|
|
[&] (CodeCompletionResultBuilder &Builder) {
|
|
Builder.addSimpleNamedParameter("condition");
|
|
});
|
|
|
|
// FIXME: These directives are only valid in conditional completion block.
|
|
addWithName("elseif ", CodeCompletionKeywordKind::pound_elseif,
|
|
[&] (CodeCompletionResultBuilder &Builder) {
|
|
Builder.addSimpleNamedParameter("condition");
|
|
});
|
|
addWithName("else", CodeCompletionKeywordKind::pound_else);
|
|
addWithName("endif", CodeCompletionKeywordKind::pound_endif);
|
|
}
|
|
|
|
/// Add platform conditions used in '#if' and '#elseif' directives.
|
|
static void addPlatformConditions(CodeCompletionResultSink &Sink) {
|
|
auto addWithName =
|
|
[&](StringRef Name,
|
|
llvm::function_ref<void(CodeCompletionResultBuilder & Builder)>
|
|
consumer) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink, CodeCompletionResultKind::Pattern,
|
|
// FIXME: SemanticContextKind::CurrentModule is not correct.
|
|
// Use 'None' (and fix prioritization) or introduce a new context.
|
|
SemanticContextKind::CurrentModule, {});
|
|
Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific);
|
|
Builder.addBaseName(Name);
|
|
Builder.addLeftParen();
|
|
consumer(Builder);
|
|
Builder.addRightParen();
|
|
};
|
|
|
|
addWithName("os", [](CodeCompletionResultBuilder &Builder) {
|
|
Builder.addSimpleNamedParameter("name");
|
|
});
|
|
addWithName("arch", [](CodeCompletionResultBuilder &Builder) {
|
|
Builder.addSimpleNamedParameter("name");
|
|
});
|
|
addWithName("canImport", [](CodeCompletionResultBuilder &Builder) {
|
|
Builder.addSimpleNamedParameter("module");
|
|
});
|
|
addWithName("targetEnvironment", [](CodeCompletionResultBuilder &Builder) {
|
|
Builder.addTextChunk("simulator");
|
|
});
|
|
addWithName("swift", [](CodeCompletionResultBuilder &Builder) {
|
|
Builder.addTextChunk(">=");
|
|
Builder.addSimpleNamedParameter("version");
|
|
});
|
|
addWithName("swift", [](CodeCompletionResultBuilder &Builder) {
|
|
Builder.addTextChunk("<");
|
|
Builder.addSimpleNamedParameter("version");
|
|
});
|
|
addWithName("compiler", [](CodeCompletionResultBuilder &Builder) {
|
|
Builder.addTextChunk(">=");
|
|
Builder.addSimpleNamedParameter("version");
|
|
});
|
|
addWithName("compiler", [](CodeCompletionResultBuilder &Builder) {
|
|
Builder.addTextChunk("<");
|
|
Builder.addSimpleNamedParameter("version");
|
|
});
|
|
|
|
addKeyword(Sink, "true", CodeCompletionKeywordKind::kw_true, "Bool");
|
|
addKeyword(Sink, "false", CodeCompletionKeywordKind::kw_false, "Bool");
|
|
}
|
|
|
|
/// Add flags specified by '-D' to completion results.
|
|
static void addConditionalCompilationFlags(ASTContext &Ctx,
|
|
CodeCompletionResultSink &Sink) {
|
|
for (auto Flag : Ctx.LangOpts.getCustomConditionalCompilationFlags()) {
|
|
// TODO: Should we filter out some flags?
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink, CodeCompletionResultKind::Keyword,
|
|
// FIXME: SemanticContextKind::CurrentModule is not correct.
|
|
// Use 'None' (and fix prioritization) or introduce a new context.
|
|
SemanticContextKind::CurrentModule, {});
|
|
Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific);
|
|
Builder.addTextChunk(Flag);
|
|
}
|
|
}
|
|
|
|
/// Add flairs to the each item in \p results .
|
|
///
|
|
/// If \p Sink is passed, the pointer of the each result may be replaced with a
|
|
/// pointer to the new item allocated in \p Sink.
|
|
/// If \p Sink is nullptr, the pointee of each result may be modified in place.
|
|
static void postProcessResults(MutableArrayRef<CodeCompletionResult *> results,
|
|
CompletionKind Kind, DeclContext *DC,
|
|
CodeCompletionResultSink *Sink) {
|
|
for (CodeCompletionResult *&result : results) {
|
|
bool modified = false;
|
|
auto flair = result->getFlair();
|
|
|
|
// Starting a statement with a protocol name is not common. So protocol
|
|
// names at non-type name position are "rare".
|
|
if (result->getKind() == CodeCompletionResultKind::Declaration &&
|
|
result->getAssociatedDeclKind() == CodeCompletionDeclKind::Protocol &&
|
|
Kind != CompletionKind::TypeSimpleBeginning &&
|
|
Kind != CompletionKind::TypeIdentifierWithoutDot &&
|
|
Kind != CompletionKind::TypeIdentifierWithDot &&
|
|
Kind != CompletionKind::TypeDeclResultBeginning &&
|
|
Kind != CompletionKind::GenericRequirement) {
|
|
flair |= CodeCompletionFlairBit::RareTypeAtCurrentPosition;
|
|
modified = true;
|
|
}
|
|
|
|
// Starting a statement at top-level in non-script files is invalid.
|
|
if (Kind == CompletionKind::StmtOrExpr &&
|
|
result->getKind() == CodeCompletionResultKind::Declaration &&
|
|
isCodeCompletionAtTopLevelOfLibraryFile(DC)) {
|
|
flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope;
|
|
modified = true;
|
|
}
|
|
|
|
if (!modified)
|
|
continue;
|
|
|
|
if (Sink) {
|
|
// Replace the result with a new result with the flair.
|
|
result = result->withFlair(flair, *Sink);
|
|
} else {
|
|
// 'Sink' == nullptr means the result is modifiable in place.
|
|
result->setFlair(flair);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void deliverCompletionResults(CodeCompletionContext &CompletionContext,
|
|
CompletionLookup &Lookup,
|
|
DeclContext *DC,
|
|
CodeCompletionConsumer &Consumer) {
|
|
auto &SF = *DC->getParentSourceFile();
|
|
llvm::SmallPtrSet<Identifier, 8> seenModuleNames;
|
|
std::vector<RequestedCachedModule> RequestedModules;
|
|
|
|
SmallPtrSet<ModuleDecl *, 4> explictlyImportedModules;
|
|
{
|
|
// Collect modules directly imported in this SourceFile.
|
|
SmallVector<ImportedModule, 4> directImport;
|
|
SF.getImportedModules(directImport,
|
|
{ModuleDecl::ImportFilterKind::Default,
|
|
ModuleDecl::ImportFilterKind::ImplementationOnly});
|
|
for (auto import : directImport)
|
|
explictlyImportedModules.insert(import.importedModule);
|
|
|
|
// Exclude modules implicitly imported in the current module.
|
|
auto implicitImports = SF.getParentModule()->getImplicitImports();
|
|
for (auto import : implicitImports.imports)
|
|
explictlyImportedModules.erase(import.module.importedModule);
|
|
|
|
// Consider the current module "explicit".
|
|
explictlyImportedModules.insert(SF.getParentModule());
|
|
}
|
|
|
|
for (auto &Request: Lookup.RequestedCachedResults) {
|
|
llvm::DenseSet<CodeCompletionCache::Key> ImportsSeen;
|
|
auto handleImport = [&](ImportedModule Import) {
|
|
ModuleDecl *TheModule = Import.importedModule;
|
|
ImportPath::Access Path = Import.accessPath;
|
|
if (TheModule->getFiles().empty())
|
|
return;
|
|
|
|
// Clang submodules are ignored and there's no lookup cost involved,
|
|
// so just ignore them and don't put the empty results in the cache
|
|
// because putting a lot of objects in the cache will push out
|
|
// other lookups.
|
|
if (isClangSubModule(TheModule))
|
|
return;
|
|
|
|
std::vector<std::string> AccessPath;
|
|
for (auto Piece : Path) {
|
|
AccessPath.push_back(std::string(Piece.Item));
|
|
}
|
|
|
|
StringRef ModuleFilename = TheModule->getModuleFilename();
|
|
// ModuleFilename can be empty if something strange happened during
|
|
// module loading, for example, the module file is corrupted.
|
|
if (!ModuleFilename.empty()) {
|
|
CodeCompletionCache::Key K{
|
|
ModuleFilename.str(),
|
|
std::string(TheModule->getName()),
|
|
AccessPath,
|
|
Request.NeedLeadingDot,
|
|
SF.hasTestableOrPrivateImport(
|
|
AccessLevel::Internal, TheModule,
|
|
SourceFile::ImportQueryKind::TestableOnly),
|
|
SF.hasTestableOrPrivateImport(
|
|
AccessLevel::Internal, TheModule,
|
|
SourceFile::ImportQueryKind::PrivateOnly),
|
|
CompletionContext.getAddInitsToTopLevel(),
|
|
CompletionContext.addCallWithNoDefaultArgs(),
|
|
CompletionContext.getAnnotateResult()};
|
|
|
|
using PairType = llvm::DenseSet<swift::ide::CodeCompletionCache::Key,
|
|
llvm::DenseMapInfo<CodeCompletionCache::Key>>::iterator;
|
|
std::pair<PairType, bool> Result = ImportsSeen.insert(K);
|
|
if (!Result.second)
|
|
return; // already handled.
|
|
RequestedModules.push_back({std::move(K), TheModule,
|
|
Request.OnlyTypes, Request.OnlyPrecedenceGroups});
|
|
|
|
auto TheModuleName = TheModule->getName();
|
|
if (Request.IncludeModuleQualifier &&
|
|
(!Lookup.isHiddenModuleName(TheModuleName) ||
|
|
explictlyImportedModules.contains(TheModule)) &&
|
|
seenModuleNames.insert(TheModuleName).second)
|
|
Lookup.addModuleName(TheModule);
|
|
}
|
|
};
|
|
|
|
if (Request.TheModule) {
|
|
// FIXME: actually check imports.
|
|
for (auto Import : namelookup::getAllImports(Request.TheModule)) {
|
|
handleImport(Import);
|
|
}
|
|
} else {
|
|
// Add results from current module.
|
|
Lookup.getToplevelCompletions(Request.OnlyTypes);
|
|
|
|
// Add the qualifying module name
|
|
auto curModule = SF.getParentModule();
|
|
if (Request.IncludeModuleQualifier &&
|
|
seenModuleNames.insert(curModule->getName()).second)
|
|
Lookup.addModuleName(curModule);
|
|
|
|
// Add results for all imported modules.
|
|
SmallVector<ImportedModule, 4> Imports;
|
|
SF.getImportedModules(
|
|
Imports, {ModuleDecl::ImportFilterKind::Exported,
|
|
ModuleDecl::ImportFilterKind::Default,
|
|
ModuleDecl::ImportFilterKind::ImplementationOnly});
|
|
|
|
for (auto Imported : Imports) {
|
|
for (auto Import : namelookup::getAllImports(Imported.importedModule))
|
|
handleImport(Import);
|
|
}
|
|
}
|
|
}
|
|
Lookup.RequestedCachedResults.clear();
|
|
CompletionContext.typeContextKind = Lookup.typeContextKind();
|
|
|
|
postProcessResults(CompletionContext.getResultSink().Results,
|
|
CompletionContext.CodeCompletionKind, DC,
|
|
/*Sink=*/nullptr);
|
|
|
|
Consumer.handleResultsAndModules(CompletionContext, RequestedModules, DC);
|
|
}
|
|
|
|
void deliverUnresolvedMemberResults(
|
|
ArrayRef<UnresolvedMemberTypeCheckCompletionCallback::ExprResult> Results,
|
|
ArrayRef<Type> EnumPatternTypes, DeclContext *DC, SourceLoc DotLoc,
|
|
ide::CodeCompletionContext &CompletionCtx,
|
|
CodeCompletionConsumer &Consumer) {
|
|
ASTContext &Ctx = DC->getASTContext();
|
|
CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC,
|
|
&CompletionCtx);
|
|
|
|
assert(DotLoc.isValid());
|
|
Lookup.setHaveDot(DotLoc);
|
|
Lookup.shouldCheckForDuplicates(Results.size() + EnumPatternTypes.size() > 1);
|
|
|
|
// Get the canonical versions of the top-level types
|
|
SmallPtrSet<CanType, 4> originalTypes;
|
|
for (auto &Result: Results)
|
|
originalTypes.insert(Result.ExpectedTy->getCanonicalType());
|
|
|
|
for (auto &Result: Results) {
|
|
Lookup.setExpectedTypes({Result.ExpectedTy},
|
|
Result.IsImplicitSingleExpressionReturn,
|
|
/*expectsNonVoid*/true);
|
|
Lookup.setIdealExpectedType(Result.ExpectedTy);
|
|
|
|
// For optional types, also get members of the unwrapped type if it's not
|
|
// already equivalent to one of the top-level types. Handling it via the top
|
|
// level type and not here ensures we give the correct type relation
|
|
// (identical, rather than convertible).
|
|
if (Result.ExpectedTy->getOptionalObjectType()) {
|
|
Type Unwrapped = Result.ExpectedTy->lookThroughAllOptionalTypes();
|
|
if (originalTypes.insert(Unwrapped->getCanonicalType()).second)
|
|
Lookup.getUnresolvedMemberCompletions(Unwrapped);
|
|
}
|
|
Lookup.getUnresolvedMemberCompletions(Result.ExpectedTy);
|
|
}
|
|
|
|
// Offer completions when interpreting the pattern match as an
|
|
// EnumElementPattern.
|
|
for (auto &Ty : EnumPatternTypes) {
|
|
Lookup.setExpectedTypes({Ty}, /*IsImplicitSingleExpressionReturn=*/false,
|
|
/*expectsNonVoid=*/true);
|
|
Lookup.setIdealExpectedType(Ty);
|
|
|
|
// We can pattern match MyEnum against Optional<MyEnum>
|
|
if (Ty->getOptionalObjectType()) {
|
|
Type Unwrapped = Ty->lookThroughAllOptionalTypes();
|
|
Lookup.getEnumElementPatternCompletions(Unwrapped);
|
|
}
|
|
|
|
Lookup.getEnumElementPatternCompletions(Ty);
|
|
}
|
|
|
|
deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer);
|
|
}
|
|
|
|
void deliverKeyPathResults(
|
|
ArrayRef<KeyPathTypeCheckCompletionCallback::Result> Results,
|
|
DeclContext *DC, SourceLoc DotLoc,
|
|
ide::CodeCompletionContext &CompletionCtx,
|
|
CodeCompletionConsumer &Consumer) {
|
|
ASTContext &Ctx = DC->getASTContext();
|
|
CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC,
|
|
&CompletionCtx);
|
|
|
|
if (DotLoc.isValid()) {
|
|
Lookup.setHaveDot(DotLoc);
|
|
}
|
|
Lookup.shouldCheckForDuplicates(Results.size() > 1);
|
|
|
|
for (auto &Result : Results) {
|
|
Lookup.setIsSwiftKeyPathExpr(Result.OnRoot);
|
|
Lookup.getValueExprCompletions(Result.BaseType);
|
|
}
|
|
|
|
deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer);
|
|
}
|
|
|
|
void deliverDotExprResults(
|
|
ArrayRef<DotExprTypeCheckCompletionCallback::Result> Results,
|
|
Expr *BaseExpr, DeclContext *DC, SourceLoc DotLoc, bool IsInSelector,
|
|
ide::CodeCompletionContext &CompletionCtx,
|
|
CodeCompletionConsumer &Consumer) {
|
|
ASTContext &Ctx = DC->getASTContext();
|
|
CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC,
|
|
&CompletionCtx);
|
|
|
|
if (DotLoc.isValid())
|
|
Lookup.setHaveDot(DotLoc);
|
|
|
|
Lookup.setIsSuperRefExpr(isa<SuperRefExpr>(BaseExpr));
|
|
|
|
if (auto *DRE = dyn_cast<DeclRefExpr>(BaseExpr))
|
|
Lookup.setIsSelfRefExpr(DRE->getDecl()->getName() == Ctx.Id_self);
|
|
|
|
if (isa<BindOptionalExpr>(BaseExpr) || isa<ForceValueExpr>(BaseExpr))
|
|
Lookup.setIsUnwrappedOptional(true);
|
|
|
|
if (IsInSelector) {
|
|
Lookup.includeInstanceMembers();
|
|
Lookup.setPreferFunctionReferencesToCalls();
|
|
}
|
|
|
|
Lookup.shouldCheckForDuplicates(Results.size() > 1);
|
|
for (auto &Result: Results) {
|
|
Lookup.setIsStaticMetatype(Result.BaseIsStaticMetaType);
|
|
Lookup.getPostfixKeywordCompletions(Result.BaseTy, BaseExpr);
|
|
Lookup.setExpectedTypes(Result.ExpectedTypes,
|
|
Result.IsImplicitSingleExpressionReturn,
|
|
Result.ExpectsNonVoid);
|
|
if (isDynamicLookup(Result.BaseTy))
|
|
Lookup.setIsDynamicLookup();
|
|
Lookup.getValueExprCompletions(Result.BaseTy, Result.BaseDecl);
|
|
}
|
|
|
|
deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer);
|
|
}
|
|
|
|
bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
|
|
assert(ParsedExpr || CurDeclContext);
|
|
|
|
SourceLoc CompletionLoc = ParsedExpr
|
|
? ParsedExpr->getLoc()
|
|
: CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc();
|
|
|
|
switch (Kind) {
|
|
case CompletionKind::DotExpr: {
|
|
assert(CodeCompleteTokenExpr);
|
|
assert(CurDeclContext);
|
|
|
|
DotExprTypeCheckCompletionCallback Lookup(CurDeclContext,
|
|
CodeCompleteTokenExpr);
|
|
llvm::SaveAndRestore<TypeCheckCompletionCallback*>
|
|
CompletionCollector(Context.CompletionCallback, &Lookup);
|
|
typeCheckContextAt(CurDeclContext, CompletionLoc);
|
|
|
|
// This (hopefully) only happens in cases where the expression isn't
|
|
// typechecked during normal compilation either (e.g. member completion in a
|
|
// switch case where there control expression is invalid). Having normal
|
|
// typechecking still resolve even these cases would be beneficial for
|
|
// tooling in general though.
|
|
if (!Lookup.gotCallback())
|
|
Lookup.fallbackTypeCheck();
|
|
|
|
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
|
|
|
|
Expr *CheckedBase = CodeCompleteTokenExpr->getBase();
|
|
deliverDotExprResults(Lookup.getResults(), CheckedBase, CurDeclContext,
|
|
DotLoc, isInsideObjCSelector(), CompletionContext,
|
|
Consumer);
|
|
return true;
|
|
}
|
|
case CompletionKind::UnresolvedMember: {
|
|
assert(CodeCompleteTokenExpr);
|
|
assert(CurDeclContext);
|
|
|
|
UnresolvedMemberTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr);
|
|
llvm::SaveAndRestore<TypeCheckCompletionCallback*>
|
|
CompletionCollector(Context.CompletionCallback, &Lookup);
|
|
typeCheckContextAt(CurDeclContext, CompletionLoc);
|
|
|
|
if (!Lookup.gotCallback())
|
|
Lookup.fallbackTypeCheck(CurDeclContext);
|
|
|
|
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
|
|
deliverUnresolvedMemberResults(Lookup.getExprResults(),
|
|
Lookup.getEnumPatternTypes(), CurDeclContext,
|
|
DotLoc, CompletionContext, Consumer);
|
|
return true;
|
|
}
|
|
case CompletionKind::KeyPathExprSwift: {
|
|
assert(CurDeclContext);
|
|
|
|
// CodeCompletionCallbacks::completeExprKeyPath takes a \c KeyPathExpr,
|
|
// so we can safely cast the \c ParsedExpr back to a \c KeyPathExpr.
|
|
auto KeyPath = cast<KeyPathExpr>(ParsedExpr);
|
|
KeyPathTypeCheckCompletionCallback Lookup(KeyPath);
|
|
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
|
|
Context.CompletionCallback, &Lookup);
|
|
typeCheckContextAt(CurDeclContext, CompletionLoc);
|
|
|
|
deliverKeyPathResults(Lookup.getResults(), CurDeclContext, DotLoc,
|
|
CompletionContext, Consumer);
|
|
return true;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Undoes the single-expression closure/function body transformation on the
|
|
// given DeclContext and its parent contexts if they have a single expression
|
|
// body that contains the code completion location.
|
|
//
|
|
// FIXME: Remove this once all expression position completions are migrated
|
|
// to work via TypeCheckCompletionCallback.
|
|
static void undoSingleExpressionReturn(DeclContext *DC) {
|
|
auto updateBody = [](BraceStmt *BS, ASTContext &Ctx) -> bool {
|
|
ASTNode LastElem = BS->getLastElement();
|
|
auto *RS = dyn_cast_or_null<ReturnStmt>(LastElem.dyn_cast<Stmt*>());
|
|
|
|
if (!RS || !RS->isImplicit())
|
|
return false;
|
|
|
|
BS->setLastElement(RS->getResult());
|
|
return true;
|
|
};
|
|
|
|
while (ClosureExpr *CE = dyn_cast_or_null<ClosureExpr>(DC)) {
|
|
if (CE->hasSingleExpressionBody()) {
|
|
if (updateBody(CE->getBody(), CE->getASTContext()))
|
|
CE->setBody(CE->getBody(), false);
|
|
}
|
|
DC = DC->getParent();
|
|
}
|
|
if (FuncDecl *FD = dyn_cast_or_null<FuncDecl>(DC)) {
|
|
if (FD->hasSingleExpressionBody()) {
|
|
if (updateBody(FD->getBody(), FD->getASTContext()))
|
|
FD->setHasSingleExpressionBody(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::doneParsing() {
|
|
CompletionContext.CodeCompletionKind = Kind;
|
|
|
|
if (Kind == CompletionKind::None) {
|
|
return;
|
|
}
|
|
|
|
bool MaybeFuncBody = true;
|
|
if (CurDeclContext) {
|
|
auto *CD = CurDeclContext->getLocalContext();
|
|
if (!CD || CD->getContextKind() == DeclContextKind::Initializer ||
|
|
CD->getContextKind() == DeclContextKind::TopLevelCodeDecl)
|
|
MaybeFuncBody = false;
|
|
}
|
|
|
|
if (auto *DC = dyn_cast_or_null<DeclContext>(ParsedDecl)) {
|
|
if (DC->isChildContextOf(CurDeclContext))
|
|
CurDeclContext = DC;
|
|
}
|
|
|
|
if (trySolverCompletion(MaybeFuncBody))
|
|
return;
|
|
|
|
undoSingleExpressionReturn(CurDeclContext);
|
|
typeCheckContextAt(
|
|
CurDeclContext,
|
|
ParsedExpr
|
|
? ParsedExpr->getLoc()
|
|
: CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc());
|
|
|
|
// Add keywords even if type checking fails completely.
|
|
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
|
|
|
|
Optional<Type> ExprType;
|
|
ConcreteDeclRef ReferencedDecl = nullptr;
|
|
if (ParsedExpr) {
|
|
if (auto *checkedExpr = findParsedExpr(CurDeclContext,
|
|
ParsedExpr->getSourceRange())) {
|
|
ParsedExpr = checkedExpr;
|
|
}
|
|
|
|
if (auto typechecked = typeCheckParsedExpr()) {
|
|
ExprType = typechecked->first;
|
|
ReferencedDecl = typechecked->second;
|
|
ParsedExpr->setType(*ExprType);
|
|
}
|
|
|
|
if (!ExprType && Kind != CompletionKind::PostfixExprParen &&
|
|
Kind != CompletionKind::CallArg &&
|
|
Kind != CompletionKind::KeyPathExprObjC)
|
|
return;
|
|
}
|
|
|
|
if (!ParsedTypeLoc.isNull() && !typecheckParsedType())
|
|
return;
|
|
|
|
CompletionLookup Lookup(CompletionContext.getResultSink(), P.Context,
|
|
CurDeclContext, &CompletionContext);
|
|
if (ExprType) {
|
|
Lookup.setIsStaticMetatype(ParsedExpr->isStaticallyDerivedMetatype());
|
|
}
|
|
if (auto *DRE = dyn_cast_or_null<DeclRefExpr>(ParsedExpr)) {
|
|
Lookup.setIsSelfRefExpr(DRE->getDecl()->getName() == Context.Id_self);
|
|
} else if (isa_and_nonnull<SuperRefExpr>(ParsedExpr)) {
|
|
Lookup.setIsSuperRefExpr();
|
|
}
|
|
|
|
if (isInsideObjCSelector())
|
|
Lookup.includeInstanceMembers();
|
|
if (PreferFunctionReferencesToCalls)
|
|
Lookup.setPreferFunctionReferencesToCalls();
|
|
|
|
auto DoPostfixExprBeginning = [&] (){
|
|
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
|
|
Lookup.getValueCompletionsInDeclContext(Loc);
|
|
Lookup.getSelfTypeCompletionInDeclContext(Loc, /*isForDeclResult=*/false);
|
|
};
|
|
|
|
switch (Kind) {
|
|
case CompletionKind::None:
|
|
case CompletionKind::DotExpr:
|
|
case CompletionKind::UnresolvedMember:
|
|
case CompletionKind::KeyPathExprSwift:
|
|
llvm_unreachable("should be already handled");
|
|
return;
|
|
|
|
case CompletionKind::StmtOrExpr:
|
|
case CompletionKind::ForEachSequence:
|
|
case CompletionKind::PostfixExprBeginning: {
|
|
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
|
|
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
|
|
ContextInfo.isImplicitSingleExpressionReturn());
|
|
DoPostfixExprBeginning();
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::PostfixExpr: {
|
|
Lookup.setHaveLeadingSpace(HasSpace);
|
|
if (isDynamicLookup(*ExprType))
|
|
Lookup.setIsDynamicLookup();
|
|
Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl());
|
|
/// We set the type of ParsedExpr explicitly above. But we don't want an
|
|
/// unresolved type in our AST when we type check again for operator
|
|
/// completions. Remove the type of the ParsedExpr and see if we can come up
|
|
/// with something more useful based on the the full sequence expression.
|
|
if (ParsedExpr->getType()->is<UnresolvedType>()) {
|
|
ParsedExpr->setType(nullptr);
|
|
}
|
|
Lookup.getOperatorCompletions(ParsedExpr, leadingSequenceExprs);
|
|
Lookup.getPostfixKeywordCompletions(*ExprType, ParsedExpr);
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::PostfixExprParen: {
|
|
Lookup.setHaveLParen(true);
|
|
|
|
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
|
|
|
|
if (ShouldCompleteCallPatternAfterParen) {
|
|
ExprContextInfo ParentContextInfo(CurDeclContext, ParsedExpr);
|
|
Lookup.setExpectedTypes(
|
|
ParentContextInfo.getPossibleTypes(),
|
|
ParentContextInfo.isImplicitSingleExpressionReturn());
|
|
if (!ContextInfo.getPossibleCallees().empty()) {
|
|
for (auto &typeAndDecl : ContextInfo.getPossibleCallees())
|
|
Lookup.tryFunctionCallCompletions(typeAndDecl.Type, typeAndDecl.Decl,
|
|
typeAndDecl.SemanticContext);
|
|
} else if (ExprType && ((*ExprType)->is<AnyFunctionType>() ||
|
|
(*ExprType)->is<AnyMetatypeType>())) {
|
|
Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl());
|
|
}
|
|
} else {
|
|
// Add argument labels, then fallthrough to get values.
|
|
Lookup.addCallArgumentCompletionResults(ContextInfo.getPossibleParams());
|
|
}
|
|
|
|
if (!Lookup.FoundFunctionCalls ||
|
|
(Lookup.FoundFunctionCalls &&
|
|
Lookup.FoundFunctionsWithoutFirstKeyword)) {
|
|
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
|
|
ContextInfo.isImplicitSingleExpressionReturn());
|
|
Lookup.setHaveLParen(false);
|
|
|
|
// Add any keywords that can be used in an argument expr position.
|
|
addSuperKeyword(CompletionContext.getResultSink());
|
|
addExprKeywords(CompletionContext.getResultSink(), CurDeclContext);
|
|
|
|
DoPostfixExprBeginning();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::KeyPathExprObjC: {
|
|
if (DotLoc.isValid())
|
|
Lookup.setHaveDot(DotLoc);
|
|
Lookup.setIsKeyPathExpr();
|
|
Lookup.includeInstanceMembers();
|
|
|
|
if (ExprType) {
|
|
if (isDynamicLookup(*ExprType))
|
|
Lookup.setIsDynamicLookup();
|
|
|
|
Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl());
|
|
} else {
|
|
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
|
|
Lookup.getValueCompletionsInDeclContext(Loc, KeyPathFilter,
|
|
/*LiteralCompletions=*/false);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::TypeDeclResultBeginning:
|
|
case CompletionKind::TypeSimpleBeginning: {
|
|
auto Loc = Context.SourceMgr.getCodeCompletionLoc();
|
|
Lookup.getTypeCompletionsInDeclContext(Loc);
|
|
Lookup.getSelfTypeCompletionInDeclContext(
|
|
Loc, Kind == CompletionKind::TypeDeclResultBeginning);
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::TypeIdentifierWithDot: {
|
|
Lookup.setHaveDot(SourceLoc());
|
|
Lookup.getTypeCompletions(ParsedTypeLoc.getType());
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::TypeIdentifierWithoutDot: {
|
|
Lookup.getTypeCompletions(ParsedTypeLoc.getType());
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::CaseStmtBeginning: {
|
|
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
|
|
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
|
|
ContextInfo.isImplicitSingleExpressionReturn());
|
|
Lookup.setIdealExpectedType(CodeCompleteTokenExpr->getType());
|
|
Lookup.getUnresolvedMemberCompletions(ContextInfo.getPossibleTypes());
|
|
DoPostfixExprBeginning();
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::NominalMemberBeginning: {
|
|
CompletionOverrideLookup OverrideLookup(CompletionContext.getResultSink(),
|
|
P.Context, CurDeclContext,
|
|
ParsedKeywords, introducerLoc);
|
|
OverrideLookup.getOverrideCompletions(SourceLoc());
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::AccessorBeginning: {
|
|
if (isa<AccessorDecl>(ParsedDecl)) {
|
|
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
|
|
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
|
|
ContextInfo.isImplicitSingleExpressionReturn());
|
|
DoPostfixExprBeginning();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::AttributeBegin: {
|
|
Lookup.getAttributeDeclCompletions(IsInSil, AttTargetDK);
|
|
|
|
// TypeName at attribute position after '@'.
|
|
// - VarDecl: Property Wrappers.
|
|
// - ParamDecl/VarDecl/FuncDecl: Function Builders.
|
|
if (!AttTargetDK || *AttTargetDK == DeclKind::Var ||
|
|
*AttTargetDK == DeclKind::Param || *AttTargetDK == DeclKind::Func)
|
|
Lookup.getTypeCompletionsInDeclContext(
|
|
P.Context.SourceMgr.getCodeCompletionLoc());
|
|
break;
|
|
}
|
|
case CompletionKind::AttributeDeclParen: {
|
|
Lookup.getAttributeDeclParamCompletions(AttrKind, AttrParamIndex);
|
|
break;
|
|
}
|
|
case CompletionKind::PoundAvailablePlatform: {
|
|
Lookup.getPoundAvailablePlatformCompletions();
|
|
break;
|
|
}
|
|
case CompletionKind::Import: {
|
|
if (DotLoc.isValid())
|
|
Lookup.addSubModuleNames(SubModuleNameVisibilityPairs);
|
|
else
|
|
Lookup.addImportModuleNames();
|
|
break;
|
|
}
|
|
case CompletionKind::CallArg: {
|
|
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
|
|
|
|
bool shouldPerformGlobalCompletion = true;
|
|
|
|
if (ShouldCompleteCallPatternAfterParen &&
|
|
!ContextInfo.getPossibleCallees().empty()) {
|
|
Lookup.setHaveLParen(true);
|
|
for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) {
|
|
auto apply = ContextInfo.getAnalyzedExpr();
|
|
if (isa_and_nonnull<SubscriptExpr>(apply)) {
|
|
Lookup.addSubscriptCallPattern(
|
|
typeAndDecl.Type,
|
|
dyn_cast_or_null<SubscriptDecl>(typeAndDecl.Decl),
|
|
typeAndDecl.SemanticContext);
|
|
} else {
|
|
Lookup.addFunctionCallPattern(
|
|
typeAndDecl.Type,
|
|
dyn_cast_or_null<AbstractFunctionDecl>(typeAndDecl.Decl),
|
|
typeAndDecl.SemanticContext);
|
|
}
|
|
}
|
|
Lookup.setHaveLParen(false);
|
|
|
|
shouldPerformGlobalCompletion =
|
|
!Lookup.FoundFunctionCalls ||
|
|
(Lookup.FoundFunctionCalls &&
|
|
Lookup.FoundFunctionsWithoutFirstKeyword);
|
|
} else if (!ContextInfo.getPossibleParams().empty()) {
|
|
auto params = ContextInfo.getPossibleParams();
|
|
Lookup.addCallArgumentCompletionResults(params);
|
|
|
|
shouldPerformGlobalCompletion = !ContextInfo.getPossibleTypes().empty();
|
|
// Fallback to global completion if the position is out of number. It's
|
|
// better than suggest nothing.
|
|
shouldPerformGlobalCompletion |= llvm::all_of(
|
|
params, [](const PossibleParamInfo &P) { return !P.Param; });
|
|
}
|
|
|
|
if (shouldPerformGlobalCompletion) {
|
|
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
|
|
ContextInfo.isImplicitSingleExpressionReturn());
|
|
|
|
// Add any keywords that can be used in an argument expr position.
|
|
addSuperKeyword(CompletionContext.getResultSink());
|
|
addExprKeywords(CompletionContext.getResultSink(), CurDeclContext);
|
|
|
|
DoPostfixExprBeginning();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::LabeledTrailingClosure: {
|
|
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
|
|
|
|
SmallVector<PossibleParamInfo, 2> params;
|
|
// Only complete function type parameters
|
|
llvm::copy_if(ContextInfo.getPossibleParams(), std::back_inserter(params),
|
|
[](const PossibleParamInfo &P) {
|
|
// nullptr indicates out of bounds.
|
|
if (!P.Param)
|
|
return true;
|
|
return P.Param->getPlainType()
|
|
->lookThroughAllOptionalTypes()
|
|
->is<AnyFunctionType>();
|
|
});
|
|
|
|
bool allRequired = false;
|
|
if (!params.empty()) {
|
|
Lookup.addCallArgumentCompletionResults(
|
|
params, /*isLabeledTrailingClosure=*/true);
|
|
allRequired = llvm::all_of(
|
|
params, [](const PossibleParamInfo &P) { return P.IsRequired; });
|
|
}
|
|
|
|
// If there're optional parameters, do global completion or member
|
|
// completion depending on the completion is happening at the start of line.
|
|
if (!allRequired) {
|
|
if (IsAtStartOfLine) {
|
|
// foo() {}
|
|
// <HERE>
|
|
|
|
auto &Sink = CompletionContext.getResultSink();
|
|
if (isa<Initializer>(CurDeclContext))
|
|
CurDeclContext = CurDeclContext->getParent();
|
|
|
|
if (CurDeclContext->isTypeContext()) {
|
|
// Override completion (CompletionKind::NominalMemberBeginning).
|
|
addDeclKeywords(Sink, CurDeclContext,
|
|
Context.LangOpts.EnableExperimentalConcurrency,
|
|
Context.LangOpts.EnableExperimentalDistributed);
|
|
addLetVarKeywords(Sink);
|
|
SmallVector<StringRef, 0> ParsedKeywords;
|
|
CompletionOverrideLookup OverrideLookup(Sink, Context, CurDeclContext,
|
|
ParsedKeywords, SourceLoc());
|
|
OverrideLookup.getOverrideCompletions(SourceLoc());
|
|
} else {
|
|
// Global completion (CompletionKind::PostfixExprBeginning).
|
|
addDeclKeywords(Sink, CurDeclContext,
|
|
Context.LangOpts.EnableExperimentalConcurrency,
|
|
Context.LangOpts.EnableExperimentalDistributed);
|
|
addStmtKeywords(Sink, CurDeclContext, MaybeFuncBody);
|
|
addSuperKeyword(Sink);
|
|
addLetVarKeywords(Sink);
|
|
addExprKeywords(Sink, CurDeclContext);
|
|
addAnyTypeKeyword(Sink, Context.TheAnyType);
|
|
DoPostfixExprBeginning();
|
|
}
|
|
} else {
|
|
// foo() {} <HERE>
|
|
// Member completion.
|
|
Expr *analyzedExpr = ContextInfo.getAnalyzedExpr();
|
|
if (!analyzedExpr)
|
|
break;
|
|
|
|
// Only if the completion token is the last token in the call.
|
|
if (analyzedExpr->getEndLoc() != CodeCompleteTokenExpr->getLoc())
|
|
break;
|
|
|
|
Type resultTy = analyzedExpr->getType();
|
|
// If the call expression doesn't have a type, fallback to:
|
|
if (!resultTy || resultTy->is<ErrorType>()) {
|
|
// 1) Try to type check removing CodeCompletionExpr from the call.
|
|
Expr *removedExpr = analyzedExpr;
|
|
removeCodeCompletionExpr(CurDeclContext->getASTContext(),
|
|
removedExpr);
|
|
ConcreteDeclRef referencedDecl;
|
|
auto optT = getTypeOfCompletionContextExpr(
|
|
CurDeclContext->getASTContext(), CurDeclContext,
|
|
CompletionTypeCheckKind::Normal, removedExpr, referencedDecl);
|
|
if (optT) {
|
|
resultTy = *optT;
|
|
analyzedExpr->setType(resultTy);
|
|
}
|
|
}
|
|
if (!resultTy || resultTy->is<ErrorType>()) {
|
|
// 2) Infer it from the possible callee info.
|
|
if (!ContextInfo.getPossibleCallees().empty()) {
|
|
auto calleeInfo = ContextInfo.getPossibleCallees()[0];
|
|
resultTy = calleeInfo.Type->getResult();
|
|
analyzedExpr->setType(resultTy);
|
|
}
|
|
}
|
|
if (!resultTy || resultTy->is<ErrorType>()) {
|
|
// 3) Give up providing postfix completions.
|
|
break;
|
|
}
|
|
|
|
auto &SM = CurDeclContext->getASTContext().SourceMgr;
|
|
auto leadingChar =
|
|
SM.extractText({SM.getCodeCompletionLoc().getAdvancedLoc(-1), 1});
|
|
Lookup.setHaveLeadingSpace(leadingChar.find_first_of(" \t\f\v") !=
|
|
StringRef::npos);
|
|
|
|
if (isDynamicLookup(resultTy))
|
|
Lookup.setIsDynamicLookup();
|
|
Lookup.getValueExprCompletions(resultTy, /*VD=*/nullptr);
|
|
Lookup.getOperatorCompletions(analyzedExpr, leadingSequenceExprs);
|
|
Lookup.getPostfixKeywordCompletions(resultTy, analyzedExpr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::ReturnStmtExpr : {
|
|
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
|
|
SmallVector<Type, 2> possibleReturnTypes;
|
|
collectPossibleReturnTypesFromContext(CurDeclContext, possibleReturnTypes);
|
|
Lookup.setExpectedTypes(possibleReturnTypes,
|
|
/*isImplicitSingleExpressionReturn*/ false);
|
|
Lookup.getValueCompletionsInDeclContext(Loc);
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::YieldStmtExpr: {
|
|
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
|
|
if (auto FD = dyn_cast<AccessorDecl>(CurDeclContext)) {
|
|
if (FD->isCoroutine()) {
|
|
// TODO: handle multi-value yields.
|
|
Lookup.setExpectedTypes(FD->getStorage()->getValueInterfaceType(),
|
|
/*isImplicitSingleExpressionReturn*/ false);
|
|
}
|
|
}
|
|
Lookup.getValueCompletionsInDeclContext(Loc);
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::AfterPoundExpr: {
|
|
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
|
|
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
|
|
ContextInfo.isImplicitSingleExpressionReturn());
|
|
|
|
Lookup.addPoundAvailable(ParentStmtKind);
|
|
Lookup.addPoundLiteralCompletions(/*needPound=*/false);
|
|
Lookup.addObjCPoundKeywordCompletions(/*needPound=*/false);
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::AfterPoundDirective: {
|
|
addPoundDirectives(CompletionContext.getResultSink());
|
|
// FIXME: Add pound expressions (e.g. '#selector()') if it's at statements
|
|
// position.
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::PlatformConditon: {
|
|
addPlatformConditions(CompletionContext.getResultSink());
|
|
addConditionalCompilationFlags(CurDeclContext->getASTContext(),
|
|
CompletionContext.getResultSink());
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::GenericRequirement: {
|
|
auto Loc = Context.SourceMgr.getCodeCompletionLoc();
|
|
Lookup.getGenericRequirementCompletions(CurDeclContext, Loc);
|
|
break;
|
|
}
|
|
case CompletionKind::PrecedenceGroup:
|
|
Lookup.getPrecedenceGroupCompletions(SyntxKind);
|
|
break;
|
|
case CompletionKind::StmtLabel: {
|
|
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
|
|
Lookup.getStmtLabelCompletions(Loc, ParentStmtKind == StmtKind::Continue);
|
|
break;
|
|
}
|
|
case CompletionKind::TypeAttrBeginning: {
|
|
Lookup.getTypeAttributeKeywordCompletions();
|
|
|
|
// Type names at attribute position after '@'.
|
|
Lookup.getTypeCompletionsInDeclContext(
|
|
P.Context.SourceMgr.getCodeCompletionLoc());
|
|
break;
|
|
|
|
}
|
|
case CompletionKind::AfterIfStmtElse:
|
|
case CompletionKind::CaseStmtKeyword:
|
|
case CompletionKind::EffectsSpecifier:
|
|
case CompletionKind::ForEachPatternBeginning:
|
|
// Handled earlier by keyword completions.
|
|
break;
|
|
}
|
|
|
|
deliverCompletionResults(CompletionContext, Lookup, CurDeclContext, Consumer);
|
|
}
|
|
|
|
namespace {
|
|
class CodeCompletionCallbacksFactoryImpl
|
|
: public CodeCompletionCallbacksFactory {
|
|
CodeCompletionContext &CompletionContext;
|
|
CodeCompletionConsumer &Consumer;
|
|
|
|
public:
|
|
CodeCompletionCallbacksFactoryImpl(CodeCompletionContext &CompletionContext,
|
|
CodeCompletionConsumer &Consumer)
|
|
: CompletionContext(CompletionContext), Consumer(Consumer) {}
|
|
|
|
CodeCompletionCallbacks *createCodeCompletionCallbacks(Parser &P) override {
|
|
return new CodeCompletionCallbacksImpl(P, CompletionContext, Consumer);
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
CodeCompletionCallbacksFactory *
|
|
swift::ide::makeCodeCompletionCallbacksFactory(
|
|
CodeCompletionContext &CompletionContext,
|
|
CodeCompletionConsumer &Consumer) {
|
|
return new CodeCompletionCallbacksFactoryImpl(CompletionContext, Consumer);
|
|
}
|
|
|
|
void swift::ide::lookupCodeCompletionResultsFromModule(
|
|
CodeCompletionResultSink &targetSink, const ModuleDecl *module,
|
|
ArrayRef<std::string> accessPath, bool needLeadingDot,
|
|
const SourceFile *SF) {
|
|
// Use the SourceFile as the decl context, to avoid decl context specific
|
|
// behaviors.
|
|
CompletionLookup Lookup(targetSink, module->getASTContext(), SF);
|
|
Lookup.lookupExternalModuleDecls(module, accessPath, needLeadingDot);
|
|
}
|
|
|
|
static MutableArrayRef<CodeCompletionResult *>
|
|
copyCodeCompletionResults(CodeCompletionResultSink &targetSink,
|
|
CodeCompletionCache::Value &source, bool onlyTypes,
|
|
bool onlyPrecedenceGroups) {
|
|
|
|
// We will be adding foreign results (from another sink) into TargetSink.
|
|
// TargetSink should have an owning pointer to the allocator that keeps the
|
|
// results alive.
|
|
targetSink.ForeignAllocators.push_back(source.Allocator);
|
|
auto startSize = targetSink.Results.size();
|
|
|
|
std::function<bool(const ContextFreeCodeCompletionResult *)>
|
|
shouldIncludeResult;
|
|
if (onlyTypes) {
|
|
shouldIncludeResult = [](const ContextFreeCodeCompletionResult *R) -> bool {
|
|
if (R->getKind() != CodeCompletionResultKind::Declaration)
|
|
return false;
|
|
switch (R->getAssociatedDeclKind()) {
|
|
case CodeCompletionDeclKind::Module:
|
|
case CodeCompletionDeclKind::Class:
|
|
case CodeCompletionDeclKind::Actor:
|
|
case CodeCompletionDeclKind::Struct:
|
|
case CodeCompletionDeclKind::Enum:
|
|
case CodeCompletionDeclKind::Protocol:
|
|
case CodeCompletionDeclKind::TypeAlias:
|
|
case CodeCompletionDeclKind::AssociatedType:
|
|
case CodeCompletionDeclKind::GenericTypeParam:
|
|
return true;
|
|
case CodeCompletionDeclKind::PrecedenceGroup:
|
|
case CodeCompletionDeclKind::EnumElement:
|
|
case CodeCompletionDeclKind::Constructor:
|
|
case CodeCompletionDeclKind::Destructor:
|
|
case CodeCompletionDeclKind::Subscript:
|
|
case CodeCompletionDeclKind::StaticMethod:
|
|
case CodeCompletionDeclKind::InstanceMethod:
|
|
case CodeCompletionDeclKind::PrefixOperatorFunction:
|
|
case CodeCompletionDeclKind::PostfixOperatorFunction:
|
|
case CodeCompletionDeclKind::InfixOperatorFunction:
|
|
case CodeCompletionDeclKind::FreeFunction:
|
|
case CodeCompletionDeclKind::StaticVar:
|
|
case CodeCompletionDeclKind::InstanceVar:
|
|
case CodeCompletionDeclKind::LocalVar:
|
|
case CodeCompletionDeclKind::GlobalVar:
|
|
return false;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled CodeCompletionDeclKind in switch.");
|
|
};
|
|
} else if (onlyPrecedenceGroups) {
|
|
shouldIncludeResult = [](const ContextFreeCodeCompletionResult *R) -> bool {
|
|
return R->getAssociatedDeclKind() ==
|
|
CodeCompletionDeclKind::PrecedenceGroup;
|
|
};
|
|
} else {
|
|
shouldIncludeResult = [](const ContextFreeCodeCompletionResult *R) -> bool {
|
|
return true;
|
|
};
|
|
}
|
|
for (auto contextFreeResult : source.Results) {
|
|
if (!shouldIncludeResult(contextFreeResult)) {
|
|
continue;
|
|
}
|
|
auto contextualResult = new (*targetSink.Allocator) CodeCompletionResult(
|
|
*contextFreeResult, SemanticContextKind::OtherModule,
|
|
CodeCompletionFlair(),
|
|
/*numBytesToErase=*/0, /*TypeContext=*/nullptr, /*DC=*/nullptr,
|
|
ContextualNotRecommendedReason::None,
|
|
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"");
|
|
targetSink.Results.push_back(contextualResult);
|
|
}
|
|
|
|
return llvm::makeMutableArrayRef(targetSink.Results.data() + startSize,
|
|
targetSink.Results.size() - startSize);
|
|
}
|
|
|
|
void SimpleCachingCodeCompletionConsumer::handleResultsAndModules(
|
|
CodeCompletionContext &context,
|
|
ArrayRef<RequestedCachedModule> requestedModules,
|
|
DeclContext *DC) {
|
|
|
|
// Use the current SourceFile as the DeclContext so that we can use it to
|
|
// perform qualified lookup, and to get the correct visibility for
|
|
// @testable imports. Also it cannot use 'DC' since it would apply decl
|
|
// context changes to cached results.
|
|
const SourceFile *SF = DC->getParentSourceFile();
|
|
|
|
for (auto &R : requestedModules) {
|
|
// FIXME(thread-safety): lock the whole AST context. We might load a
|
|
// module.
|
|
llvm::Optional<CodeCompletionCache::ValueRefCntPtr> V =
|
|
context.Cache.get(R.Key);
|
|
if (!V.hasValue()) {
|
|
// No cached results found. Fill the cache.
|
|
V = context.Cache.createValue();
|
|
// Temporary sink in which we gather the result. The cache value retains
|
|
// the sink's allocator.
|
|
CodeCompletionResultSink Sink;
|
|
Sink.annotateResult = context.getAnnotateResult();
|
|
Sink.addInitsToTopLevel = context.getAddInitsToTopLevel();
|
|
Sink.enableCallPatternHeuristics = context.getCallPatternHeuristics();
|
|
Sink.includeObjectLiterals = context.includeObjectLiterals();
|
|
Sink.addCallWithNoDefaultArgs = context.addCallWithNoDefaultArgs();
|
|
lookupCodeCompletionResultsFromModule(Sink, R.TheModule, R.Key.AccessPath,
|
|
R.Key.ResultsHaveLeadingDot, SF);
|
|
(*V)->Allocator = Sink.Allocator;
|
|
auto &CachedResults = (*V)->Results;
|
|
CachedResults.reserve(Sink.Results.size());
|
|
// Instead of copying the context free results out of the sink's allocator
|
|
// retain the sink's entire allocator (which also includes the contextual
|
|
// properities) and simply store pointers to the context free results that
|
|
// back the contextual results.
|
|
for (auto Result : Sink.Results) {
|
|
CachedResults.push_back(Result->getContextFreeResultPtr());
|
|
}
|
|
context.Cache.set(R.Key, *V);
|
|
}
|
|
assert(V.hasValue());
|
|
auto newItems = copyCodeCompletionResults(
|
|
context.getResultSink(), **V, R.OnlyTypes, R.OnlyPrecedenceGroups);
|
|
postProcessResults(newItems, context.CodeCompletionKind, DC,
|
|
&context.getResultSink());
|
|
}
|
|
|
|
handleResults(context);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ImportDepth
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
ImportDepth::ImportDepth(ASTContext &context,
|
|
const FrontendOptions &frontendOptions) {
|
|
llvm::DenseSet<ModuleDecl *> seen;
|
|
std::deque<std::pair<ModuleDecl *, uint8_t>> worklist;
|
|
|
|
StringRef mainModule = frontendOptions.ModuleName;
|
|
auto *main = context.getLoadedModule(context.getIdentifier(mainModule));
|
|
assert(main && "missing main module");
|
|
worklist.emplace_back(main, uint8_t(0));
|
|
|
|
// Imports from -import-name such as Playground auxiliary sources are treated
|
|
// specially by applying import depth 0.
|
|
llvm::StringSet<> auxImports;
|
|
for (const auto &pair : frontendOptions.getImplicitImportModuleNames())
|
|
auxImports.insert(pair.first);
|
|
|
|
// Private imports from this module.
|
|
// FIXME: only the private imports from the current source file.
|
|
// FIXME: ImportFilterKind::ShadowedByCrossImportOverlay?
|
|
SmallVector<ImportedModule, 16> mainImports;
|
|
main->getImportedModules(mainImports,
|
|
{ModuleDecl::ImportFilterKind::Default,
|
|
ModuleDecl::ImportFilterKind::ImplementationOnly});
|
|
for (auto &import : mainImports) {
|
|
uint8_t depth = 1;
|
|
if (auxImports.count(import.importedModule->getName().str()))
|
|
depth = 0;
|
|
worklist.emplace_back(import.importedModule, depth);
|
|
}
|
|
|
|
// Fill depths with BFS over module imports.
|
|
while (!worklist.empty()) {
|
|
ModuleDecl *module;
|
|
uint8_t depth;
|
|
std::tie(module, depth) = worklist.front();
|
|
worklist.pop_front();
|
|
|
|
if (!seen.insert(module).second)
|
|
continue;
|
|
|
|
// Insert new module:depth mapping.
|
|
const clang::Module *CM = module->findUnderlyingClangModule();
|
|
if (CM) {
|
|
depths[CM->getFullModuleName()] = depth;
|
|
} else {
|
|
depths[module->getName().str()] = depth;
|
|
}
|
|
|
|
// Add imports to the worklist.
|
|
SmallVector<ImportedModule, 16> imports;
|
|
module->getImportedModules(imports);
|
|
for (auto &import : imports) {
|
|
uint8_t next = std::max(depth, uint8_t(depth + 1)); // unsigned wrap
|
|
|
|
// Implicitly imported sub-modules get the same depth as their parent.
|
|
if (const clang::Module *CMI =
|
|
import.importedModule->findUnderlyingClangModule())
|
|
if (CM && CMI->isSubModuleOf(CM))
|
|
next = depth;
|
|
worklist.emplace_back(import.importedModule, next);
|
|
}
|
|
}
|
|
}
|