mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
3053 lines
99 KiB
C++
3053 lines
99 KiB
C++
//===- CodeCompletion.cpp - Code completion implementation ----------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/IDE/CodeCompletion.h"
|
|
#include "swift/IDE/Utils.h"
|
|
#include "swift/Basic/Cache.h"
|
|
#include "swift/Basic/Fallthrough.h"
|
|
#include "swift/AST/ASTPrinter.h"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/LazyResolver.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/AST/USRGeneration.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/ClangImporter/ClangModule.h"
|
|
#include "swift/Parse/CodeCompletionCallbacks.h"
|
|
#include "swift/Sema/CodeCompletionTypeChecking.h"
|
|
#include "swift/Subsystems.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/SmallSet.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
#include "CodeCompletionResultBuilder.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/Basic/Module.h"
|
|
#include "clang/Index/USRGeneration.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 != '#');
|
|
continue;
|
|
}
|
|
CleanFile += C;
|
|
}
|
|
return CleanFile;
|
|
}
|
|
|
|
namespace {
|
|
class StmtFinder : public ASTWalker {
|
|
SourceManager &SM;
|
|
SourceLoc Loc;
|
|
StmtKind Kind;
|
|
Stmt *Found = nullptr;
|
|
|
|
public:
|
|
StmtFinder(SourceManager &SM, SourceLoc Loc, StmtKind Kind)
|
|
: SM(SM), Loc(Loc), Kind(Kind) {}
|
|
|
|
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
|
|
if (SM.rangeContainsTokenLoc(S->getSourceRange(), Loc))
|
|
return { true, S };
|
|
else
|
|
return { false, S };
|
|
}
|
|
|
|
Stmt *walkToStmtPost(Stmt *S) override {
|
|
if (S->getKind() == Kind) {
|
|
Found = S;
|
|
return nullptr;
|
|
}
|
|
return S;
|
|
}
|
|
|
|
Stmt *getFoundStmt() const {
|
|
return Found;
|
|
}
|
|
};
|
|
} // unnamed namespace
|
|
|
|
static Stmt *findNearestStmt(const AbstractFunctionDecl *AFD, SourceLoc Loc,
|
|
StmtKind Kind) {
|
|
auto &SM = AFD->getASTContext().SourceMgr;
|
|
assert(SM.rangeContainsTokenLoc(AFD->getSourceRange(), Loc));
|
|
StmtFinder Finder(SM, Loc, Kind);
|
|
// FIXME(thread-safety): the walker is is mutating the AST.
|
|
const_cast<AbstractFunctionDecl *>(AFD)->walk(Finder);
|
|
return Finder.getFoundStmt();
|
|
}
|
|
|
|
CodeCompletionString::CodeCompletionString(ArrayRef<Chunk> Chunks) {
|
|
Chunk *TailChunks = reinterpret_cast<Chunk *>(this + 1);
|
|
std::copy(Chunks.begin(), Chunks.end(), TailChunks);
|
|
NumChunks = Chunks.size();
|
|
}
|
|
|
|
CodeCompletionString *CodeCompletionString::create(llvm::BumpPtrAllocator &Allocator, ArrayRef<Chunk> Chunks) {
|
|
void *CCSMem = Allocator.Allocate(sizeof(CodeCompletionString) +
|
|
Chunks.size() * sizeof(CodeCompletionString::Chunk),
|
|
llvm::alignOf<CodeCompletionString>());
|
|
return new (CCSMem) CodeCompletionString(Chunks);
|
|
}
|
|
|
|
void CodeCompletionString::print(raw_ostream &OS) const {
|
|
unsigned PrevNestingLevel = 0;
|
|
for (auto C : getChunks()) {
|
|
bool AnnotatedTextChunk = false;
|
|
if (C.getNestingLevel() < PrevNestingLevel) {
|
|
OS << "#}";
|
|
}
|
|
switch (C.getKind()) {
|
|
case Chunk::ChunkKind::AccessControlKeyword:
|
|
case Chunk::ChunkKind::DeclAttrKeyword:
|
|
case Chunk::ChunkKind::DeclAttrParamKeyword:
|
|
case Chunk::ChunkKind::OverrideKeyword:
|
|
case Chunk::ChunkKind::DeclIntroducer:
|
|
case Chunk::ChunkKind::Text:
|
|
case Chunk::ChunkKind::LeftParen:
|
|
case Chunk::ChunkKind::RightParen:
|
|
case Chunk::ChunkKind::LeftBracket:
|
|
case Chunk::ChunkKind::RightBracket:
|
|
case Chunk::ChunkKind::LeftAngle:
|
|
case Chunk::ChunkKind::RightAngle:
|
|
case Chunk::ChunkKind::Dot:
|
|
case Chunk::ChunkKind::Ellipsis:
|
|
case Chunk::ChunkKind::Comma:
|
|
case Chunk::ChunkKind::ExclamationMark:
|
|
case Chunk::ChunkKind::QuestionMark:
|
|
case Chunk::ChunkKind::Ampersand:
|
|
AnnotatedTextChunk = C.isAnnotation();
|
|
SWIFT_FALLTHROUGH;
|
|
case Chunk::ChunkKind::CallParameterName:
|
|
case Chunk::ChunkKind::CallParameterInternalName:
|
|
case Chunk::ChunkKind::CallParameterColon:
|
|
case Chunk::ChunkKind::DeclAttrParamEqual:
|
|
case Chunk::ChunkKind::CallParameterType:
|
|
case Chunk::ChunkKind::CallParameterClosureType:
|
|
case CodeCompletionString::Chunk::ChunkKind::GenericParameterName:
|
|
if (AnnotatedTextChunk)
|
|
OS << "['";
|
|
else if (C.getKind() == Chunk::ChunkKind::CallParameterInternalName)
|
|
OS << "(";
|
|
else if (C.getKind() == Chunk::ChunkKind::CallParameterClosureType)
|
|
OS << "##";
|
|
for (char Ch : C.getText()) {
|
|
if (Ch == '\n')
|
|
OS << "\\n";
|
|
else
|
|
OS << Ch;
|
|
}
|
|
if (AnnotatedTextChunk)
|
|
OS << "']";
|
|
else if (C.getKind() == Chunk::ChunkKind::CallParameterInternalName)
|
|
OS << ")";
|
|
break;
|
|
case Chunk::ChunkKind::OptionalBegin:
|
|
case Chunk::ChunkKind::CallParameterBegin:
|
|
case CodeCompletionString::Chunk::ChunkKind::GenericParameterBegin:
|
|
OS << "{#";
|
|
break;
|
|
case Chunk::ChunkKind::DynamicLookupMethodCallTail:
|
|
case Chunk::ChunkKind::OptionalMethodCallTail:
|
|
OS << C.getText();
|
|
break;
|
|
case Chunk::ChunkKind::TypeAnnotation:
|
|
OS << "[#";
|
|
OS << C.getText();
|
|
OS << "#]";
|
|
break;
|
|
case Chunk::ChunkKind::BraceStmtWithCursor:
|
|
OS << " {|}";
|
|
break;
|
|
}
|
|
PrevNestingLevel = C.getNestingLevel();
|
|
}
|
|
while (PrevNestingLevel > 0) {
|
|
OS << "#}";
|
|
PrevNestingLevel--;
|
|
}
|
|
}
|
|
|
|
void CodeCompletionString::dump() const {
|
|
print(llvm::errs());
|
|
}
|
|
|
|
CodeCompletionDeclKind
|
|
CodeCompletionResult::getCodeCompletionDeclKind(const Decl *D) {
|
|
switch (D->getKind()) {
|
|
case DeclKind::Import:
|
|
case DeclKind::Extension:
|
|
case DeclKind::PatternBinding:
|
|
case DeclKind::EnumCase:
|
|
case DeclKind::TopLevelCode:
|
|
case DeclKind::InfixOperator:
|
|
case DeclKind::PrefixOperator:
|
|
case DeclKind::PostfixOperator:
|
|
case DeclKind::IfConfig:
|
|
llvm_unreachable("not expecting such a declaration result");
|
|
|
|
case DeclKind::TypeAlias:
|
|
case DeclKind::AssociatedType:
|
|
return CodeCompletionDeclKind::TypeAlias;
|
|
case DeclKind::GenericTypeParam:
|
|
return CodeCompletionDeclKind::GenericTypeParam;
|
|
case DeclKind::Enum:
|
|
return CodeCompletionDeclKind::Enum;
|
|
case DeclKind::Struct:
|
|
return CodeCompletionDeclKind::Struct;
|
|
case DeclKind::Class:
|
|
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::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())
|
|
return CodeCompletionDeclKind::OperatorFunction;
|
|
return CodeCompletionDeclKind::FreeFunction;
|
|
}
|
|
case DeclKind::EnumElement:
|
|
return CodeCompletionDeclKind::EnumElement;
|
|
case DeclKind::Subscript:
|
|
return CodeCompletionDeclKind::Subscript;
|
|
}
|
|
llvm_unreachable("invalid DeclKind");
|
|
}
|
|
|
|
void CodeCompletionResult::print(raw_ostream &OS) const {
|
|
llvm::SmallString<64> Prefix;
|
|
switch (getKind()) {
|
|
case ResultKind::Declaration:
|
|
Prefix.append("Decl");
|
|
switch (getAssociatedDeclKind()) {
|
|
case CodeCompletionDeclKind::Class:
|
|
Prefix.append("[Class]");
|
|
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::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::OperatorFunction:
|
|
Prefix.append("[OperatorFunction]");
|
|
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;
|
|
}
|
|
break;
|
|
case ResultKind::Keyword:
|
|
Prefix.append("Keyword");
|
|
break;
|
|
case ResultKind::Pattern:
|
|
Prefix.append("Pattern");
|
|
break;
|
|
}
|
|
Prefix.append("/");
|
|
switch (getSemanticContext()) {
|
|
case SemanticContextKind::None:
|
|
Prefix.append("None");
|
|
break;
|
|
case SemanticContextKind::ExpressionSpecific:
|
|
Prefix.append("ExprSpecific");
|
|
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 (!ModuleName.empty())
|
|
Prefix.append((Twine("[") + ModuleName + "]").str());
|
|
break;
|
|
}
|
|
if (NotRecommended)
|
|
Prefix.append("/NotRecommended");
|
|
if (NumBytesToErase != 0) {
|
|
Prefix.append("/Erase[");
|
|
Prefix.append(Twine(NumBytesToErase).str());
|
|
Prefix.append("]");
|
|
}
|
|
Prefix.append(": ");
|
|
while (Prefix.size() < 36) {
|
|
Prefix.append(" ");
|
|
}
|
|
OS << Prefix;
|
|
CompletionString->print(OS);
|
|
}
|
|
|
|
void CodeCompletionResult::dump() const {
|
|
print(llvm::errs());
|
|
}
|
|
|
|
static StringRef copyString(llvm::BumpPtrAllocator &Allocator,
|
|
StringRef Str) {
|
|
char *Mem = Allocator.Allocate<char>(Str.size());
|
|
std::copy(Str.begin(), Str.end(), Mem);
|
|
return StringRef(Mem, Str.size());
|
|
}
|
|
|
|
static ArrayRef<StringRef> copyStringArray(llvm::BumpPtrAllocator &Allocator,
|
|
ArrayRef<StringRef> Arr) {
|
|
StringRef *Buff = Allocator.Allocate<StringRef>(Arr.size());
|
|
std::copy(Arr.begin(), Arr.end(), Buff);
|
|
return llvm::makeArrayRef(Buff, Arr.size());
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind Kind, StringRef Text) {
|
|
addChunkWithTextNoCopy(Kind, copyString(*Sink.Allocator, Text));
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::setAssociatedDecl(const Decl *D) {
|
|
assert(Kind == CodeCompletionResult::ResultKind::Declaration);
|
|
AssociatedDecl = D;
|
|
|
|
if (auto *ClangD = D->getClangDecl())
|
|
CurrentModule = ClangD->getOwningModule();
|
|
// FIXME: macros
|
|
// FIXME: imported header module
|
|
|
|
if (!CurrentModule)
|
|
CurrentModule = D->getModuleContext();
|
|
}
|
|
|
|
StringRef CodeCompletionContext::copyString(StringRef Str) {
|
|
return ::copyString(*CurrentResults.Allocator, Str);
|
|
}
|
|
|
|
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 (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<StringRef> copyAssociatedUSRs(llvm::BumpPtrAllocator &Allocator,
|
|
const Decl *D) {
|
|
llvm::SmallVector<StringRef, 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 = printDeclUSR(OVD, OS);
|
|
}
|
|
} else if (auto *OND = OD.dyn_cast<const clang::NamedDecl*>()) {
|
|
Ignored = clang::index::generateUSRForDecl(OND, SS);
|
|
}
|
|
|
|
if (!Ignored)
|
|
USRs.push_back(copyString(Allocator, SS));
|
|
});
|
|
|
|
if (!USRs.empty())
|
|
return copyStringArray(Allocator, USRs);
|
|
|
|
return ArrayRef<StringRef>();
|
|
}
|
|
|
|
CodeCompletionResult *CodeCompletionResultBuilder::takeResult() {
|
|
auto *CCS = CodeCompletionString::create(*Sink.Allocator, Chunks);
|
|
|
|
switch (Kind) {
|
|
case CodeCompletionResult::ResultKind::Declaration: {
|
|
StringRef BriefComment;
|
|
auto MaybeClangNode = AssociatedDecl->getClangNode();
|
|
if (MaybeClangNode) {
|
|
if (auto *D = MaybeClangNode.getAsDecl()) {
|
|
const auto &ClangContext = D->getASTContext();
|
|
if (const clang::RawComment *RC =
|
|
ClangContext.getRawCommentForAnyRedecl(D))
|
|
BriefComment = RC->getBriefText(ClangContext);
|
|
}
|
|
} else {
|
|
BriefComment = AssociatedDecl->getBriefComment();
|
|
}
|
|
|
|
StringRef ModuleName;
|
|
if (CurrentModule) {
|
|
if (Sink.LastModule.first == CurrentModule.getOpaqueValue()) {
|
|
ModuleName = Sink.LastModule.second;
|
|
} else {
|
|
if (auto *C = CurrentModule.dyn_cast<const clang::Module *>()) {
|
|
ModuleName = copyString(*Sink.Allocator, C->getFullModuleName());
|
|
} else {
|
|
ModuleName = copyString(
|
|
*Sink.Allocator,
|
|
CurrentModule.get<const swift::Module *>()->getName().str());
|
|
}
|
|
Sink.LastModule.first = CurrentModule.getOpaqueValue();
|
|
Sink.LastModule.second = ModuleName;
|
|
}
|
|
}
|
|
|
|
return new (*Sink.Allocator) CodeCompletionResult(
|
|
SemanticContext, NumBytesToErase, CCS, AssociatedDecl, ModuleName,
|
|
/*NotRecommended=*/false, copyString(*Sink.Allocator, BriefComment),
|
|
copyAssociatedUSRs(*Sink.Allocator, AssociatedDecl));
|
|
}
|
|
|
|
case CodeCompletionResult::ResultKind::Keyword:
|
|
case CodeCompletionResult::ResultKind::Pattern:
|
|
return new (*Sink.Allocator)
|
|
CodeCompletionResult(Kind, SemanticContext, NumBytesToErase, CCS);
|
|
}
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::finishResult() {
|
|
Sink.Results.push_back(takeResult());
|
|
}
|
|
|
|
namespace swift {
|
|
namespace ide {
|
|
struct CodeCompletionCacheImpl {
|
|
using Key = CodeCompletionCache::Key;
|
|
using Value = CodeCompletionCache::Value;
|
|
using ValueRefCntPtr = CodeCompletionCache::ValueRefCntPtr;
|
|
sys::Cache<Key, ValueRefCntPtr> TheCache{"swift.libIDE.CodeCompletionCache"};
|
|
};
|
|
} // end namespace ide
|
|
} // end namespace swift
|
|
|
|
namespace llvm {
|
|
template<>
|
|
struct DenseMapInfo<swift::ide::CodeCompletionCacheImpl::Key> {
|
|
using KeyTy = swift::ide::CodeCompletionCacheImpl::Key;
|
|
static inline KeyTy getEmptyKey() {
|
|
return KeyTy{"", "", {}, false, false};
|
|
}
|
|
static inline KeyTy getTombstoneKey() {
|
|
return KeyTy{"", "", {}, true, false};
|
|
}
|
|
static unsigned getHashValue(const KeyTy &Val) {
|
|
size_t H = 0;
|
|
H ^= std::hash<std::string>()(Val.ModuleFilename);
|
|
H ^= std::hash<std::string>()(Val.ModuleName);
|
|
for (auto Piece : Val.AccessPath)
|
|
H ^= std::hash<std::string>()(Piece);
|
|
H ^= std::hash<bool>()(Val.ResultsHaveLeadingDot);
|
|
H ^= std::hash<bool>()(Val.ForTestableLookup);
|
|
return static_cast<unsigned>(H);
|
|
}
|
|
static bool isEqual(const KeyTy &LHS, const KeyTy &RHS) {
|
|
return LHS == RHS;
|
|
}
|
|
};
|
|
} // namespace llvm
|
|
|
|
namespace swift {
|
|
namespace sys {
|
|
template<>
|
|
struct CacheValueCostInfo<swift::ide::CodeCompletionCacheImpl::Value> {
|
|
static size_t
|
|
getCost(const swift::ide::CodeCompletionCacheImpl::Value &V) {
|
|
return V.Sink.Allocator->getTotalMemory();
|
|
}
|
|
};
|
|
} // namespace sys
|
|
} // namespace swift
|
|
|
|
void CodeCompletionCache::getResults(
|
|
const Key &K, CodeCompletionResultSink &TargetSink, bool OnlyTypes,
|
|
const Module *TheModule,
|
|
std::function<ValueRefCntPtr(CodeCompletionCache &, Key, const Module *)>
|
|
FillCacheCallback) {
|
|
// FIXME(thread-safety): lock the whole AST context. We might load a module.
|
|
llvm::Optional<ValueRefCntPtr> V = get(K);
|
|
if (!V.hasValue()) {
|
|
// No cached results found. Fill the cache.
|
|
V = FillCacheCallback(*this, K, TheModule);
|
|
}
|
|
assert(V.hasValue());
|
|
auto &SourceSink = V.getValue()->Sink;
|
|
|
|
copyCodeCompletionResults(TargetSink, SourceSink, OnlyTypes);
|
|
}
|
|
|
|
CodeCompletionCache::ValueRefCntPtr CodeCompletionCache::createValue() {
|
|
return ValueRefCntPtr(new Value);
|
|
}
|
|
|
|
Optional<CodeCompletionCache::ValueRefCntPtr>
|
|
CodeCompletionCache::get(const Key &K) {
|
|
auto &TheCache = Impl->TheCache;
|
|
llvm::Optional<ValueRefCntPtr> V = TheCache.get(K);
|
|
if (V) {
|
|
// Check whether V is up to date.
|
|
llvm::sys::fs::file_status ModuleStatus;
|
|
if (llvm::sys::fs::status(K.ModuleFilename, ModuleStatus) ||
|
|
V.getValue()->ModuleModificationTime !=
|
|
ModuleStatus.getLastModificationTime()) {
|
|
// Cache is stale.
|
|
V = None;
|
|
TheCache.remove(K);
|
|
}
|
|
}
|
|
return V;
|
|
}
|
|
|
|
void CodeCompletionCache::set(const Key &K, ValueRefCntPtr V) {
|
|
{
|
|
assert(!K.ModuleFilename.empty());
|
|
|
|
llvm::sys::fs::file_status ModuleStatus;
|
|
if (llvm::sys::fs::status(K.ModuleFilename, ModuleStatus)) {
|
|
V->ModuleModificationTime = llvm::sys::TimeValue::now();
|
|
} else {
|
|
V->ModuleModificationTime = ModuleStatus.getLastModificationTime();
|
|
}
|
|
}
|
|
Impl->TheCache.set(K, V);
|
|
}
|
|
|
|
CodeCompletionCache::CodeCompletionCache()
|
|
: Impl(new CodeCompletionCacheImpl()) {}
|
|
|
|
CodeCompletionCache::~CodeCompletionCache() {}
|
|
|
|
MutableArrayRef<CodeCompletionResult *> CodeCompletionContext::takeResults() {
|
|
// Copy pointers to the results.
|
|
const size_t Count = CurrentResults.Results.size();
|
|
CodeCompletionResult **Results =
|
|
CurrentResults.Allocator->Allocate<CodeCompletionResult *>(Count);
|
|
std::copy(CurrentResults.Results.begin(), CurrentResults.Results.end(),
|
|
Results);
|
|
CurrentResults.Results.clear();
|
|
return MutableArrayRef<CodeCompletionResult *>(Results, Count);
|
|
}
|
|
|
|
Optional<unsigned> CodeCompletionString::getFirstTextChunkIndex() const {
|
|
for (auto i : indices(getChunks())) {
|
|
auto &C = getChunks()[i];
|
|
switch (C.getKind()) {
|
|
case CodeCompletionString::Chunk::ChunkKind::Text:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterName:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterInternalName:
|
|
case CodeCompletionString::Chunk::ChunkKind::GenericParameterName:
|
|
case CodeCompletionString::Chunk::ChunkKind::LeftParen:
|
|
case CodeCompletionString::Chunk::ChunkKind::LeftBracket:
|
|
case CodeCompletionString::Chunk::ChunkKind::DeclAttrParamKeyword:
|
|
case CodeCompletionString::Chunk::ChunkKind::DeclAttrKeyword:
|
|
return i;
|
|
case CodeCompletionString::Chunk::ChunkKind::RightParen:
|
|
case CodeCompletionString::Chunk::ChunkKind::RightBracket:
|
|
case CodeCompletionString::Chunk::ChunkKind::LeftAngle:
|
|
case CodeCompletionString::Chunk::ChunkKind::RightAngle:
|
|
case CodeCompletionString::Chunk::ChunkKind::Dot:
|
|
case CodeCompletionString::Chunk::ChunkKind::Ellipsis:
|
|
case CodeCompletionString::Chunk::ChunkKind::Comma:
|
|
case CodeCompletionString::Chunk::ChunkKind::ExclamationMark:
|
|
case CodeCompletionString::Chunk::ChunkKind::QuestionMark:
|
|
case CodeCompletionString::Chunk::ChunkKind::Ampersand:
|
|
case CodeCompletionString::Chunk::ChunkKind::AccessControlKeyword:
|
|
case CodeCompletionString::Chunk::ChunkKind::OverrideKeyword:
|
|
case CodeCompletionString::Chunk::ChunkKind::DeclIntroducer:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterColon:
|
|
case CodeCompletionString::Chunk::ChunkKind::DeclAttrParamEqual:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterType:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterClosureType:
|
|
case CodeCompletionString::Chunk::ChunkKind::OptionalBegin:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterBegin:
|
|
case CodeCompletionString::Chunk::ChunkKind::GenericParameterBegin:
|
|
case CodeCompletionString::Chunk::ChunkKind::DynamicLookupMethodCallTail:
|
|
case CodeCompletionString::Chunk::ChunkKind::OptionalMethodCallTail:
|
|
case CodeCompletionString::Chunk::ChunkKind::TypeAnnotation:
|
|
continue;
|
|
|
|
case CodeCompletionString::Chunk::ChunkKind::BraceStmtWithCursor:
|
|
llvm_unreachable("should have already extracted the text");
|
|
}
|
|
}
|
|
return None;
|
|
}
|
|
|
|
StringRef CodeCompletionString::getFirstTextChunk() const {
|
|
Optional<unsigned> Idx = getFirstTextChunkIndex();
|
|
if (Idx.hasValue())
|
|
return getChunks()[*Idx].getText();
|
|
return StringRef();
|
|
}
|
|
|
|
void CodeCompletionString::getName(raw_ostream &OS) const {
|
|
auto FirstTextChunk = getFirstTextChunkIndex();
|
|
int TextSize = 0;
|
|
if (FirstTextChunk.hasValue()) {
|
|
for (auto C : getChunks().slice(*FirstTextChunk)) {
|
|
using ChunkKind = CodeCompletionString::Chunk::ChunkKind;
|
|
if (C.getKind() == ChunkKind::BraceStmtWithCursor)
|
|
break;
|
|
if (C.getKind() == ChunkKind::TypeAnnotation ||
|
|
C.getKind() == ChunkKind::CallParameterClosureType)
|
|
continue;
|
|
if (C.getKind() == ChunkKind::DeclAttrParamEqual)
|
|
continue;
|
|
if (C.hasText() && !C.isAnnotation()) {
|
|
TextSize += C.getText().size();
|
|
OS << C.getText();
|
|
}
|
|
}
|
|
}
|
|
assert((TextSize > 0) &&
|
|
"code completion string should have non-empty name!");
|
|
}
|
|
|
|
void CodeCompletionContext::sortCompletionResults(
|
|
MutableArrayRef<CodeCompletionResult *> Results) {
|
|
struct ResultAndName {
|
|
CodeCompletionResult *result;
|
|
std::string name;
|
|
};
|
|
|
|
// Caching the name of each field is important to avoid unnecessary calls to
|
|
// CodeCompletionString::getName().
|
|
std::vector<ResultAndName> nameCache(Results.size());
|
|
for (unsigned i = 0, n = Results.size(); i < n; ++i) {
|
|
auto *result = Results[i];
|
|
nameCache[i].result = result;
|
|
llvm::raw_string_ostream OS(nameCache[i].name);
|
|
result->getCompletionString()->getName(OS);
|
|
OS.flush();
|
|
}
|
|
|
|
// Sort nameCache, and then transform Results to return the pointers in order.
|
|
std::sort(nameCache.begin(), nameCache.end(),
|
|
[](const ResultAndName &LHS, const ResultAndName &RHS) {
|
|
int Result = StringRef(LHS.name).compare_lower(RHS.name);
|
|
// If the case insensitive comparison is equal, then secondary sort order
|
|
// should be case sensitive.
|
|
if (Result == 0)
|
|
Result = LHS.name.compare(RHS.name);
|
|
return Result < 0;
|
|
});
|
|
|
|
std::transform(nameCache.begin(), nameCache.end(), Results.begin(),
|
|
[](const ResultAndName &entry) { return entry.result; });
|
|
}
|
|
|
|
namespace {
|
|
class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
|
|
CodeCompletionContext &CompletionContext;
|
|
CodeCompletionConsumer &Consumer;
|
|
|
|
enum class CompletionKind {
|
|
None,
|
|
DotExpr,
|
|
PostfixExprBeginning,
|
|
PostfixExpr,
|
|
PostfixExprParen,
|
|
SuperExpr,
|
|
SuperExprDot,
|
|
TypeSimpleBeginning,
|
|
TypeIdentifierWithDot,
|
|
TypeIdentifierWithoutDot,
|
|
CaseStmtBeginning,
|
|
CaseStmtDotPrefix,
|
|
CatchStmtBeginning,
|
|
ThrowStmtBeginning,
|
|
NominalMemberBeginning,
|
|
AttributeBegin,
|
|
AttributeDeclParen,
|
|
};
|
|
|
|
CompletionKind Kind = CompletionKind::None;
|
|
Expr *ParsedExpr = nullptr;
|
|
SourceLoc DotLoc;
|
|
TypeLoc ParsedTypeLoc;
|
|
DeclContext *CurDeclContext = nullptr;
|
|
Decl *CStyleForLoopIterationVariable = nullptr;
|
|
DeclAttrKind AttrKind;
|
|
int AttrParamIndex;
|
|
bool IsInSil;
|
|
Optional<DeclKind> AttTargetDK;
|
|
|
|
SmallVector<StringRef, 3> ParsedKeywords;
|
|
|
|
/// \brief Set to true when we have delivered code completion results
|
|
/// to the \c Consumer.
|
|
bool DeliveredResults = false;
|
|
|
|
bool typecheckContextImpl(DeclContext *DC) {
|
|
// Type check the function that contains the expression.
|
|
if (DC->getContextKind() == DeclContextKind::AbstractClosureExpr ||
|
|
DC->getContextKind() == DeclContextKind::AbstractFunctionDecl) {
|
|
SourceLoc EndTypeCheckLoc =
|
|
ParsedExpr ? ParsedExpr->getStartLoc()
|
|
: P.Context.SourceMgr.getCodeCompletionLoc();
|
|
// Find the nearest containing function or nominal decl.
|
|
DeclContext *DCToTypeCheck = DC;
|
|
while (!DCToTypeCheck->isModuleContext() &&
|
|
!isa<AbstractFunctionDecl>(DCToTypeCheck) &&
|
|
!isa<NominalTypeDecl>(DCToTypeCheck) &&
|
|
!isa<TopLevelCodeDecl>(DCToTypeCheck))
|
|
DCToTypeCheck = DCToTypeCheck->getParent();
|
|
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DCToTypeCheck)) {
|
|
// We found a function. First, type check the nominal decl that
|
|
// contains the function. Then type check the function itself.
|
|
typecheckContextImpl(DCToTypeCheck->getParent());
|
|
return typeCheckAbstractFunctionBodyUntil(AFD, EndTypeCheckLoc);
|
|
}
|
|
if (isa<NominalTypeDecl>(DCToTypeCheck)) {
|
|
// We found a nominal decl (for example, the closure is used in an
|
|
// initializer of a property).
|
|
return typecheckContextImpl(DCToTypeCheck);
|
|
}
|
|
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(DCToTypeCheck)) {
|
|
return typeCheckTopLevelCodeDecl(TLCD);
|
|
}
|
|
return false;
|
|
}
|
|
if (auto *NTD = dyn_cast<NominalTypeDecl>(DC)) {
|
|
// First, type check the parent DeclContext.
|
|
typecheckContextImpl(DC->getParent());
|
|
if (NTD->hasType())
|
|
return true;
|
|
return typeCheckCompletionDecl(cast<NominalTypeDecl>(DC));
|
|
}
|
|
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(DC)) {
|
|
return typeCheckTopLevelCodeDecl(TLCD);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// \returns true on success, false on failure.
|
|
bool typecheckContext() {
|
|
return typecheckContextImpl(CurDeclContext);
|
|
}
|
|
|
|
/// \returns true on success, false on failure.
|
|
bool typecheckDelayedParsedDecl() {
|
|
assert(DelayedParsedDecl && "should have a delayed parsed decl");
|
|
return typeCheckCompletionDecl(DelayedParsedDecl);
|
|
}
|
|
|
|
/// \returns true on success, false on failure.
|
|
bool typecheckParsedExpr() {
|
|
assert(ParsedExpr && "should have an expression");
|
|
|
|
Expr *TypecheckedExpr = ParsedExpr;
|
|
if (!typeCheckCompletionContextExpr(P.Context, CurDeclContext,
|
|
TypecheckedExpr))
|
|
return false;
|
|
|
|
if (TypecheckedExpr->getType()->is<ErrorType>())
|
|
return false;
|
|
|
|
ParsedExpr = TypecheckedExpr;
|
|
return true;
|
|
}
|
|
|
|
/// \returns true on success, false on failure.
|
|
bool typecheckParsedType() {
|
|
assert(ParsedTypeLoc.getTypeRepr() && "should have a TypeRepr");
|
|
return !performTypeLocChecking(P.Context, ParsedTypeLoc, /*SIL*/ false,
|
|
CurDeclContext, false);
|
|
}
|
|
|
|
public:
|
|
CodeCompletionCallbacksImpl(Parser &P,
|
|
CodeCompletionContext &CompletionContext,
|
|
CodeCompletionConsumer &Consumer)
|
|
: CodeCompletionCallbacks(P), CompletionContext(CompletionContext),
|
|
Consumer(Consumer) {
|
|
}
|
|
|
|
void completeExpr() override;
|
|
void completeDotExpr(Expr *E, SourceLoc DotLoc) override;
|
|
void completePostfixExprBeginning() override;
|
|
void completePostfixExpr(Expr *E) override;
|
|
void completePostfixExprParen(Expr *E) override;
|
|
void completeExprSuper(SuperRefExpr *SRE) override;
|
|
void completeExprSuperDot(SuperRefExpr *SRE) override;
|
|
|
|
void completeTypeSimpleBeginning() override;
|
|
void completeTypeIdentifierWithDot(IdentTypeRepr *ITR) override;
|
|
void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) override;
|
|
|
|
void completeCaseStmtBeginning() override;
|
|
void completeCaseStmtDotPrefix() override;
|
|
void completeCatchStmtBeginning() override;
|
|
void completeThrowStmtBeginning() override;
|
|
void completeDeclAttrKeyword(Decl *D, bool Sil, bool Param) override;
|
|
void completeDeclAttrParam(DeclAttrKind DK, int Index) override;
|
|
void completeNominalMemberBeginning(
|
|
SmallVectorImpl<StringRef> &Keywords) override;
|
|
|
|
void addKeywords(CodeCompletionResultSink &Sink);
|
|
|
|
void doneParsing() override;
|
|
|
|
void deliverCompletionResults();
|
|
};
|
|
} // end unnamed namespace
|
|
|
|
void CodeCompletionCallbacksImpl::completeExpr() {
|
|
if (DeliveredResults)
|
|
return;
|
|
|
|
Parser::ParserPositionRAII RestorePosition(P);
|
|
P.restoreParserPosition(ExprBeginPosition);
|
|
|
|
// FIXME: implement fallback code completion.
|
|
|
|
deliverCompletionResults();
|
|
}
|
|
|
|
namespace {
|
|
/// Build completions by doing visible decl lookup from a context.
|
|
class CompletionLookup final : public swift::VisibleDeclConsumer {
|
|
CodeCompletionResultSink &Sink;
|
|
ASTContext &Ctx;
|
|
OwnedResolver TypeResolver;
|
|
const DeclContext *CurrDeclContext;
|
|
|
|
enum class LookupKind {
|
|
ValueExpr,
|
|
ValueInDeclContext,
|
|
EnumElement,
|
|
Type,
|
|
TypeInDeclContext,
|
|
ImportFromModule,
|
|
};
|
|
|
|
LookupKind Kind;
|
|
|
|
/// Type of the user-provided expression for LookupKind::ValueExpr
|
|
/// completions.
|
|
Type ExprType;
|
|
|
|
/// User-provided base type for LookupKind::Type completions.
|
|
Type BaseType;
|
|
|
|
bool HaveDot = false;
|
|
SourceLoc DotLoc;
|
|
bool NeedLeadingDot = false;
|
|
|
|
bool NeedOptionalUnwrap = false;
|
|
unsigned NumBytesToEraseForOptionalUnwrap = 0;
|
|
|
|
bool HaveLParen = false;
|
|
bool IsSuperRefExpr = false;
|
|
bool IsDynamicLookup = false;
|
|
|
|
/// \brief True if we are code completing inside a static method.
|
|
bool InsideStaticMethod = false;
|
|
|
|
/// \brief Innermost method that the code completion point is in.
|
|
const AbstractFunctionDecl *CurrentMethod = nullptr;
|
|
|
|
/// \brief Declarations that should get ExpressionSpecific semantic context.
|
|
llvm::SmallSet<const Decl *, 4> ExpressionSpecificDecls;
|
|
|
|
using DeducedAssociatedTypes =
|
|
llvm::DenseMap<const AssociatedTypeDecl *, Type>;
|
|
std::map<const NominalTypeDecl *, DeducedAssociatedTypes>
|
|
DeducedAssociatedTypeCache;
|
|
|
|
Optional<SemanticContextKind> ForcedSemanticContext = None;
|
|
|
|
public:
|
|
bool FoundFunctionCalls = false;
|
|
bool FoundFunctionsWithoutFirstKeyword = false;
|
|
|
|
private:
|
|
void foundFunction(const AbstractFunctionDecl *AFD) {
|
|
FoundFunctionCalls = true;
|
|
DeclName Name = AFD->getFullName();
|
|
auto ArgNames = Name.getArgumentNames();
|
|
if (ArgNames.empty())
|
|
return;
|
|
if (ArgNames[0].empty())
|
|
FoundFunctionsWithoutFirstKeyword = true;
|
|
}
|
|
|
|
void foundFunction(const AnyFunctionType *AFT) {
|
|
FoundFunctionCalls = true;
|
|
Type In = AFT->getInput();
|
|
if (!In)
|
|
return;
|
|
if (isa<ParenType>(In.getPointer())) {
|
|
FoundFunctionsWithoutFirstKeyword = true;
|
|
return;
|
|
}
|
|
TupleType *InTuple = In->getAs<TupleType>();
|
|
if (!InTuple)
|
|
return;
|
|
auto Elements = InTuple->getElements();
|
|
if (Elements.empty())
|
|
return;
|
|
if (!Elements[0].hasName())
|
|
FoundFunctionsWithoutFirstKeyword = true;
|
|
}
|
|
|
|
public:
|
|
struct RequestedResultsTy {
|
|
const Module *TheModule;
|
|
bool OnlyTypes;
|
|
bool NeedLeadingDot;
|
|
|
|
static RequestedResultsTy fromModule(const Module *TheModule) {
|
|
return { TheModule, false, false };
|
|
}
|
|
|
|
RequestedResultsTy onlyTypes() const {
|
|
return { TheModule, true, NeedLeadingDot };
|
|
}
|
|
|
|
RequestedResultsTy needLeadingDot(bool NeedDot) const {
|
|
return { TheModule, OnlyTypes, NeedDot };
|
|
}
|
|
|
|
static RequestedResultsTy toplevelResults() {
|
|
return { nullptr, false, false };
|
|
}
|
|
};
|
|
|
|
Optional<RequestedResultsTy> RequestedCachedResults;
|
|
|
|
public:
|
|
CompletionLookup(CodeCompletionResultSink &Sink,
|
|
ASTContext &Ctx,
|
|
const DeclContext *CurrDeclContext)
|
|
: Sink(Sink), Ctx(Ctx),
|
|
TypeResolver(createLazyResolver(Ctx)),
|
|
CurrDeclContext(CurrDeclContext) {
|
|
|
|
// Determine if we are doing code completion inside a static method.
|
|
if (CurrDeclContext) {
|
|
CurrentMethod = CurrDeclContext->getInnermostMethodContext();
|
|
if (auto *FD = dyn_cast_or_null<FuncDecl>(CurrentMethod))
|
|
InsideStaticMethod = FD->isStatic();
|
|
}
|
|
}
|
|
|
|
void discardTypeResolver() {
|
|
TypeResolver.reset();
|
|
}
|
|
|
|
void setHaveDot(SourceLoc DotLoc) {
|
|
HaveDot = true;
|
|
this->DotLoc = DotLoc;
|
|
}
|
|
|
|
bool needDot() const {
|
|
return NeedLeadingDot;
|
|
}
|
|
|
|
void setHaveLParen(bool Value) {
|
|
HaveLParen = Value;
|
|
}
|
|
|
|
void setIsSuperRefExpr() {
|
|
IsSuperRefExpr = true;
|
|
}
|
|
|
|
void setIsDynamicLookup() {
|
|
IsDynamicLookup = true;
|
|
}
|
|
|
|
void addExpressionSpecificDecl(const Decl *D) {
|
|
ExpressionSpecificDecls.insert(D);
|
|
}
|
|
|
|
SemanticContextKind getSemanticContext(const Decl *D,
|
|
DeclVisibilityKind Reason) {
|
|
if (ForcedSemanticContext)
|
|
return *ForcedSemanticContext;
|
|
|
|
switch (Reason) {
|
|
case DeclVisibilityKind::LocalVariable:
|
|
case DeclVisibilityKind::FunctionParameter:
|
|
case DeclVisibilityKind::GenericParameter:
|
|
if (ExpressionSpecificDecls.count(D))
|
|
return SemanticContextKind::ExpressionSpecific;
|
|
return SemanticContextKind::Local;
|
|
|
|
case DeclVisibilityKind::MemberOfCurrentNominal:
|
|
if (IsSuperRefExpr &&
|
|
CurrentMethod && CurrentMethod->getOverriddenDecl() == D)
|
|
return SemanticContextKind::ExpressionSpecific;
|
|
return SemanticContextKind::CurrentNominal;
|
|
|
|
case DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal:
|
|
case DeclVisibilityKind::MemberOfSuper:
|
|
return SemanticContextKind::Super;
|
|
|
|
case DeclVisibilityKind::MemberOfOutsideNominal:
|
|
return SemanticContextKind::OutsideNominal;
|
|
|
|
case DeclVisibilityKind::VisibleAtTopLevel:
|
|
if (CurrDeclContext &&
|
|
D->getModuleContext() == CurrDeclContext->getParentModule())
|
|
return SemanticContextKind::CurrentModule;
|
|
else
|
|
return SemanticContextKind::OtherModule;
|
|
|
|
case DeclVisibilityKind::DynamicLookup:
|
|
// AnyObject results can come from different modules, including the
|
|
// current module, but we always assign them the OtherModule semantic
|
|
// context. These declarations are uniqued by signature, so it is
|
|
// totally random (determined by the hash function) which of the
|
|
// equivalent declarations (across multiple modules) we will get.
|
|
return SemanticContextKind::OtherModule;
|
|
}
|
|
llvm_unreachable("unhandled kind");
|
|
}
|
|
|
|
void addLeadingDot(CodeCompletionResultBuilder &Builder) {
|
|
if (NeedOptionalUnwrap) {
|
|
Builder.setNumBytesToErase(NumBytesToEraseForOptionalUnwrap);
|
|
Builder.addQuestionMark();
|
|
Builder.addLeadingDot();
|
|
return;
|
|
}
|
|
if (needDot())
|
|
Builder.addLeadingDot();
|
|
}
|
|
|
|
void addTypeAnnotation(CodeCompletionResultBuilder &Builder, Type T) {
|
|
T = T->getReferenceStorageReferent();
|
|
if (T->isVoid())
|
|
Builder.addTypeAnnotation("Void");
|
|
else
|
|
Builder.addTypeAnnotation(T.getString());
|
|
}
|
|
|
|
static bool isBoringBoundGenericType(Type T) {
|
|
BoundGenericType *BGT = T->getAs<BoundGenericType>();
|
|
if (!BGT)
|
|
return false;
|
|
for (Type Arg : BGT->getGenericArgs()) {
|
|
if (!Arg->is<GenericTypeParamType>())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Type getTypeOfMember(const ValueDecl *VD) {
|
|
if (ExprType) {
|
|
Type ContextTy = VD->getDeclContext()->getDeclaredTypeOfContext();
|
|
if (ContextTy) {
|
|
Type MaybeNominalType = ExprType->getRValueInstanceType();
|
|
if (ContextTy->getAnyNominal() == MaybeNominalType->getAnyNominal() &&
|
|
!isBoringBoundGenericType(MaybeNominalType))
|
|
return MaybeNominalType->getTypeOfMember(
|
|
CurrDeclContext->getParentModule(), VD, TypeResolver.get());
|
|
}
|
|
}
|
|
|
|
return VD->getType();
|
|
}
|
|
|
|
const DeducedAssociatedTypes &
|
|
getAssociatedTypeMap(const NominalTypeDecl *NTD) {
|
|
{
|
|
auto It = DeducedAssociatedTypeCache.find(NTD);
|
|
if (It != DeducedAssociatedTypeCache.end())
|
|
return It->second;
|
|
}
|
|
|
|
DeducedAssociatedTypes Types;
|
|
for (auto Conformance : NTD->getAllConformances()) {
|
|
if (!Conformance->isComplete())
|
|
continue;
|
|
Conformance->forEachTypeWitness(TypeResolver.get(),
|
|
[&](const AssociatedTypeDecl *ATD,
|
|
const Substitution &Subst,
|
|
TypeDecl *TD) -> bool {
|
|
Types[ATD] = Subst.getReplacement();
|
|
return false;
|
|
});
|
|
}
|
|
|
|
auto ItAndInserted = DeducedAssociatedTypeCache.insert({ NTD, Types });
|
|
assert(ItAndInserted.second == true && "should not be in the map");
|
|
return ItAndInserted.first->second;
|
|
}
|
|
|
|
Type getAssociatedTypeType(const AssociatedTypeDecl *ATD) {
|
|
Type BaseTy = BaseType;
|
|
if (!BaseTy)
|
|
BaseTy = ExprType;
|
|
if (!BaseTy && CurrDeclContext)
|
|
BaseTy = CurrDeclContext->getInnermostTypeContext()
|
|
->getDeclaredTypeInContext();
|
|
if (BaseTy) {
|
|
BaseTy = BaseTy->getRValueInstanceType();
|
|
if (auto NTD = BaseTy->getAnyNominal()) {
|
|
auto &Types = getAssociatedTypeMap(NTD);
|
|
if (Type T = Types.lookup(ATD))
|
|
return T;
|
|
}
|
|
}
|
|
return Type();
|
|
}
|
|
|
|
void addVarDeclRef(const VarDecl *VD, DeclVisibilityKind Reason) {
|
|
if (!VD->hasName())
|
|
return;
|
|
if (!VD->isUserAccessible())
|
|
return;
|
|
|
|
StringRef Name = VD->getName().get();
|
|
assert(!Name.empty() && "name should not be empty");
|
|
|
|
assert(VD->isStatic() ||
|
|
!(InsideStaticMethod &&
|
|
VD->getDeclContext() == CurrentMethod->getDeclContext()) &&
|
|
"name lookup bug -- can not see an instance variable "
|
|
"in a static function");
|
|
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Declaration,
|
|
getSemanticContext(VD, Reason));
|
|
Builder.setAssociatedDecl(VD);
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk(Name);
|
|
|
|
// Add a type annotation.
|
|
Type VarType = getTypeOfMember(VD);
|
|
if (VD->getName() == Ctx.Id_self) {
|
|
// Strip inout from 'self'. It is useful to show inout for function
|
|
// parameters. But for 'self' it is just noise.
|
|
VarType = VarType->getInOutObjectType();
|
|
}
|
|
if (IsDynamicLookup || VD->getAttrs().hasAttribute<OptionalAttr>()) {
|
|
// Values of properties that were found on a AnyObject have
|
|
// Optional<T> type. Same applies to optional members.
|
|
VarType = OptionalType::get(VarType);
|
|
}
|
|
addTypeAnnotation(Builder, VarType);
|
|
}
|
|
|
|
void addPatternParameters(CodeCompletionResultBuilder &Builder,
|
|
const Pattern *P) {
|
|
if (auto *TP = dyn_cast<TuplePattern>(P)) {
|
|
bool NeedComma = false;
|
|
for (unsigned i = 0, end = TP->getNumElements(); i < end; ++i) {
|
|
TuplePatternElt TupleElt = TP->getElement(i);
|
|
if (NeedComma)
|
|
Builder.addComma();
|
|
NeedComma = true;
|
|
|
|
// The last elt must be the vararg if there is one.
|
|
bool IsVarArg = i == end-1 && TP->hasVararg();
|
|
Type EltT = TupleElt.getPattern()->getType();
|
|
if (IsVarArg)
|
|
EltT = TupleTypeElt::getVarargBaseTy(EltT);
|
|
|
|
Builder.addCallParameter(TupleElt.getPattern()->getBoundName(),
|
|
EltT, IsVarArg);
|
|
}
|
|
return;
|
|
}
|
|
|
|
Type PType = P->getType();
|
|
if (auto Parens = dyn_cast<ParenType>(PType.getPointer()))
|
|
PType = Parens->getUnderlyingType();
|
|
Builder.addCallParameter(P->getBoundName(), PType, /*IsVarArg*/false);
|
|
}
|
|
|
|
void addPatternFromTypeImpl(CodeCompletionResultBuilder &Builder, Type T,
|
|
Identifier Label, bool IsTopLevel, bool IsVarArg) {
|
|
if (auto *TT = T->getAs<TupleType>()) {
|
|
if (!Label.empty()) {
|
|
Builder.addTextChunk(Label.str());
|
|
Builder.addTextChunk(": ");
|
|
}
|
|
if (!IsTopLevel || !HaveLParen)
|
|
Builder.addLeftParen();
|
|
else
|
|
Builder.addAnnotatedLeftParen();
|
|
bool NeedComma = false;
|
|
for (auto TupleElt : TT->getElements()) {
|
|
if (NeedComma)
|
|
Builder.addComma();
|
|
Type EltT = TupleElt.isVararg() ? TupleElt.getVarargBaseTy()
|
|
: TupleElt.getType();
|
|
addPatternFromTypeImpl(Builder, EltT, TupleElt.getName(), false,
|
|
TupleElt.isVararg());
|
|
NeedComma = true;
|
|
}
|
|
Builder.addRightParen();
|
|
return;
|
|
}
|
|
if (auto *PT = dyn_cast<ParenType>(T.getPointer())) {
|
|
if (IsTopLevel && !HaveLParen)
|
|
Builder.addLeftParen();
|
|
else if (IsTopLevel)
|
|
Builder.addAnnotatedLeftParen();
|
|
Builder.addCallParameter(Identifier(), PT->getUnderlyingType(),
|
|
/*IsVarArg*/false);
|
|
if (IsTopLevel)
|
|
Builder.addRightParen();
|
|
return;
|
|
}
|
|
|
|
if (IsTopLevel && !HaveLParen)
|
|
Builder.addLeftParen();
|
|
else if (IsTopLevel)
|
|
Builder.addAnnotatedLeftParen();
|
|
|
|
Builder.addCallParameter(Label, T, IsVarArg);
|
|
if (IsTopLevel)
|
|
Builder.addRightParen();
|
|
}
|
|
|
|
void addPatternFromType(CodeCompletionResultBuilder &Builder, Type T) {
|
|
addPatternFromTypeImpl(Builder, T, Identifier(), true, /*isVarArg*/false);
|
|
}
|
|
|
|
static bool hasInterestingDefaultValues(const AnyFunctionType *AFT) {
|
|
if (auto *TT = dyn_cast<TupleType>(AFT->getInput().getPointer())) {
|
|
for (const TupleTypeElt &EltT : TT->getElements()) {
|
|
switch (EltT.getDefaultArgKind()) {
|
|
case DefaultArgumentKind::Normal:
|
|
case DefaultArgumentKind::Inherited: // FIXME: include this?
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void addParamPatternFromFunction(CodeCompletionResultBuilder &Builder,
|
|
const AnyFunctionType *AFT,
|
|
const AbstractFunctionDecl *AFD,
|
|
bool includeDefaultArgs = true) {
|
|
|
|
const TuplePattern *BodyTuple = nullptr;
|
|
if (AFD) {
|
|
auto BodyPatterns = AFD->getBodyParamPatterns();
|
|
// Skip over the implicit 'self'.
|
|
if (AFD->getImplicitSelfDecl()) {
|
|
BodyPatterns = BodyPatterns.slice(1);
|
|
}
|
|
|
|
if (!BodyPatterns.empty())
|
|
BodyTuple = dyn_cast<TuplePattern>(BodyPatterns.front());
|
|
}
|
|
|
|
// Do not desugar AFT->getInput(), as we want to treat (_: (a,b)) distinctly
|
|
// from (a,b) for code-completion.
|
|
if (auto *TT = dyn_cast<TupleType>(AFT->getInput().getPointer())) {
|
|
bool NeedComma = false;
|
|
// Iterate over the tuple type fields, corresponding to each parameter.
|
|
for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) {
|
|
const auto &TupleElt = TT->getElement(i);
|
|
switch (TupleElt.getDefaultArgKind()) {
|
|
case DefaultArgumentKind::None:
|
|
break;
|
|
|
|
case DefaultArgumentKind::Normal:
|
|
case DefaultArgumentKind::Inherited:
|
|
if (includeDefaultArgs)
|
|
break;
|
|
continue;
|
|
|
|
case DefaultArgumentKind::File:
|
|
case DefaultArgumentKind::Line:
|
|
case DefaultArgumentKind::Column:
|
|
case DefaultArgumentKind::Function:
|
|
case DefaultArgumentKind::DSOHandle:
|
|
// Skip parameters that are defaulted to source location or other
|
|
// caller context information. Users typically don't want to specify
|
|
// these parameters.
|
|
continue;
|
|
}
|
|
auto ParamType = TupleElt.isVararg() ? TupleElt.getVarargBaseTy()
|
|
: TupleElt.getType();
|
|
auto Name = TupleElt.getName();
|
|
|
|
if (NeedComma)
|
|
Builder.addComma();
|
|
if (BodyTuple) {
|
|
// If we have a local name for the parameter, pass in that as well.
|
|
auto ParamPat = BodyTuple->getElement(i).getPattern();
|
|
Builder.addCallParameter(Name, ParamPat->getBodyName(), ParamType,
|
|
TupleElt.isVararg());
|
|
} else {
|
|
Builder.addCallParameter(Name, ParamType, TupleElt.isVararg());
|
|
}
|
|
NeedComma = true;
|
|
}
|
|
} else {
|
|
// If it's not a tuple, it could be a unary function.
|
|
Type T = AFT->getInput();
|
|
if (auto *PT = dyn_cast<ParenType>(T.getPointer())) {
|
|
// Only unwrap the paren sugar, if it exists.
|
|
T = PT->getUnderlyingType();
|
|
}
|
|
if (BodyTuple) {
|
|
auto ParamPat = BodyTuple->getElement(0).getPattern();
|
|
Builder.addCallParameter(Identifier(), ParamPat->getBodyName(), T,
|
|
/*IsVarArg*/false);
|
|
} else
|
|
Builder.addCallParameter(Identifier(), T, /*IsVarArg*/false);
|
|
}
|
|
}
|
|
|
|
void addFunctionCallPattern(const AnyFunctionType *AFT,
|
|
const AbstractFunctionDecl *AFD = nullptr) {
|
|
foundFunction(AFT);
|
|
|
|
// Add the pattern, possibly including any default arguments.
|
|
auto addPattern = [&](bool includeDefaultArgs = true) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink, CodeCompletionResult::ResultKind::Pattern,
|
|
SemanticContextKind::ExpressionSpecific);
|
|
if (!HaveLParen)
|
|
Builder.addLeftParen();
|
|
else
|
|
Builder.addAnnotatedLeftParen();
|
|
|
|
addParamPatternFromFunction(Builder, AFT, AFD, includeDefaultArgs);
|
|
|
|
Builder.addRightParen();
|
|
addTypeAnnotation(Builder, AFT->getResult());
|
|
};
|
|
|
|
if (hasInterestingDefaultValues(AFT))
|
|
addPattern(/*includeDefaultArgs*/ false);
|
|
addPattern();
|
|
}
|
|
|
|
void addMethodCall(const FuncDecl *FD, DeclVisibilityKind Reason) {
|
|
foundFunction(FD);
|
|
bool IsImplicitlyCurriedInstanceMethod;
|
|
switch (Kind) {
|
|
case LookupKind::ValueExpr:
|
|
IsImplicitlyCurriedInstanceMethod = ExprType->is<AnyMetatypeType>() &&
|
|
!FD->isStatic();
|
|
break;
|
|
case LookupKind::ValueInDeclContext:
|
|
IsImplicitlyCurriedInstanceMethod =
|
|
InsideStaticMethod &&
|
|
FD->getDeclContext() == CurrentMethod->getDeclContext() &&
|
|
!FD->isStatic();
|
|
if (!IsImplicitlyCurriedInstanceMethod) {
|
|
if (auto Init = dyn_cast<Initializer>(CurrDeclContext)) {
|
|
IsImplicitlyCurriedInstanceMethod =
|
|
FD->getDeclContext() == Init->getParent() &&
|
|
!FD->isStatic();
|
|
}
|
|
}
|
|
break;
|
|
case LookupKind::EnumElement:
|
|
case LookupKind::Type:
|
|
case LookupKind::TypeInDeclContext:
|
|
llvm_unreachable("can not have a method call while doing a "
|
|
"type completion");
|
|
case LookupKind::ImportFromModule:
|
|
IsImplicitlyCurriedInstanceMethod = false;
|
|
break;
|
|
}
|
|
|
|
StringRef Name = FD->getName().get();
|
|
assert(!Name.empty() && "name should not be empty");
|
|
|
|
unsigned FirstIndex = 0;
|
|
if (!IsImplicitlyCurriedInstanceMethod && FD->getImplicitSelfDecl())
|
|
FirstIndex = 1;
|
|
Type FunctionType = getTypeOfMember(FD);
|
|
|
|
if (FirstIndex != 0 && !FunctionType->is<ErrorType>())
|
|
FunctionType = FunctionType->castTo<AnyFunctionType>()->getResult();
|
|
|
|
// Add the method, possibly including any default arguments.
|
|
auto addMethodImpl = [&](bool includeDefaultArgs = true) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink, CodeCompletionResult::ResultKind::Declaration,
|
|
getSemanticContext(FD, Reason));
|
|
Builder.setAssociatedDecl(FD);
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk(Name);
|
|
if (IsDynamicLookup)
|
|
Builder.addDynamicLookupMethodCallTail();
|
|
else if (FD->getAttrs().hasAttribute<OptionalAttr>())
|
|
Builder.addOptionalMethodCallTail();
|
|
|
|
llvm::SmallString<32> TypeStr;
|
|
|
|
if (FunctionType->is<ErrorType>()) {
|
|
llvm::raw_svector_ostream OS(TypeStr);
|
|
FunctionType.print(OS);
|
|
Builder.addTypeAnnotation(OS.str());
|
|
return;
|
|
}
|
|
|
|
Type FirstInputType = FunctionType->castTo<AnyFunctionType>()->getInput();
|
|
|
|
if (IsImplicitlyCurriedInstanceMethod) {
|
|
if (auto PT = dyn_cast<ParenType>(FirstInputType.getPointer()))
|
|
FirstInputType = PT->getUnderlyingType();
|
|
|
|
Builder.addLeftParen();
|
|
Builder.addCallParameter(Ctx.Id_self, FirstInputType,
|
|
/*IsVarArg*/ false);
|
|
Builder.addRightParen();
|
|
} else {
|
|
Builder.addLeftParen();
|
|
addParamPatternFromFunction(Builder,
|
|
FunctionType->castTo<AnyFunctionType>(), FD,
|
|
includeDefaultArgs);
|
|
Builder.addRightParen();
|
|
}
|
|
|
|
Type ResultType = FunctionType->castTo<AnyFunctionType>()->getResult();
|
|
|
|
// Build type annotation.
|
|
{
|
|
llvm::raw_svector_ostream OS(TypeStr);
|
|
for (unsigned i = FirstIndex + 1, e = FD->getBodyParamPatterns().size();
|
|
i != e; ++i) {
|
|
ResultType->castTo<AnyFunctionType>()->getInput()->print(OS);
|
|
ResultType = ResultType->castTo<AnyFunctionType>()->getResult();
|
|
OS << " -> ";
|
|
}
|
|
// What's left is the result type.
|
|
if (ResultType->isVoid())
|
|
OS << "Void";
|
|
else
|
|
ResultType.print(OS);
|
|
}
|
|
Builder.addTypeAnnotation(TypeStr);
|
|
};
|
|
|
|
if (!FunctionType->is<ErrorType>() &&
|
|
hasInterestingDefaultValues(FunctionType->castTo<AnyFunctionType>())) {
|
|
addMethodImpl(/*includeDefaultArgs*/ false);
|
|
}
|
|
addMethodImpl();
|
|
}
|
|
|
|
void addConstructorCall(const ConstructorDecl *CD, DeclVisibilityKind Reason,
|
|
Identifier addName = Identifier()) {
|
|
foundFunction(CD);
|
|
Type MemberType = getTypeOfMember(CD);
|
|
AnyFunctionType *ConstructorType = nullptr;
|
|
if (!MemberType->is<ErrorType>())
|
|
ConstructorType = MemberType->castTo<AnyFunctionType>()
|
|
->getResult()
|
|
->castTo<AnyFunctionType>();
|
|
|
|
// Add the constructor, possibly including any default arguments.
|
|
auto addConstructorImpl = [&](bool includeDefaultArgs = true) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink, CodeCompletionResult::ResultKind::Declaration,
|
|
getSemanticContext(CD, Reason));
|
|
Builder.setAssociatedDecl(CD);
|
|
if (IsSuperRefExpr) {
|
|
assert(addName.empty());
|
|
assert(isa<ConstructorDecl>(CurrDeclContext) &&
|
|
"can call super.init only inside a constructor");
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk("init");
|
|
} else if (!addName.empty()) {
|
|
Builder.addTextChunk(addName.str());
|
|
}
|
|
|
|
if (MemberType->is<ErrorType>()) {
|
|
addTypeAnnotation(Builder, MemberType);
|
|
return;
|
|
}
|
|
assert(ConstructorType);
|
|
|
|
if (!HaveLParen)
|
|
Builder.addLeftParen();
|
|
else
|
|
Builder.addAnnotatedLeftParen();
|
|
|
|
addParamPatternFromFunction(Builder, ConstructorType, CD,
|
|
includeDefaultArgs);
|
|
|
|
Builder.addRightParen();
|
|
addTypeAnnotation(Builder, ConstructorType->getResult());
|
|
};
|
|
|
|
if (ConstructorType && hasInterestingDefaultValues(ConstructorType))
|
|
addConstructorImpl(/*includeDefaultArgs*/ false);
|
|
addConstructorImpl();
|
|
}
|
|
|
|
void addConstructorCallsForType(Type type, Identifier name,
|
|
DeclVisibilityKind Reason) {
|
|
if (!Ctx.LangOpts.CodeCompleteInitsInPostfixExpr)
|
|
return;
|
|
|
|
assert(CurrDeclContext);
|
|
SmallVector<ValueDecl *, 16> initializers;
|
|
if (CurrDeclContext->lookupQualified(type, Ctx.Id_init, NL_QualifiedDefault,
|
|
TypeResolver.get(), initializers)) {
|
|
for (auto *init : initializers) {
|
|
if (init->isPrivateStdlibDecl(/*whitelistProtocols*/ false) ||
|
|
AvailabilityAttr::isUnavailable(init))
|
|
continue;
|
|
addConstructorCall(cast<ConstructorDecl>(init), Reason, name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void addSubscriptCall(const SubscriptDecl *SD, DeclVisibilityKind Reason) {
|
|
assert(!HaveDot && "can not add a subscript after a dot");
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Declaration,
|
|
getSemanticContext(SD, Reason));
|
|
Builder.setAssociatedDecl(SD);
|
|
Builder.addLeftBracket();
|
|
addPatternParameters(Builder, SD->getIndices());
|
|
Builder.addRightBracket();
|
|
|
|
// Add a type annotation.
|
|
Type T = SD->getElementType();
|
|
if (IsDynamicLookup) {
|
|
// Values of properties that were found on a AnyObject have
|
|
// Optional<T> type.
|
|
T = OptionalType::get(T);
|
|
}
|
|
addTypeAnnotation(Builder, T);
|
|
}
|
|
|
|
void addNominalTypeRef(const NominalTypeDecl *NTD,
|
|
DeclVisibilityKind Reason) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Declaration,
|
|
getSemanticContext(NTD, Reason));
|
|
Builder.setAssociatedDecl(NTD);
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk(NTD->getName().str());
|
|
addTypeAnnotation(Builder, NTD->getDeclaredType());
|
|
}
|
|
|
|
void addTypeAliasRef(const TypeAliasDecl *TAD, DeclVisibilityKind Reason) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Declaration,
|
|
getSemanticContext(TAD, Reason));
|
|
Builder.setAssociatedDecl(TAD);
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk(TAD->getName().str());
|
|
if (TAD->hasUnderlyingType() && !TAD->getUnderlyingType()->is<ErrorType>())
|
|
addTypeAnnotation(Builder, TAD->getUnderlyingType());
|
|
else {
|
|
addTypeAnnotation(Builder, TAD->getDeclaredType());
|
|
}
|
|
}
|
|
|
|
void addGenericTypeParamRef(const GenericTypeParamDecl *GP,
|
|
DeclVisibilityKind Reason) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Declaration,
|
|
getSemanticContext(GP, Reason));
|
|
Builder.setAssociatedDecl(GP);
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk(GP->getName().str());
|
|
addTypeAnnotation(Builder, GP->getDeclaredType());
|
|
}
|
|
|
|
void addAssociatedTypeRef(const AssociatedTypeDecl *AT,
|
|
DeclVisibilityKind Reason) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Declaration,
|
|
getSemanticContext(AT, Reason));
|
|
Builder.setAssociatedDecl(AT);
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk(AT->getName().str());
|
|
if (Type T = getAssociatedTypeType(AT))
|
|
addTypeAnnotation(Builder, T);
|
|
}
|
|
|
|
void addEnumElementRef(const EnumElementDecl *EED,
|
|
DeclVisibilityKind Reason,
|
|
bool HasTypeContext) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Declaration,
|
|
HasTypeContext ? SemanticContextKind::ExpressionSpecific
|
|
: getSemanticContext(EED, Reason));
|
|
Builder.setAssociatedDecl(EED);
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk(EED->getName().str());
|
|
if (EED->hasArgumentType())
|
|
addPatternFromType(Builder, EED->getArgumentType());
|
|
Type EnumType = EED->getType();
|
|
|
|
// Enum element is of function type such as EnumName.type -> Int ->
|
|
// EnumName; however we should show Int -> EnumName as the type
|
|
if (auto FuncType = EED->getType()->getAs<AnyFunctionType>()) {
|
|
EnumType = FuncType->getResult();
|
|
}
|
|
addTypeAnnotation(Builder, EnumType);
|
|
}
|
|
|
|
void addKeyword(StringRef Name, Type TypeAnnotation) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Keyword,
|
|
SemanticContextKind::None);
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk(Name);
|
|
if (!TypeAnnotation.isNull())
|
|
addTypeAnnotation(Builder, TypeAnnotation);
|
|
}
|
|
|
|
void addKeyword(StringRef Name, StringRef TypeAnnotation) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Keyword,
|
|
SemanticContextKind::None);
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk(Name);
|
|
if (!TypeAnnotation.empty())
|
|
Builder.addTypeAnnotation(TypeAnnotation);
|
|
}
|
|
|
|
void addDeclAttrParamKeyword(StringRef Name, StringRef Annotation,
|
|
bool NeedSpecify) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Keyword,
|
|
SemanticContextKind::None);
|
|
Builder.addDeclAttrParamKeyword(Name, Annotation, NeedSpecify);
|
|
}
|
|
|
|
void addDeclAttrKeyword(StringRef Name, StringRef Annotation) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Keyword,
|
|
SemanticContextKind::None);
|
|
Builder.addDeclAttrKeyword(Name, Annotation);
|
|
}
|
|
|
|
// Implement swift::VisibleDeclConsumer.
|
|
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason) override {
|
|
// Hide private stdlib declarations.
|
|
if (D->isPrivateStdlibDecl(/*whitelistProtocols*/false))
|
|
return;
|
|
if (AvailabilityAttr::isUnavailable(D))
|
|
return;
|
|
|
|
if (!D->hasType())
|
|
TypeResolver->resolveDeclSignature(D);
|
|
else if (isa<TypeAliasDecl>(D)) {
|
|
// A TypeAliasDecl might have type set, but not the underlying type.
|
|
TypeResolver->resolveDeclSignature(D);
|
|
}
|
|
|
|
switch (Kind) {
|
|
case LookupKind::ValueExpr:
|
|
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
|
|
if (ExprType->is<AnyMetatypeType>()) {
|
|
if (HaveDot)
|
|
return;
|
|
addConstructorCall(CD, Reason);
|
|
}
|
|
if (IsSuperRefExpr) {
|
|
if (!isa<ConstructorDecl>(CurrDeclContext))
|
|
return;
|
|
addConstructorCall(CD, Reason);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (HaveLParen)
|
|
return;
|
|
|
|
if (auto *VD = dyn_cast<VarDecl>(D)) {
|
|
addVarDeclRef(VD, Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *FD = dyn_cast<FuncDecl>(D)) {
|
|
// We can not call operators with a postfix parenthesis syntax.
|
|
if (FD->isBinaryOperator() || FD->isUnaryOperator())
|
|
return;
|
|
|
|
// We can not call accessors. We use VarDecls and SubscriptDecls to
|
|
// produce completions that refer to getters and setters.
|
|
if (FD->isAccessor())
|
|
return;
|
|
|
|
addMethodCall(FD, Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
|
|
addNominalTypeRef(NTD, Reason);
|
|
addConstructorCallsForType(NTD->getType(), NTD->getName(), Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *TAD = dyn_cast<TypeAliasDecl>(D)) {
|
|
addTypeAliasRef(TAD, Reason);
|
|
addConstructorCallsForType(TAD->getUnderlyingType(), TAD->getName(),
|
|
Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *GP = dyn_cast<GenericTypeParamDecl>(D)) {
|
|
addGenericTypeParamRef(GP, Reason);
|
|
for (auto *protocol : GP->getConformingProtocols(nullptr))
|
|
addConstructorCallsForType(protocol->getType(), GP->getName(),
|
|
Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *AT = dyn_cast<AssociatedTypeDecl>(D)) {
|
|
addAssociatedTypeRef(AT, Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *EED = dyn_cast<EnumElementDecl>(D)) {
|
|
addEnumElementRef(EED, Reason, /*HasTypeContext=*/false);
|
|
}
|
|
|
|
if (HaveDot)
|
|
return;
|
|
|
|
if (auto *SD = dyn_cast<SubscriptDecl>(D)) {
|
|
if (ExprType->is<AnyMetatypeType>())
|
|
return;
|
|
addSubscriptCall(SD, Reason);
|
|
return;
|
|
}
|
|
return;
|
|
|
|
case LookupKind::ValueInDeclContext:
|
|
case LookupKind::ImportFromModule:
|
|
if (auto *VD = dyn_cast<VarDecl>(D)) {
|
|
addVarDeclRef(VD, Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *FD = dyn_cast<FuncDecl>(D)) {
|
|
// We can not call operators with a postfix parenthesis syntax.
|
|
if (FD->isBinaryOperator() || FD->isUnaryOperator())
|
|
return;
|
|
|
|
// We can not call accessors. We use VarDecls and SubscriptDecls to
|
|
// produce completions that refer to getters and setters.
|
|
if (FD->isAccessor())
|
|
return;
|
|
|
|
addMethodCall(FD, Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
|
|
addNominalTypeRef(NTD, Reason);
|
|
addConstructorCallsForType(NTD->getType(), NTD->getName(), Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *TAD = dyn_cast<TypeAliasDecl>(D)) {
|
|
addTypeAliasRef(TAD, Reason);
|
|
addConstructorCallsForType(TAD->getUnderlyingType(), TAD->getName(),
|
|
Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *GP = dyn_cast<GenericTypeParamDecl>(D)) {
|
|
addGenericTypeParamRef(GP, Reason);
|
|
for (auto *protocol : GP->getConformingProtocols(nullptr))
|
|
addConstructorCallsForType(protocol->getType(), GP->getName(),
|
|
Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *AT = dyn_cast<AssociatedTypeDecl>(D)) {
|
|
addAssociatedTypeRef(AT, Reason);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
|
|
case LookupKind::EnumElement:
|
|
if (auto *EED = dyn_cast<EnumElementDecl>(D)) {
|
|
addEnumElementRef(EED, Reason, /*HasTypeContext=*/true);
|
|
}
|
|
return;
|
|
|
|
case LookupKind::Type:
|
|
case LookupKind::TypeInDeclContext:
|
|
if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
|
|
addNominalTypeRef(NTD, Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *TAD = dyn_cast<TypeAliasDecl>(D)) {
|
|
addTypeAliasRef(TAD, Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *GP = dyn_cast<GenericTypeParamDecl>(D)) {
|
|
addGenericTypeParamRef(GP, Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *AT = dyn_cast<AssociatedTypeDecl>(D)) {
|
|
addAssociatedTypeRef(AT, Reason);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
void getTupleExprCompletions(TupleType *ExprType) {
|
|
unsigned Index = 0;
|
|
for (auto TupleElt : ExprType->getElements()) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Pattern,
|
|
SemanticContextKind::CurrentNominal);
|
|
addLeadingDot(Builder);
|
|
if (TupleElt.hasName()) {
|
|
Builder.addTextChunk(TupleElt.getName().str());
|
|
} else {
|
|
llvm::SmallString<4> IndexStr;
|
|
{
|
|
llvm::raw_svector_ostream OS(IndexStr);
|
|
OS << Index;
|
|
}
|
|
Builder.addTextChunk(IndexStr.str());
|
|
}
|
|
addTypeAnnotation(Builder, TupleElt.getType());
|
|
Index++;
|
|
}
|
|
}
|
|
|
|
bool tryFunctionCallCompletions(Type ExprType, const ValueDecl *VD) {
|
|
ExprType = ExprType->getRValueType();
|
|
if (auto AFT = ExprType->getAs<AnyFunctionType>()) {
|
|
if (auto *AFD = dyn_cast_or_null<AbstractFunctionDecl>(VD)) {
|
|
addFunctionCallPattern(AFT, AFD);
|
|
} else {
|
|
addFunctionCallPattern(AFT);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool tryStdlibOptionalCompletions(Type ExprType) {
|
|
// FIXME: consider types convertible to T?.
|
|
|
|
ExprType = ExprType->getRValueType();
|
|
if (Type Unwrapped = ExprType->getOptionalObjectType()) {
|
|
llvm::SaveAndRestore<bool> ChangeNeedOptionalUnwrap(NeedOptionalUnwrap,
|
|
true);
|
|
if (DotLoc.isValid()) {
|
|
NumBytesToEraseForOptionalUnwrap = Ctx.SourceMgr.getByteDistance(
|
|
DotLoc, Ctx.SourceMgr.getCodeCompletionLoc());
|
|
} else {
|
|
NumBytesToEraseForOptionalUnwrap = 0;
|
|
}
|
|
if (NumBytesToEraseForOptionalUnwrap <=
|
|
CodeCompletionResult::MaxNumBytesToErase)
|
|
lookupVisibleMemberDecls(*this, Unwrapped, CurrDeclContext,
|
|
TypeResolver.get());
|
|
} else if (Type Unwrapped = ExprType->getImplicitlyUnwrappedOptionalObjectType()) {
|
|
lookupVisibleMemberDecls(*this, Unwrapped, CurrDeclContext,
|
|
TypeResolver.get());
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
// Ignore the internal members of Optional, like getLogicValue() and
|
|
// getMirror().
|
|
// These are not commonly used and cause noise and confusion when showing
|
|
// among the the members of the underlying type. If someone really wants to
|
|
// use them they can write them directly.
|
|
|
|
return true;
|
|
}
|
|
|
|
void getValueExprCompletions(Type ExprType, ValueDecl *VD = nullptr) {
|
|
Kind = LookupKind::ValueExpr;
|
|
NeedLeadingDot = !HaveDot;
|
|
this->ExprType = ExprType;
|
|
bool Done = false;
|
|
if (tryFunctionCallCompletions(ExprType, VD))
|
|
Done = true;
|
|
if (auto MT = ExprType->getAs<ModuleType>()) {
|
|
Module *M = MT->getModule();
|
|
if (CurrDeclContext->getParentModule() != M) {
|
|
// Only use the cache if it is not the current module.
|
|
RequestedCachedResults = RequestedResultsTy::fromModule(M)
|
|
.needLeadingDot(needDot());
|
|
Done = true;
|
|
}
|
|
}
|
|
if (auto *TT = ExprType->getRValueType()->getAs<TupleType>()) {
|
|
getTupleExprCompletions(TT);
|
|
Done = true;
|
|
}
|
|
tryStdlibOptionalCompletions(ExprType);
|
|
if (!Done) {
|
|
lookupVisibleMemberDecls(*this, ExprType, CurrDeclContext,
|
|
TypeResolver.get());
|
|
}
|
|
}
|
|
|
|
void getValueCompletionsInDeclContext(SourceLoc Loc) {
|
|
Kind = LookupKind::ValueInDeclContext;
|
|
NeedLeadingDot = false;
|
|
lookupVisibleDecls(*this, CurrDeclContext, TypeResolver.get(),
|
|
/*IncludeTopLevel=*/false, Loc);
|
|
|
|
RequestedCachedResults = RequestedResultsTy::toplevelResults();
|
|
}
|
|
|
|
bool isErrorDecl(ValueDecl *VD) {
|
|
// This VD is an exception if it conforms the base error type
|
|
// or, if it has extensions that conform the base error type
|
|
if (auto TD = dyn_cast<NominalTypeDecl>(VD)) {
|
|
auto ContainsErrorProtocol = [&](ArrayRef<ProtocolDecl*> Protocols) {
|
|
auto BaseException = CurrDeclContext->getASTContext().
|
|
getExceptionTypeDecl();
|
|
return std::find(Protocols.begin(), Protocols.end(), BaseException) !=
|
|
Protocols.end();
|
|
};
|
|
if (TD->getKind() != DeclKind::Protocol) {
|
|
return ContainsErrorProtocol(TD->getAllProtocols());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isErrorType(Type Ty) {
|
|
if (Ty.isNull())
|
|
return false;
|
|
if (auto NT = dyn_cast<NominalType>(Ty.getPointer())) {
|
|
return isErrorDecl(NT->getDecl());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void getThrowStmtCompletion(SourceLoc Loc) {
|
|
Kind = LookupKind::ValueInDeclContext;
|
|
NeedLeadingDot = false;
|
|
auto Consumer = FilteredDeclConsumer([&](ValueDecl *VD,
|
|
DeclVisibilityKind Kind) {
|
|
auto IsOfErrorType = [&](ValueDecl *VD) {
|
|
if (!VD->hasType())
|
|
TypeResolver->resolveDeclSignature(VD);
|
|
return isErrorType(VD->getType());
|
|
};
|
|
return isErrorDecl(VD) || IsOfErrorType(VD);
|
|
}, *this);
|
|
lookupVisibleDecls(Consumer, CurrDeclContext, TypeResolver.get(),
|
|
/*IncludeTopLevel=*/true, Loc);
|
|
}
|
|
|
|
void getTypeContextEnumElementCompletions(SourceLoc Loc) {
|
|
llvm::SaveAndRestore<LookupKind> ChangeLookupKind(
|
|
Kind, LookupKind::EnumElement);
|
|
NeedLeadingDot = !HaveDot;
|
|
|
|
const DeclContext *FunctionDC = CurrDeclContext;
|
|
const AbstractFunctionDecl *CurrentFunction = nullptr;
|
|
while (FunctionDC->isLocalContext()) {
|
|
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(FunctionDC)) {
|
|
CurrentFunction = AFD;
|
|
break;
|
|
}
|
|
FunctionDC = FunctionDC->getParent();
|
|
}
|
|
if (!CurrentFunction)
|
|
return;
|
|
|
|
auto *Switch = cast_or_null<SwitchStmt>(
|
|
findNearestStmt(CurrentFunction, Loc, StmtKind::Switch));
|
|
if (!Switch)
|
|
return;
|
|
auto Ty = Switch->getSubjectExpr()->getType();
|
|
if (!Ty)
|
|
return;
|
|
auto *TheEnumDecl = dyn_cast_or_null<EnumDecl>(Ty->getAnyNominal());
|
|
if (!TheEnumDecl)
|
|
return;
|
|
for (auto Element : TheEnumDecl->getAllElements()) {
|
|
foundDecl(Element, DeclVisibilityKind::MemberOfCurrentNominal);
|
|
}
|
|
}
|
|
|
|
class FilteredDeclConsumer : public VisibleDeclConsumer {
|
|
llvm::function_ref<bool(ValueDecl *VD, DeclVisibilityKind Reason)> Filter;
|
|
VisibleDeclConsumer &RealConsumer;
|
|
public:
|
|
FilteredDeclConsumer(llvm::function_ref<bool(ValueDecl *VD, DeclVisibilityKind
|
|
Reason)> Filter, VisibleDeclConsumer &RealConsumer) : Filter(Filter),
|
|
RealConsumer(RealConsumer) {}
|
|
virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override {
|
|
if (Filter(VD, Reason)) {
|
|
RealConsumer.foundDecl(VD, Reason);
|
|
}
|
|
}
|
|
};
|
|
|
|
void getExceptionCompletions(SourceLoc Loc) {
|
|
Kind = LookupKind::TypeInDeclContext;
|
|
FilteredDeclConsumer Consumer([&](ValueDecl *VD, DeclVisibilityKind Reason) {
|
|
return isErrorDecl(VD);
|
|
}, *this);
|
|
lookupVisibleDecls(Consumer, CurrDeclContext, TypeResolver.get(), true, Loc);
|
|
}
|
|
|
|
void getExceptionHandleClauses() {
|
|
auto AddLet = [&](bool NeedWhere) {
|
|
CodeCompletionResultBuilder Builder(Sink,
|
|
CodeCompletionResult::ResultKind::
|
|
Keyword,
|
|
SemanticContextKind::None);
|
|
Builder.addDeclIntroducer("let ");
|
|
Builder.addTextChunk("e");
|
|
if (NeedWhere) {
|
|
// TODO: add where clause
|
|
}
|
|
Builder.addBraceStmtWithCursor();
|
|
};
|
|
AddLet(false);
|
|
CodeCompletionResultBuilder Builder(Sink,
|
|
CodeCompletionResult::ResultKind::
|
|
Keyword,
|
|
SemanticContextKind::None);
|
|
Builder.addTextChunk("_");
|
|
Builder.addBraceStmtWithCursor();
|
|
}
|
|
|
|
void getTypeCompletions(Type BaseType) {
|
|
Kind = LookupKind::Type;
|
|
this->BaseType = BaseType;
|
|
NeedLeadingDot = !HaveDot;
|
|
Type MetaBase = MetatypeType::get(BaseType);
|
|
lookupVisibleMemberDecls(*this, MetaBase,
|
|
CurrDeclContext, TypeResolver.get());
|
|
addKeyword("Type", MetaBase);
|
|
addKeyword("self", BaseType);
|
|
}
|
|
|
|
void getAttributeDeclCompletions(bool IsInSil, Optional<DeclKind> DK) {
|
|
// FIXME: also include user-defined attribute keywords
|
|
StringRef TargetName = "Declaration";
|
|
if (DK.hasValue()) {
|
|
switch (DK.getValue()) {
|
|
#define DECL(Id, ...) \
|
|
case DeclKind::Id: \
|
|
TargetName = #Id; \
|
|
break;
|
|
#include "swift/AST/DeclNodes.def"
|
|
}
|
|
}
|
|
std::string Description = TargetName.str() + " Attribute";
|
|
#define DECL_ATTR(KEYWORD, NAME, ...) \
|
|
if (!DeclAttribute::isUserInaccessible(DAK_##NAME) && \
|
|
!DeclAttribute::isDeclModifier(DAK_##NAME) && \
|
|
!DeclAttribute::shouldBeRejectedByParser(DAK_##NAME) && \
|
|
(!DeclAttribute::isSilOnly(DAK_##NAME) || IsInSil)) { \
|
|
if (!DK.hasValue() || DeclAttribute::canAttributeAppearOnDeclKind \
|
|
(DAK_##NAME, DK.getValue())) \
|
|
addDeclAttrKeyword(#KEYWORD, Description); \
|
|
}
|
|
#include "swift/AST/Attr.def"
|
|
}
|
|
|
|
void getAttributeDeclParamCompletions(DeclAttrKind AttrKind, int ParamIndex) {
|
|
if (AttrKind == DAK_Availability) {
|
|
if (ParamIndex == 0) {
|
|
addDeclAttrParamKeyword("*", "Platform", false);
|
|
#define AVAILABILITY_PLATFORM(X, PrettyName) \
|
|
addDeclAttrParamKeyword(#X, "Platform", false);
|
|
#include "swift/AST/PlatformKinds.def"
|
|
} else {
|
|
addDeclAttrParamKeyword("unavailable", "", false);
|
|
addDeclAttrParamKeyword("message", "Specify message", true);
|
|
addDeclAttrParamKeyword("renamed", "Specify replacing name", true);
|
|
addDeclAttrParamKeyword("introduced", "Specify version number", true);
|
|
addDeclAttrParamKeyword("deprecated", "Specify version number", true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void getTypeCompletionsInDeclContext(SourceLoc Loc) {
|
|
Kind = LookupKind::TypeInDeclContext;
|
|
lookupVisibleDecls(*this, CurrDeclContext, TypeResolver.get(),
|
|
/*IncludeTopLevel=*/false, Loc);
|
|
|
|
RequestedCachedResults =
|
|
RequestedResultsTy::toplevelResults().onlyTypes();
|
|
}
|
|
|
|
void getToplevelCompletions(bool OnlyTypes) {
|
|
Kind = OnlyTypes ? LookupKind::TypeInDeclContext
|
|
: LookupKind::ValueInDeclContext;
|
|
NeedLeadingDot = false;
|
|
Module *M = CurrDeclContext->getParentModule();
|
|
AccessFilteringDeclConsumer FilteringConsumer(CurrDeclContext, *this,
|
|
TypeResolver.get());
|
|
M->lookupVisibleDecls({}, FilteringConsumer, NLKind::UnqualifiedLookup);
|
|
}
|
|
|
|
void getVisibleDeclsOfModule(const Module *TheModule,
|
|
ArrayRef<std::string> AccessPath,
|
|
bool ResultsHaveLeadingDot) {
|
|
Kind = LookupKind::ImportFromModule;
|
|
NeedLeadingDot = ResultsHaveLeadingDot;
|
|
|
|
llvm::SmallVector<std::pair<Identifier, SourceLoc>, 1> LookupAccessPath;
|
|
for (auto Piece : AccessPath) {
|
|
LookupAccessPath.push_back(
|
|
std::make_pair(Ctx.getIdentifier(Piece), SourceLoc()));
|
|
}
|
|
AccessFilteringDeclConsumer FilteringConsumer(CurrDeclContext, *this,
|
|
TypeResolver.get());
|
|
TheModule->lookupVisibleDecls(LookupAccessPath, FilteringConsumer,
|
|
NLKind::UnqualifiedLookup);
|
|
}
|
|
};
|
|
|
|
class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
|
|
CodeCompletionResultSink &Sink;
|
|
OwnedResolver TypeResolver;
|
|
const DeclContext *CurrDeclContext;
|
|
SmallVectorImpl<StringRef> &ParsedKeywords;
|
|
|
|
public:
|
|
CompletionOverrideLookup(CodeCompletionResultSink &Sink,
|
|
ASTContext &Ctx,
|
|
const DeclContext *CurrDeclContext,
|
|
SmallVectorImpl<StringRef> &ParsedKeywords)
|
|
: Sink(Sink), TypeResolver(createLazyResolver(Ctx)),
|
|
CurrDeclContext(CurrDeclContext),
|
|
ParsedKeywords(ParsedKeywords){}
|
|
|
|
bool isKeywordSpecified(StringRef Word) {
|
|
return std::find(ParsedKeywords.begin(), ParsedKeywords.end(), Word)
|
|
!= ParsedKeywords.end();
|
|
}
|
|
|
|
void addMethodOverride(const FuncDecl *FD, DeclVisibilityKind Reason) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Declaration,
|
|
SemanticContextKind::Super);
|
|
Builder.setAssociatedDecl(FD);
|
|
|
|
class DeclNameOffsetLocatorPrinter : public StreamPrinter {
|
|
public:
|
|
using StreamPrinter::StreamPrinter;
|
|
|
|
Optional<unsigned> NameOffset;
|
|
|
|
void printDeclLoc(const Decl *D) override {
|
|
if (!NameOffset.hasValue())
|
|
NameOffset = OS.tell();
|
|
}
|
|
};
|
|
|
|
llvm::SmallString<256> DeclStr;
|
|
unsigned NameOffset = 0;
|
|
{
|
|
llvm::raw_svector_ostream OS(DeclStr);
|
|
DeclNameOffsetLocatorPrinter Printer(OS);
|
|
PrintOptions Options;
|
|
Options.PrintDefaultParameterPlaceholder = false;
|
|
Options.PrintImplicitAttrs = false;
|
|
Options.ExclusiveAttrList.push_back(DAK_NoReturn);
|
|
Options.PrintOverrideKeyword = false;
|
|
FD->print(Printer, Options);
|
|
NameOffset = Printer.NameOffset.getValue();
|
|
}
|
|
|
|
Accessibility AccessibilityOfContext;
|
|
if (auto *NTD = dyn_cast<NominalTypeDecl>(CurrDeclContext))
|
|
AccessibilityOfContext = NTD->getFormalAccess();
|
|
else
|
|
AccessibilityOfContext = cast<ExtensionDecl>(CurrDeclContext)
|
|
->getExtendedType()
|
|
->getAnyNominal()
|
|
->getFormalAccess();
|
|
// If the developer has not input "func", we need to add necessary keywords
|
|
if (!isKeywordSpecified("func")) {
|
|
if (!isKeywordSpecified("private") &&
|
|
!isKeywordSpecified("public") &&
|
|
!isKeywordSpecified("internal"))
|
|
Builder.addAccessControlKeyword(std::min(FD->getFormalAccess(),
|
|
AccessibilityOfContext));
|
|
|
|
if (Reason == DeclVisibilityKind::MemberOfSuper &&
|
|
!isKeywordSpecified("override"))
|
|
Builder.addOverrideKeyword();
|
|
Builder.addDeclIntroducer(DeclStr.str().substr(0, NameOffset));
|
|
}
|
|
Builder.addTextChunk(DeclStr.str().substr(NameOffset));
|
|
Builder.addBraceStmtWithCursor();
|
|
}
|
|
|
|
void addConstructor(const ConstructorDecl *CD) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Declaration,
|
|
SemanticContextKind::Super);
|
|
Builder.setAssociatedDecl(CD);
|
|
|
|
llvm::SmallString<256> DeclStr;
|
|
{
|
|
llvm::raw_svector_ostream OS(DeclStr);
|
|
PrintOptions Options;
|
|
Options.PrintImplicitAttrs = false;
|
|
Options.ExclusiveAttrList.push_back(DAK_NoReturn);
|
|
Options.PrintDefaultParameterPlaceholder = false;
|
|
CD->print(OS, Options);
|
|
}
|
|
Builder.addTextChunk(DeclStr);
|
|
Builder.addBraceStmtWithCursor();
|
|
}
|
|
|
|
// Implement swift::VisibleDeclConsumer.
|
|
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason) override {
|
|
if (Reason == DeclVisibilityKind::MemberOfCurrentNominal)
|
|
return;
|
|
|
|
if (D->getAttrs().hasAttribute<FinalAttr>())
|
|
return;
|
|
|
|
if (!D->hasType())
|
|
TypeResolver->resolveDeclSignature(D);
|
|
|
|
if (auto *FD = dyn_cast<FuncDecl>(D)) {
|
|
// We can override operators as members.
|
|
if (FD->isBinaryOperator() || FD->isUnaryOperator())
|
|
return;
|
|
|
|
// We can not override individual accessors.
|
|
if (FD->isAccessor())
|
|
return;
|
|
|
|
addMethodOverride(FD, Reason);
|
|
return;
|
|
}
|
|
|
|
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
|
|
if (!isa<ProtocolDecl>(CD->getDeclContext()))
|
|
return;
|
|
if (CD->isRequired() || CD->isDesignatedInit())
|
|
addConstructor(CD);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void addDesignatedInitializers(Type CurrTy) {
|
|
if (!CurrTy)
|
|
return;
|
|
const auto *NTD = CurrTy->getAnyNominal();
|
|
if (!NTD)
|
|
return;
|
|
const auto *CD = dyn_cast<ClassDecl>(NTD);
|
|
if (!CD)
|
|
return;
|
|
if (!CD->getSuperclass())
|
|
return;
|
|
CD = CD->getSuperclass()->getClassOrBoundGenericClass();
|
|
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);
|
|
}
|
|
}
|
|
|
|
void getOverrideCompletions(SourceLoc Loc) {
|
|
auto TypeContext = CurrDeclContext->getInnermostTypeContext();
|
|
if (!TypeContext)
|
|
return;
|
|
Type CurrTy = TypeContext->getDeclaredTypeInContext();
|
|
lookupVisibleMemberDecls(*this, CurrTy, CurrDeclContext,
|
|
TypeResolver.get());
|
|
addDesignatedInitializers(CurrTy);
|
|
}
|
|
};
|
|
|
|
} // end unnamed namespace
|
|
|
|
void CodeCompletionCallbacksImpl::completeDotExpr(Expr *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;
|
|
ParsedExpr = E;
|
|
this->DotLoc = DotLoc;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completePostfixExprBeginning() {
|
|
assert(P.Tok.is(tok::code_complete));
|
|
|
|
// Don't produce any results in an enum element.
|
|
if (InEnumElementRawValue)
|
|
return;
|
|
|
|
Kind = CompletionKind::PostfixExprBeginning;
|
|
CurDeclContext = P.CurDeclContext;
|
|
CStyleForLoopIterationVariable =
|
|
CodeCompletionCallbacks::CStyleForLoopIterationVariable;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E) {
|
|
assert(P.Tok.is(tok::code_complete));
|
|
|
|
// Don't produce any results in an enum element.
|
|
if (InEnumElementRawValue)
|
|
return;
|
|
|
|
Kind = CompletionKind::PostfixExpr;
|
|
ParsedExpr = E;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completePostfixExprParen(Expr *E) {
|
|
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;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeExprSuper(SuperRefExpr *SRE) {
|
|
// Don't produce any results in an enum element.
|
|
if (InEnumElementRawValue)
|
|
return;
|
|
|
|
Kind = CompletionKind::SuperExpr;
|
|
ParsedExpr = SRE;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeExprSuperDot(SuperRefExpr *SRE) {
|
|
// Don't produce any results in an enum element.
|
|
if (InEnumElementRawValue)
|
|
return;
|
|
|
|
Kind = CompletionKind::SuperExprDot;
|
|
ParsedExpr = SRE;
|
|
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::completeDeclAttrKeyword(Decl *D,
|
|
bool Sil,
|
|
bool Param) {
|
|
Kind = CompletionKind::AttributeBegin;
|
|
IsInSil = Sil;
|
|
if (Param) {
|
|
AttTargetDK = DeclKind::Param;
|
|
} else if (D) {
|
|
AttTargetDK = D->getKind();
|
|
}
|
|
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::completeCaseStmtBeginning() {
|
|
assert(!InEnumElementRawValue);
|
|
|
|
Kind = CompletionKind::CaseStmtBeginning;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeCaseStmtDotPrefix() {
|
|
assert(!InEnumElementRawValue);
|
|
|
|
Kind = CompletionKind::CaseStmtDotPrefix;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeCatchStmtBeginning() {
|
|
assert(!InEnumElementRawValue);
|
|
|
|
Kind = CompletionKind::CatchStmtBeginning;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeThrowStmtBeginning() {
|
|
assert(!InEnumElementRawValue);
|
|
|
|
Kind = CompletionKind::ThrowStmtBeginning;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::completeNominalMemberBeginning(
|
|
SmallVectorImpl<StringRef> &Keywords) {
|
|
assert(!InEnumElementRawValue);
|
|
ParsedKeywords.clear();
|
|
ParsedKeywords.append(Keywords.begin(), Keywords.end());
|
|
Kind = CompletionKind::NominalMemberBeginning;
|
|
CurDeclContext = P.CurDeclContext;
|
|
}
|
|
|
|
static bool isDynamicLookup(Type T) {
|
|
if (auto *PT = T->getRValueType()->getAs<ProtocolType>())
|
|
return PT->getDecl()->isSpecificProtocol(KnownProtocolKind::AnyObject);
|
|
return false;
|
|
}
|
|
|
|
static bool isClangSubModule(Module *TheModule) {
|
|
if (auto ClangMod = TheModule->findUnderlyingClangModule())
|
|
return ClangMod->isSubModule();
|
|
return false;
|
|
}
|
|
|
|
static void addDeclKeywords(CodeCompletionResultSink &Sink) {
|
|
auto AddKeyword = [&](StringRef Name) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink, CodeCompletionResult::ResultKind::Keyword,
|
|
SemanticContextKind::None);
|
|
Builder.addTextChunk(Name);
|
|
};
|
|
|
|
#define DECL_KEYWORD(kw) AddKeyword(#kw);
|
|
#include "swift/Parse/Tokens.def"
|
|
// Context-sensitive keywords.
|
|
AddKeyword("weak");
|
|
AddKeyword("unowned");
|
|
AddKeyword("optional");
|
|
AddKeyword("required");
|
|
AddKeyword("lazy");
|
|
AddKeyword("final");
|
|
AddKeyword("dynamic");
|
|
AddKeyword("prefix");
|
|
AddKeyword("postfix");
|
|
AddKeyword("infix");
|
|
AddKeyword("override");
|
|
AddKeyword("mutating");
|
|
AddKeyword("nonmutating");
|
|
AddKeyword("convenience");
|
|
}
|
|
|
|
static void addStmtKeywords(CodeCompletionResultSink &Sink) {
|
|
auto AddKeyword = [&](StringRef Name, StringRef TypeAnnotation) {
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink, CodeCompletionResult::ResultKind::Keyword,
|
|
SemanticContextKind::None);
|
|
Builder.addTextChunk(Name);
|
|
if (!TypeAnnotation.empty())
|
|
Builder.addTypeAnnotation(TypeAnnotation);
|
|
};
|
|
|
|
#define STMT_KEYWORD(kw) AddKeyword(#kw, StringRef());
|
|
#include "swift/Parse/Tokens.def"
|
|
|
|
// FIXME: The pedantically correct way to find the type is to resolve the
|
|
// Swift.StringLiteralType type.
|
|
AddKeyword("__FUNCTION__", "String");
|
|
AddKeyword("__FILE__", "String");
|
|
// Same: Swift.IntegerLiteralType.
|
|
AddKeyword("__LINE__", "Int");
|
|
AddKeyword("__COLUMN__", "Int");
|
|
// Same: Swift.BooleanLiteralType.
|
|
AddKeyword("false", "Bool");
|
|
AddKeyword("true", "Bool");
|
|
|
|
AddKeyword("__DSO_HANDLE__", "UnsafeMutablePointer<Void>");
|
|
|
|
AddKeyword("nil", StringRef());
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink) {
|
|
switch (Kind) {
|
|
case CompletionKind::None:
|
|
case CompletionKind::DotExpr:
|
|
case CompletionKind::AttributeDeclParen:
|
|
case CompletionKind::AttributeBegin:
|
|
break;
|
|
|
|
case CompletionKind::PostfixExprBeginning:
|
|
addDeclKeywords(Sink);
|
|
addStmtKeywords(Sink);
|
|
break;
|
|
|
|
case CompletionKind::PostfixExpr:
|
|
case CompletionKind::PostfixExprParen:
|
|
case CompletionKind::SuperExpr:
|
|
case CompletionKind::SuperExprDot:
|
|
case CompletionKind::TypeSimpleBeginning:
|
|
case CompletionKind::TypeIdentifierWithDot:
|
|
case CompletionKind::TypeIdentifierWithoutDot:
|
|
case CompletionKind::CaseStmtBeginning:
|
|
case CompletionKind::CaseStmtDotPrefix:
|
|
case CompletionKind::CatchStmtBeginning:
|
|
case CompletionKind::ThrowStmtBeginning:
|
|
break;
|
|
|
|
case CompletionKind::NominalMemberBeginning:
|
|
addDeclKeywords(Sink);
|
|
break;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
class NearestExprParentFinder : public ASTWalker {
|
|
Expr *ChildExpr;
|
|
llvm::function_ref<bool(Expr*)> Predicate;
|
|
|
|
public:
|
|
llvm::SmallVector<Expr*, 5> Ancestors;
|
|
Expr *ParentExpr = nullptr;
|
|
NearestExprParentFinder(Expr* ChildExpr, llvm::function_ref<bool(Expr*)>
|
|
Predicate) : ChildExpr(ChildExpr), Predicate(Predicate){}
|
|
|
|
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
|
|
if (E == ChildExpr) {
|
|
if (!Ancestors.empty())
|
|
ParentExpr = Ancestors.back();
|
|
return { false, nullptr };
|
|
}
|
|
if (Predicate(E))
|
|
Ancestors.push_back(E);
|
|
return { true, E };
|
|
}
|
|
|
|
Expr *walkToExprPost(Expr *E) override {
|
|
if (Predicate(E))
|
|
Ancestors.pop_back();
|
|
return E;
|
|
}
|
|
};
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::doneParsing() {
|
|
if (Kind == CompletionKind::None) {
|
|
return;
|
|
}
|
|
|
|
// Add keywords even if type checking fails completely.
|
|
addKeywords(CompletionContext.getResultSink());
|
|
|
|
if (!typecheckContext())
|
|
return;
|
|
|
|
if (DelayedParsedDecl && !typecheckDelayedParsedDecl())
|
|
return;
|
|
|
|
if (auto *AFD = dyn_cast_or_null<AbstractFunctionDecl>(DelayedParsedDecl))
|
|
CurDeclContext = AFD;
|
|
|
|
if (ParsedExpr && !typecheckParsedExpr()) {
|
|
if (Kind != CompletionKind::PostfixExprParen)
|
|
return;
|
|
}
|
|
|
|
if (!ParsedTypeLoc.isNull() && !typecheckParsedType())
|
|
return;
|
|
|
|
CompletionLookup Lookup(CompletionContext.getResultSink(), P.Context,
|
|
CurDeclContext);
|
|
|
|
auto DoPostfixExprBeginning = [&] {
|
|
if (CStyleForLoopIterationVariable)
|
|
Lookup.addExpressionSpecificDecl(CStyleForLoopIterationVariable);
|
|
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
|
|
Lookup.getValueCompletionsInDeclContext(Loc);
|
|
};
|
|
|
|
switch (Kind) {
|
|
case CompletionKind::None:
|
|
llvm_unreachable("should be already handled");
|
|
return;
|
|
|
|
case CompletionKind::DotExpr: {
|
|
Lookup.setHaveDot(DotLoc);
|
|
Type OriginalType = ParsedExpr->getType();
|
|
Type ExprType = OriginalType;
|
|
|
|
// If there is no nominal type in the expr, try to find nominal types
|
|
// in the ancestors of the expr.
|
|
if (!OriginalType->getAnyNominal()) {
|
|
NearestExprParentFinder Walker(ParsedExpr, [&](Expr* E) {
|
|
return E->getType() && E->getType()->getAnyNominal();
|
|
});
|
|
CurDeclContext->walkContext(Walker);
|
|
ExprType = Walker.ParentExpr ? Walker.ParentExpr->getType():
|
|
OriginalType;
|
|
}
|
|
|
|
if (isDynamicLookup(ExprType))
|
|
Lookup.setIsDynamicLookup();
|
|
Lookup.getValueExprCompletions(ExprType);
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::PostfixExprBeginning: {
|
|
DoPostfixExprBeginning();
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::PostfixExpr: {
|
|
Type ExprType = ParsedExpr->getType();
|
|
if (isDynamicLookup(ExprType))
|
|
Lookup.setIsDynamicLookup();
|
|
Lookup.getValueExprCompletions(ExprType);
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::PostfixExprParen: {
|
|
Lookup.setHaveLParen(true);
|
|
ValueDecl *VD = nullptr;
|
|
if (auto *AE = dyn_cast<ApplyExpr>(ParsedExpr)) {
|
|
if (auto *DRE = dyn_cast<DeclRefExpr>(AE->getFn()))
|
|
VD = DRE->getDecl();
|
|
}
|
|
if (ParsedExpr->getType())
|
|
Lookup.getValueExprCompletions(ParsedExpr->getType(), VD);
|
|
|
|
if (!Lookup.FoundFunctionCalls ||
|
|
(Lookup.FoundFunctionCalls &&
|
|
Lookup.FoundFunctionsWithoutFirstKeyword)) {
|
|
Lookup.setHaveLParen(false);
|
|
DoPostfixExprBeginning();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::SuperExpr: {
|
|
Lookup.setIsSuperRefExpr();
|
|
Lookup.getValueExprCompletions(ParsedExpr->getType());
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::SuperExprDot: {
|
|
Lookup.setIsSuperRefExpr();
|
|
Lookup.setHaveDot(SourceLoc());
|
|
Lookup.getValueExprCompletions(ParsedExpr->getType());
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::TypeSimpleBeginning: {
|
|
Lookup.getTypeCompletionsInDeclContext(
|
|
P.Context.SourceMgr.getCodeCompletionLoc());
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::TypeIdentifierWithDot: {
|
|
Lookup.setHaveDot(SourceLoc());
|
|
Lookup.getTypeCompletions(ParsedTypeLoc.getType());
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::TypeIdentifierWithoutDot: {
|
|
Lookup.getTypeCompletions(ParsedTypeLoc.getType());
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::CatchStmtBeginning: {
|
|
Lookup.getExceptionCompletions(P.Context.SourceMgr.getCodeCompletionLoc());
|
|
Lookup.getExceptionHandleClauses();
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::ThrowStmtBeginning: {
|
|
Lookup.getThrowStmtCompletion(P.Context.SourceMgr.getCodeCompletionLoc());
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::CaseStmtBeginning: {
|
|
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
|
|
Lookup.getValueCompletionsInDeclContext(Loc);
|
|
Lookup.getTypeContextEnumElementCompletions(Loc);
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::CaseStmtDotPrefix: {
|
|
Lookup.setHaveDot(SourceLoc());
|
|
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
|
|
Lookup.getTypeContextEnumElementCompletions(Loc);
|
|
break;
|
|
}
|
|
|
|
case CompletionKind::NominalMemberBeginning: {
|
|
Lookup.discardTypeResolver();
|
|
CompletionOverrideLookup OverrideLookup(CompletionContext.getResultSink(),
|
|
P.Context, CurDeclContext,
|
|
ParsedKeywords);
|
|
OverrideLookup.getOverrideCompletions(SourceLoc());
|
|
break;
|
|
}
|
|
case CompletionKind::AttributeBegin: {
|
|
Lookup.getAttributeDeclCompletions(IsInSil, AttTargetDK);
|
|
break;
|
|
}
|
|
case CompletionKind::AttributeDeclParen: {
|
|
Lookup.getAttributeDeclParamCompletions(AttrKind, AttrParamIndex);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Lookup.RequestedCachedResults) {
|
|
// Create helpers for result caching.
|
|
auto &SwiftContext = P.Context;
|
|
|
|
// 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.
|
|
const SourceFile &SF = P.SF;
|
|
|
|
auto FillCacheCallback = [&SwiftContext, &SF](
|
|
CodeCompletionCache &Cache, const CodeCompletionCache::Key &K,
|
|
const Module *TheModule) {
|
|
auto V = Cache.createValue();
|
|
lookupCodeCompletionResultsFromModule(V->Sink, TheModule, K.AccessPath,
|
|
K.ResultsHaveLeadingDot, &SF);
|
|
Cache.set(K, V);
|
|
return V;
|
|
};
|
|
|
|
auto &Request = Lookup.RequestedCachedResults.getValue();
|
|
|
|
llvm::DenseSet<CodeCompletionCache::Key> ImportsSeen;
|
|
auto handleImport = [&](Module::ImportedModule Import) {
|
|
Module *TheModule = Import.second;
|
|
Module::AccessPathTy Path = Import.first;
|
|
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(Piece.first.str());
|
|
}
|
|
|
|
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, TheModule->Name.str(),
|
|
AccessPath, Request.NeedLeadingDot,
|
|
SF.hasTestableImport(TheModule)};
|
|
std::pair<decltype(ImportsSeen)::iterator, bool>
|
|
Result = ImportsSeen.insert(K);
|
|
if (!Result.second)
|
|
return; // already handled.
|
|
|
|
CompletionContext.Cache.getResults(K, CompletionContext.getResultSink(),
|
|
Request.OnlyTypes, TheModule,
|
|
FillCacheCallback);
|
|
}
|
|
};
|
|
|
|
if (Request.TheModule) {
|
|
Lookup.discardTypeResolver();
|
|
|
|
// FIXME: actually check imports.
|
|
const_cast<Module*>(Request.TheModule)
|
|
->forAllVisibleModules({}, handleImport);
|
|
} else {
|
|
// Add results from current module.
|
|
Lookup.getToplevelCompletions(Request.OnlyTypes);
|
|
Lookup.discardTypeResolver();
|
|
|
|
// Add results for all imported modules.
|
|
SmallVector<Module::ImportedModule, 4> Imports;
|
|
auto *SF = CurDeclContext->getParentSourceFile();
|
|
SF->getImportedModules(Imports, Module::ImportFilter::All);
|
|
|
|
for (auto Imported : Imports) {
|
|
Module *TheModule = Imported.second;
|
|
Module::AccessPathTy AccessPath = Imported.first;
|
|
TheModule->forAllVisibleModules(AccessPath, handleImport);
|
|
}
|
|
}
|
|
Lookup.RequestedCachedResults.reset();
|
|
}
|
|
|
|
deliverCompletionResults();
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::deliverCompletionResults() {
|
|
auto Results = CompletionContext.takeResults();
|
|
if (!Results.empty()) {
|
|
Consumer.handleResults(Results);
|
|
DeliveredResults = true;
|
|
}
|
|
}
|
|
|
|
void PrintingCodeCompletionConsumer::handleResults(
|
|
MutableArrayRef<CodeCompletionResult *> Results) {
|
|
unsigned NumResults = 0;
|
|
for (auto Result : Results) {
|
|
if (!IncludeKeywords && Result->getKind() == CodeCompletionResult::Keyword)
|
|
continue;
|
|
NumResults++;
|
|
}
|
|
if (NumResults == 0)
|
|
return;
|
|
|
|
OS << "Begin completions, " << NumResults << " items\n";
|
|
for (auto Result : Results) {
|
|
if (!IncludeKeywords && Result->getKind() == CodeCompletionResult::Keyword)
|
|
continue;
|
|
Result->print(OS);
|
|
|
|
llvm::SmallString<64> Name;
|
|
llvm::raw_svector_ostream NameOs(Name);
|
|
Result->getCompletionString()->getName(NameOs);
|
|
NameOs.flush();
|
|
OS << "; name=" << Name;
|
|
|
|
OS << "\n";
|
|
}
|
|
OS << "End completions\n";
|
|
}
|
|
|
|
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 unnamed namespace
|
|
|
|
CodeCompletionCallbacksFactory *
|
|
swift::ide::makeCodeCompletionCallbacksFactory(
|
|
CodeCompletionContext &CompletionContext,
|
|
CodeCompletionConsumer &Consumer) {
|
|
return new CodeCompletionCallbacksFactoryImpl(CompletionContext, Consumer);
|
|
}
|
|
|
|
void swift::ide::lookupCodeCompletionResultsFromModule(
|
|
CodeCompletionResultSink &targetSink, const Module *module,
|
|
ArrayRef<std::string> accessPath, bool needLeadingDot,
|
|
const DeclContext *currDeclContext) {
|
|
CompletionLookup Lookup(targetSink, module->getASTContext(), currDeclContext);
|
|
Lookup.getVisibleDeclsOfModule(module, accessPath, needLeadingDot);
|
|
}
|
|
|
|
void swift::ide::copyCodeCompletionResults(CodeCompletionResultSink &targetSink, CodeCompletionResultSink &sourceSink, bool onlyTypes) {
|
|
|
|
// 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(sourceSink.Allocator);
|
|
|
|
if (onlyTypes) {
|
|
std::copy_if(sourceSink.Results.begin(), sourceSink.Results.end(),
|
|
std::back_inserter(targetSink.Results),
|
|
[](CodeCompletionResult *R) -> bool {
|
|
if (R->getKind() != CodeCompletionResult::Declaration)
|
|
return false;
|
|
switch(R->getAssociatedDeclKind()) {
|
|
case CodeCompletionDeclKind::Class:
|
|
case CodeCompletionDeclKind::Struct:
|
|
case CodeCompletionDeclKind::Enum:
|
|
case CodeCompletionDeclKind::Protocol:
|
|
case CodeCompletionDeclKind::TypeAlias:
|
|
case CodeCompletionDeclKind::GenericTypeParam:
|
|
return true;
|
|
case CodeCompletionDeclKind::EnumElement:
|
|
case CodeCompletionDeclKind::Constructor:
|
|
case CodeCompletionDeclKind::Destructor:
|
|
case CodeCompletionDeclKind::Subscript:
|
|
case CodeCompletionDeclKind::StaticMethod:
|
|
case CodeCompletionDeclKind::InstanceMethod:
|
|
case CodeCompletionDeclKind::OperatorFunction:
|
|
case CodeCompletionDeclKind::FreeFunction:
|
|
case CodeCompletionDeclKind::StaticVar:
|
|
case CodeCompletionDeclKind::InstanceVar:
|
|
case CodeCompletionDeclKind::LocalVar:
|
|
case CodeCompletionDeclKind::GlobalVar:
|
|
return false;
|
|
}
|
|
});
|
|
} else {
|
|
targetSink.Results.insert(targetSink.Results.end(),
|
|
sourceSink.Results.begin(),
|
|
sourceSink.Results.end());
|
|
}
|
|
}
|