mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Introduce the new BooleanLiteralConvertible protocol for Boolean literals. Take "true" and "false" as real keywords (which is most of the reason for the testsuite churn). Make Bool BooleanLiteralConvertible and the default Boolean literal type, and ObjCBool BooleanLiteralConvertible. Fixes <rdar://problem/17405310> and the recent regression that made ObjCBool not work with true/false. Swift SVN r19728
2513 lines
78 KiB
C++
2513 lines
78 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/Basic/Cache.h"
|
|
#include "swift/Basic/Fallthrough.h"
|
|
#include "swift/Basic/ThreadSafeRefCounted.h"
|
|
#include "swift/AST/ASTPrinter.h"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/LazyResolver.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/Optional.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 <algorithm>
|
|
#include <functional>
|
|
#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();
|
|
}
|
|
|
|
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::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::CallParameterType:
|
|
case CodeCompletionString::Chunk::ChunkKind::GenericParameterName:
|
|
if (AnnotatedTextChunk)
|
|
OS << "['";
|
|
else if (C.getKind() == Chunk::ChunkKind::CallParameterInternalName)
|
|
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");
|
|
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());
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind Kind, StringRef Text) {
|
|
addChunkWithTextNoCopy(Kind, copyString(*Sink.Allocator, Text));
|
|
}
|
|
|
|
StringRef CodeCompletionContext::copyString(StringRef Str) {
|
|
return ::copyString(*CurrentResults.Allocator, Str);
|
|
}
|
|
|
|
static bool isDeclUnavailable(const Decl *D) {
|
|
auto Attr = D->getAttrs().getUnavailable();
|
|
if (!Attr)
|
|
return false;
|
|
|
|
// FIXME: match platform.
|
|
if (Attr->hasPlatform())
|
|
return false;
|
|
|
|
return Attr->IsUnvailable;
|
|
}
|
|
|
|
CodeCompletionResult *CodeCompletionResultBuilder::takeResult() {
|
|
void *CCSMem = Sink.Allocator
|
|
->Allocate(sizeof(CodeCompletionString) +
|
|
Chunks.size() * sizeof(CodeCompletionString::Chunk),
|
|
llvm::alignOf<CodeCompletionString>());
|
|
auto *CCS = new (CCSMem) CodeCompletionString(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();
|
|
}
|
|
bool NotRecommended = isDeclUnavailable(AssociatedDecl);
|
|
return new (*Sink.Allocator) CodeCompletionResult(
|
|
SemanticContext, NumBytesToErase, CCS, AssociatedDecl, NotRecommended,
|
|
copyString(*Sink.Allocator, BriefComment));
|
|
}
|
|
|
|
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 {
|
|
/// \brief Cache key.
|
|
struct Key {
|
|
std::string ModuleFilename;
|
|
std::string ModuleName;
|
|
std::vector<std::string> AccessPath;
|
|
bool ResultsHaveLeadingDot;
|
|
|
|
friend bool operator==(const Key &LHS, const Key &RHS) {
|
|
return LHS.ModuleFilename == RHS.ModuleFilename &&
|
|
LHS.ModuleName == RHS.ModuleName &&
|
|
LHS.AccessPath == RHS.AccessPath &&
|
|
LHS.ResultsHaveLeadingDot == RHS.ResultsHaveLeadingDot;
|
|
}
|
|
};
|
|
|
|
struct Value : public ThreadSafeRefCountedBase<Value> {
|
|
llvm::sys::TimeValue ModuleModificationTime;
|
|
CodeCompletionResultSink Sink;
|
|
};
|
|
using ValueRefCntPtr = llvm::IntrusiveRefCntPtr<Value>;
|
|
|
|
sys::Cache<Key, ValueRefCntPtr> TheCache{"swift.libIDE.CodeCompletionCache"};
|
|
|
|
void getResults(
|
|
const Key &K, CodeCompletionResultSink &TargetSink, bool OnlyTypes,
|
|
const Module *TheModule,
|
|
std::function<void(CodeCompletionCacheImpl &, Key,
|
|
const Module *)> FillCacheCallback);
|
|
|
|
ValueRefCntPtr getResultSinkFor(const Key &K);
|
|
|
|
void storeResults(const Key &K, ValueRefCntPtr V);
|
|
};
|
|
|
|
} // namespace ide
|
|
} // namespace swift
|
|
|
|
namespace llvm {
|
|
template<>
|
|
struct DenseMapInfo<swift::ide::CodeCompletionCacheImpl::Key> {
|
|
using KeyTy = swift::ide::CodeCompletionCacheImpl::Key;
|
|
static inline KeyTy getEmptyKey() {
|
|
return KeyTy{"", "", {}, false};
|
|
}
|
|
static inline KeyTy getTombstoneKey() {
|
|
return KeyTy{"x", "", {}, 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);
|
|
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 CodeCompletionCacheImpl::getResults(
|
|
const Key &K, CodeCompletionResultSink &TargetSink, bool OnlyTypes,
|
|
const Module *TheModule,
|
|
std::function<void(CodeCompletionCacheImpl &, Key, const Module *)>
|
|
FillCacheCallback) {
|
|
// FIXME(thread-safety): lock the whole AST context. We might load a module.
|
|
llvm::Optional<ValueRefCntPtr> V = TheCache.get(K);
|
|
if (!V.hasValue()) {
|
|
// No cached results found. Fill the cache.
|
|
FillCacheCallback(*this, K, TheModule);
|
|
V = TheCache.get(K);
|
|
} else {
|
|
llvm::sys::fs::file_status ModuleStatus;
|
|
if (llvm::sys::fs::status(K.ModuleFilename, ModuleStatus) ||
|
|
V.getValue()->ModuleModificationTime !=
|
|
ModuleStatus.getLastModificationTime()) {
|
|
// Cache is stale. Update the cache.
|
|
TheCache.remove(K);
|
|
FillCacheCallback(*this, K, TheModule);
|
|
V = TheCache.get(K);
|
|
}
|
|
}
|
|
assert(V.hasValue());
|
|
auto &SourceSink = V.getValue()->Sink;
|
|
|
|
// 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());
|
|
}
|
|
}
|
|
|
|
CodeCompletionCacheImpl::ValueRefCntPtr
|
|
CodeCompletionCacheImpl::getResultSinkFor(const Key &K) {
|
|
TheCache.remove(K);
|
|
auto V = ValueRefCntPtr(new Value);
|
|
TheCache.set(K, V);
|
|
return V;
|
|
}
|
|
|
|
void CodeCompletionCacheImpl::storeResults(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();
|
|
}
|
|
}
|
|
|
|
// Remove the cache entry and add it back to refresh the cost value.
|
|
TheCache.remove(K);
|
|
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:
|
|
return i;
|
|
case CodeCompletionString::Chunk::ChunkKind::LeftParen:
|
|
case CodeCompletionString::Chunk::ChunkKind::RightParen:
|
|
case CodeCompletionString::Chunk::ChunkKind::LeftBracket:
|
|
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::OverrideKeyword:
|
|
case CodeCompletionString::Chunk::ChunkKind::DeclIntroducer:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterName:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterInternalName:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterColon:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterType:
|
|
case CodeCompletionString::Chunk::ChunkKind::OptionalBegin:
|
|
case CodeCompletionString::Chunk::ChunkKind::CallParameterBegin:
|
|
case CodeCompletionString::Chunk::ChunkKind::GenericParameterBegin:
|
|
case CodeCompletionString::Chunk::ChunkKind::GenericParameterName:
|
|
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 {
|
|
for (auto C : getChunks().slice(*getFirstTextChunkIndex())) {
|
|
if (C.getKind() == CodeCompletionString::Chunk::ChunkKind::BraceStmtWithCursor)
|
|
break;
|
|
if (C.getKind() == CodeCompletionString::Chunk::ChunkKind::TypeAnnotation)
|
|
continue;
|
|
if (C.hasText() && !C.isAnnotation()) {
|
|
OS << C.getText();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CodeCompletionContext::sortCompletionResults(
|
|
MutableArrayRef<CodeCompletionResult *> Results) {
|
|
std::sort(Results.begin(), Results.end(),
|
|
[](CodeCompletionResult *LHS, CodeCompletionResult *RHS) {
|
|
llvm::SmallString<64> LSS;
|
|
llvm::SmallString<64> RSS;
|
|
{
|
|
llvm::raw_svector_ostream LOS(LSS);
|
|
LHS->getCompletionString()->getName(LOS);
|
|
llvm::raw_svector_ostream ROS(RSS);
|
|
RHS->getCompletionString()->getName(ROS);
|
|
}
|
|
int Result = LSS.compare_lower(RSS);
|
|
// If the case insensitive comparison is equal, then secondary sort order
|
|
// should be case sensitive.
|
|
if (Result == 0)
|
|
Result = LSS.compare(RSS);
|
|
return Result < 0;
|
|
});
|
|
}
|
|
|
|
namespace {
|
|
class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
|
|
CodeCompletionContext &CompletionContext;
|
|
CodeCompletionConsumer &Consumer;
|
|
|
|
enum class CompletionKind {
|
|
None,
|
|
DotExpr,
|
|
PostfixExprBeginning,
|
|
PostfixExpr,
|
|
PostfixExprParen,
|
|
SuperExpr,
|
|
SuperExprDot,
|
|
TypeSimpleBeginning,
|
|
TypeIdentifierWithDot,
|
|
TypeIdentifierWithoutDot,
|
|
CaseStmtBeginning,
|
|
CaseStmtDotPrefix,
|
|
NominalMemberBeginning,
|
|
};
|
|
|
|
CompletionKind Kind = CompletionKind::None;
|
|
Expr *ParsedExpr = nullptr;
|
|
SourceLoc DotLoc;
|
|
TypeLoc ParsedTypeLoc;
|
|
DeclContext *CurDeclContext = nullptr;
|
|
Decl *CStyleForLoopIterationVariable = nullptr;
|
|
|
|
/// \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 outer function.
|
|
DeclContext *DCToTypeCheck = DC;
|
|
while (!DCToTypeCheck->isModuleContext() &&
|
|
!isa<AbstractFunctionDecl>(DCToTypeCheck))
|
|
DCToTypeCheck = DCToTypeCheck->getParent();
|
|
// First, type check the nominal decl that contains the function.
|
|
// Then type check the function itself.
|
|
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DCToTypeCheck)) {
|
|
typecheckContextImpl(DCToTypeCheck->getParent());
|
|
return typeCheckAbstractFunctionBodyUntil(AFD, EndTypeCheckLoc);
|
|
}
|
|
return false;
|
|
}
|
|
if (DC->getContextKind() == DeclContextKind::NominalTypeDecl) {
|
|
auto *NTD = cast<NominalTypeDecl>(DC);
|
|
// First, type check the parent DeclContext.
|
|
typecheckContextImpl(DC->getParent());
|
|
if (NTD->hasType())
|
|
return true;
|
|
return typeCheckCompletionDecl(cast<NominalTypeDecl>(DC));
|
|
}
|
|
if (DC->getContextKind() == DeclContextKind::TopLevelCodeDecl) {
|
|
return typeCheckTopLevelCodeDecl(cast<TopLevelCodeDecl>(DC));
|
|
}
|
|
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 completeNominalMemberBeginning() override;
|
|
|
|
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 = {};
|
|
|
|
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 Fields = InTuple->getFields();
|
|
if (Fields.empty())
|
|
return;
|
|
if (!Fields[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;
|
|
SmallVector<ProtocolConformance *, 8> Worklist;
|
|
|
|
// Collect conformances from the nominal and its superclasses.
|
|
const auto *CurrNominal = NTD;
|
|
while (true) {
|
|
auto Conformances = CurrNominal->getConformances();
|
|
Worklist.append(Conformances.begin(), Conformances.end());
|
|
if (const auto *CD = dyn_cast<ClassDecl>(CurrNominal)) {
|
|
if (CD->hasSuperclass()) {
|
|
CurrNominal = CD->getSuperclass()->getAnyNominal();
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
while (!Worklist.empty()) {
|
|
auto Conformance = Worklist.pop_back_val();
|
|
if (!Conformance->isComplete())
|
|
continue;
|
|
Conformance->forEachTypeWitness(TypeResolver.get(),
|
|
[&](const AssociatedTypeDecl *ATD,
|
|
const Substitution &Subst) -> bool {
|
|
Types[ATD] = Subst.Replacement;
|
|
return false;
|
|
});
|
|
for (auto It : Conformance->getInheritedConformances())
|
|
Worklist.push_back(It.second);
|
|
}
|
|
|
|
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;
|
|
|
|
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().isOptional()) {
|
|
// 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->getNumFields(); i < end; ++i) {
|
|
TuplePatternElt TupleElt = TP->getFields()[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;
|
|
}
|
|
|
|
Builder.addCallParameter(P->getBoundName(), P->getType(),/*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->getFields()) {
|
|
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);
|
|
}
|
|
|
|
void addParamPatternFromFunction(CodeCompletionResultBuilder &Builder,
|
|
const AnyFunctionType *AFT,
|
|
const AbstractFunctionDecl *AFD) {
|
|
|
|
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->getFields().size(); i != e; ++i) {
|
|
const auto &TupleElt = TT->getFields()[i];
|
|
switch (TupleElt.getDefaultArgKind()) {
|
|
case DefaultArgumentKind::None:
|
|
case DefaultArgumentKind::Normal:
|
|
case DefaultArgumentKind::Inherited:
|
|
break;
|
|
|
|
case DefaultArgumentKind::File:
|
|
case DefaultArgumentKind::Line:
|
|
case DefaultArgumentKind::Column:
|
|
case DefaultArgumentKind::Function:
|
|
// Skip parameters that are defaulted to source location. 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->getFields()[i].getPattern();
|
|
Builder.addCallParameter(Name, ParamPat->getBoundName(), 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->getFields().front().getPattern();
|
|
Builder.addCallParameter(Identifier(), ParamPat->getBoundName(), T,
|
|
/*IsVarArg*/false);
|
|
} else
|
|
Builder.addCallParameter(Identifier(), T, /*IsVarArg*/false);
|
|
}
|
|
}
|
|
|
|
void addFunctionCallPattern(const AnyFunctionType *AFT,
|
|
const AbstractFunctionDecl *AFD = nullptr) {
|
|
foundFunction(AFT);
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Pattern,
|
|
SemanticContextKind::ExpressionSpecific);
|
|
if (!HaveLParen)
|
|
Builder.addLeftParen();
|
|
else
|
|
Builder.addAnnotatedLeftParen();
|
|
|
|
addParamPatternFromFunction(Builder, AFT, AFD);
|
|
|
|
Builder.addRightParen();
|
|
addTypeAnnotation(Builder, AFT->getResult());
|
|
}
|
|
|
|
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");
|
|
|
|
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().isOptional())
|
|
Builder.addOptionalMethodCallTail();
|
|
|
|
llvm::SmallString<32> TypeStr;
|
|
|
|
unsigned FirstIndex = 0;
|
|
if (!IsImplicitlyCurriedInstanceMethod && FD->getImplicitSelfDecl())
|
|
FirstIndex = 1;
|
|
Type FunctionType = getTypeOfMember(FD);
|
|
|
|
if (FunctionType->is<ErrorType>()) {
|
|
llvm::raw_svector_ostream OS(TypeStr);
|
|
FunctionType.print(OS);
|
|
Builder.addTypeAnnotation(OS.str());
|
|
return;
|
|
}
|
|
|
|
if (FirstIndex != 0)
|
|
FunctionType = FunctionType->castTo<AnyFunctionType>()->getResult();
|
|
|
|
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);
|
|
Builder.addRightParen();
|
|
}
|
|
|
|
FunctionType = 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) {
|
|
FunctionType->castTo<AnyFunctionType>()->getInput()->print(OS);
|
|
FunctionType = FunctionType->castTo<AnyFunctionType>()->getResult();
|
|
OS << " -> ";
|
|
}
|
|
// What's left is the result type.
|
|
Type ResultType = FunctionType;
|
|
if (ResultType->isVoid())
|
|
OS << "Void";
|
|
else
|
|
ResultType.print(OS);
|
|
}
|
|
Builder.addTypeAnnotation(TypeStr);
|
|
|
|
// TODO: skip arguments with default parameters?
|
|
}
|
|
|
|
void addConstructorCall(const ConstructorDecl *CD,
|
|
DeclVisibilityKind Reason) {
|
|
foundFunction(CD);
|
|
CodeCompletionResultBuilder Builder(
|
|
Sink,
|
|
CodeCompletionResult::ResultKind::Declaration,
|
|
getSemanticContext(CD, Reason));
|
|
Builder.setAssociatedDecl(CD);
|
|
if (IsSuperRefExpr) {
|
|
assert(isa<ConstructorDecl>(
|
|
dyn_cast<AbstractFunctionDecl>(CurrDeclContext)) &&
|
|
"can call super.init only inside a constructor");
|
|
addLeadingDot(Builder);
|
|
Builder.addTextChunk("init");
|
|
}
|
|
|
|
Type MemberType = getTypeOfMember(CD);
|
|
if (MemberType->is<ErrorType>()) {
|
|
addTypeAnnotation(Builder, MemberType);
|
|
return;
|
|
}
|
|
|
|
if (!HaveLParen)
|
|
Builder.addLeftParen();
|
|
else
|
|
Builder.addAnnotatedLeftParen();
|
|
|
|
Type ConstructorType = MemberType->castTo<AnyFunctionType>()->getResult();
|
|
addParamPatternFromFunction(
|
|
Builder, ConstructorType->castTo<AnyFunctionType>(), CD);
|
|
|
|
Builder.addRightParen();
|
|
addTypeAnnotation(
|
|
Builder, ConstructorType->castTo<AnyFunctionType>()->getResult());
|
|
}
|
|
|
|
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())
|
|
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());
|
|
addTypeAnnotation(Builder, EED->getType());
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// Implement swift::VisibleDeclConsumer.
|
|
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason) override {
|
|
// Hide private stdlib declarations.
|
|
if (D->isPrivateStdlibDecl())
|
|
return;
|
|
|
|
if (!D->hasType())
|
|
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 (auto *AFD = dyn_cast<AbstractFunctionDecl>(CurrDeclContext))
|
|
if (!isa<ConstructorDecl>(AFD))
|
|
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);
|
|
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;
|
|
}
|
|
|
|
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);
|
|
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;
|
|
|
|
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->getFields()) {
|
|
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;
|
|
}
|
|
if (tryStdlibOptionalCompletions(ExprType))
|
|
Done = true;
|
|
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);
|
|
|
|
// FIXME: The pedantically correct way to find the type is to resolve the
|
|
// Swift.StringLiteralType type.
|
|
addKeyword("__FILE__", "String");
|
|
// Same: Swift.IntegerLiteralType.
|
|
addKeyword("__LINE__", "Int");
|
|
addKeyword("__COLUMN__", "Int");
|
|
// Same: Swift.BooleanLiteralType.
|
|
addKeyword("false", "Bool");
|
|
addKeyword("true", "Bool");
|
|
|
|
addKeyword("nil", StringRef());
|
|
RequestedCachedResults = RequestedResultsTy::toplevelResults();
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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 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();
|
|
M->lookupVisibleDecls({}, *this, NLKind::QualifiedLookup);
|
|
}
|
|
|
|
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()));
|
|
}
|
|
TheModule->lookupVisibleDecls(LookupAccessPath, *this,
|
|
NLKind::QualifiedLookup);
|
|
}
|
|
|
|
void getModuleImportCompletions(StringRef ModuleName,
|
|
const std::vector<std::string> &AccessPath,
|
|
bool ResultsHaveLeadingDot) {
|
|
Kind = LookupKind::ImportFromModule;
|
|
NeedLeadingDot = ResultsHaveLeadingDot;
|
|
|
|
auto ModulePath =
|
|
std::make_pair(Ctx.getIdentifier(ModuleName), SourceLoc());
|
|
Module *M = Ctx.getModule(llvm::makeArrayRef(ModulePath));
|
|
if (!M)
|
|
return;
|
|
|
|
llvm::SmallVector<std::pair<Identifier, SourceLoc>, 1> LookupAccessPath;
|
|
for (auto Piece : AccessPath) {
|
|
LookupAccessPath.push_back(
|
|
std::make_pair(Ctx.getIdentifier(Piece), SourceLoc()));
|
|
}
|
|
|
|
using namespace swift::namelookup;
|
|
SmallVector<ValueDecl *, 1> Decls;
|
|
lookupVisibleDeclsInModule(M, LookupAccessPath, Decls,
|
|
NLKind::QualifiedLookup,
|
|
ResolutionKind::Exact,
|
|
TypeResolver.get());
|
|
|
|
for (auto *VD : Decls) {
|
|
foundDecl(VD, DeclVisibilityKind::VisibleAtTopLevel);
|
|
}
|
|
}
|
|
};
|
|
|
|
class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
|
|
CodeCompletionResultSink &Sink;
|
|
OwnedResolver TypeResolver;
|
|
const DeclContext *CurrDeclContext;
|
|
|
|
public:
|
|
CompletionOverrideLookup(CodeCompletionResultSink &Sink,
|
|
ASTContext &Ctx,
|
|
const DeclContext *CurrDeclContext)
|
|
: Sink(Sink),
|
|
TypeResolver(createLazyResolver(Ctx)),
|
|
CurrDeclContext(CurrDeclContext) {}
|
|
|
|
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);
|
|
if (Reason == DeclVisibilityKind::MemberOfSuper)
|
|
Builder.addOverrideKeyword();
|
|
PrintOptions Options;
|
|
Options.FunctionDefinitions = false;
|
|
Options.PrintDefaultParameterPlaceholder = false;
|
|
Options.PrintImplicitAttrs = false;
|
|
Options.ExclusiveAttrList.push_back(DAK_NoReturn);
|
|
Options.PrintAttrTransparent = false;
|
|
Options.PrintOverrideKeyword = false;
|
|
FD->print(Printer, Options);
|
|
NameOffset = Printer.NameOffset.getValue();
|
|
}
|
|
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.PrintAttrTransparent = false;
|
|
Options.ExclusiveAttrList.push_back(DAK_NoReturn);
|
|
Options.FunctionDefinitions = false;
|
|
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::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::completeNominalMemberBeginning() {
|
|
assert(!InEnumElementRawValue);
|
|
|
|
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) {
|
|
auto Files = TheModule->getFiles();
|
|
assert(!Files.empty());
|
|
if (auto CMU = dyn_cast<ClangModuleUnit>(Files.front())) {
|
|
if (auto ClangMod = CMU->getClangModule())
|
|
return ClangMod->isSubModule();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CodeCompletionCallbacksImpl::doneParsing() {
|
|
if (Kind == CompletionKind::None) {
|
|
return;
|
|
}
|
|
|
|
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 ExprType = ParsedExpr->getType();
|
|
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::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);
|
|
OverrideLookup.getOverrideCompletions(SourceLoc());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Lookup.RequestedCachedResults) {
|
|
// Create helpers for result caching.
|
|
auto &SwiftContext = P.Context;
|
|
auto FillCacheCallback =
|
|
[&SwiftContext](CodeCompletionCacheImpl &Cache,
|
|
const CodeCompletionCacheImpl::Key &K,
|
|
const Module *TheModule) {
|
|
auto V = Cache.getResultSinkFor(K);
|
|
CompletionLookup Lookup(V->Sink, SwiftContext, nullptr);
|
|
Lookup.getVisibleDeclsOfModule(TheModule, K.AccessPath,
|
|
K.ResultsHaveLeadingDot);
|
|
Cache.storeResults(K, V);
|
|
};
|
|
|
|
auto &Request = Lookup.RequestedCachedResults.getValue();
|
|
|
|
llvm::DenseSet<CodeCompletionCacheImpl::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()) {
|
|
CodeCompletionCacheImpl::Key K{ModuleFilename,
|
|
TheModule->Name.str(),
|
|
AccessPath, Request.NeedLeadingDot};
|
|
std::pair<decltype(ImportsSeen)::iterator, bool>
|
|
Result = ImportsSeen.insert(K);
|
|
if (!Result.second)
|
|
return; // already handled.
|
|
|
|
CompletionContext.Cache.Impl->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.
|
|
auto *SF = CurDeclContext->getParentSourceFile();
|
|
for (std::pair<Module::ImportedModule, bool> Imported :
|
|
SF->getImports()) {
|
|
Module *TheModule = Imported.first.second;
|
|
Module::AccessPathTy AccessPath = Imported.first.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) {
|
|
OS << "Begin completions, " << Results.size() << " items\n";
|
|
for (auto Result : Results) {
|
|
Result->print(OS);
|
|
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);
|
|
}
|
|
|