//===- 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/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/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 #include #include 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 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(AFD)->walk(Finder); return Finder.getFoundStmt(); } CodeCompletionString::CodeCompletionString(ArrayRef Chunks) { Chunk *TailChunks = reinterpret_cast(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::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(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(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(Str.size()); std::copy(Str.begin(), Str.end(), Mem); return StringRef(Mem, Str.size()); } static ArrayRef copyStringArray(llvm::BumpPtrAllocator &Allocator, ArrayRef Arr) { StringRef *Buff = Allocator.Allocate(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(VD) && !isa(VD)) return false; if (isa(VD)) return false; if (VD->hasClangNode() && !VD->getClangDecl()) return false; return true; } template static void walkValueDeclAndOverriddenDecls(const Decl *D, const FnTy &Fn) { if (auto *VD = dyn_cast(D)) { Fn(VD); walkOverriddenDecls(VD, Fn); } } ArrayRef copyAssociatedUSRs(llvm::BumpPtrAllocator &Allocator, const Decl *D) { llvm::SmallVector USRs; walkValueDeclAndOverriddenDecls(D, [&](llvm::PointerUnion OD) { llvm::SmallString<128> SS; bool Ignored = true; if (auto *OVD = OD.dyn_cast()) { if (shouldCopyAssociatedUSRForDecl(OVD)) { llvm::raw_svector_ostream OS(SS); Ignored = printDeclUSR(OVD, OS); } } else if (auto *OND = OD.dyn_cast()) { Ignored = clang::index::generateUSRForDecl(OND, SS); } if (!Ignored) USRs.push_back(copyString(Allocator, SS)); }); if (!USRs.empty()) return copyStringArray(Allocator, USRs); return ArrayRef(); } CodeCompletionResult *CodeCompletionResultBuilder::takeResult() { void *CCSMem = Sink.Allocator ->Allocate(sizeof(CodeCompletionString) + Chunks.size() * sizeof(CodeCompletionString::Chunk), llvm::alignOf()); 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(); } StringRef ModuleName; if (CurrentModule) { if (Sink.LastModule.first == CurrentModule.getOpaqueValue()) { ModuleName = Sink.LastModule.second; } else { if (auto *C = CurrentModule.dyn_cast()) { ModuleName = copyString(*Sink.Allocator, C->getFullModuleName()); } else { ModuleName = copyString( *Sink.Allocator, CurrentModule.get()->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 { /// \brief Cache key. struct Key { std::string ModuleFilename; std::string ModuleName; std::vector AccessPath; bool ResultsHaveLeadingDot; bool ForTestableLookup; 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 && LHS.ForTestableLookup == RHS.ForTestableLookup; } }; struct Value : public ThreadSafeRefCountedBase { llvm::sys::TimeValue ModuleModificationTime; CodeCompletionResultSink Sink; }; using ValueRefCntPtr = llvm::IntrusiveRefCntPtr; sys::Cache TheCache{"swift.libIDE.CodeCompletionCache"}; void getResults( const Key &K, CodeCompletionResultSink &TargetSink, bool OnlyTypes, const Module *TheModule, std::function FillCacheCallback); ValueRefCntPtr getResultSinkFor(const Key &K); void storeResults(const Key &K, ValueRefCntPtr V); }; } // namespace ide } // namespace swift namespace llvm { template<> struct DenseMapInfo { 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()(Val.ModuleFilename); H ^= std::hash()(Val.ModuleName); for (auto Piece : Val.AccessPath) H ^= std::hash()(Piece); H ^= std::hash()(Val.ResultsHaveLeadingDot); H ^= std::hash()(Val.ForTestableLookup); return static_cast(H); } static bool isEqual(const KeyTy &LHS, const KeyTy &RHS) { return LHS == RHS; } }; } // namespace llvm namespace swift { namespace sys { template<> struct CacheValueCostInfo { 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 FillCacheCallback) { // FIXME(thread-safety): lock the whole AST context. We might load a module. llvm::Optional V = TheCache.get(K); if (!V.hasValue()) { // No cached results found. Fill the cache. V = FillCacheCallback(*this, K, TheModule); } 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); V = FillCacheCallback(*this, K, TheModule); } } 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 CodeCompletionContext::takeResults() { // Copy pointers to the results. const size_t Count = CurrentResults.Results.size(); CodeCompletionResult **Results = CurrentResults.Allocator->Allocate(Count); std::copy(CurrentResults.Results.begin(), CurrentResults.Results.end(), Results); CurrentResults.Results.clear(); return MutableArrayRef(Results, Count); } Optional 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 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 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 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 AttTargetDK; SmallVector 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(DCToTypeCheck) && !isa(DCToTypeCheck) && !isa(DCToTypeCheck)) DCToTypeCheck = DCToTypeCheck->getParent(); if (auto *AFD = dyn_cast(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(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(DCToTypeCheck)) { return typeCheckTopLevelCodeDecl(TLCD); } return false; } if (auto *NTD = dyn_cast(DC)) { // First, type check the parent DeclContext. typecheckContextImpl(DC->getParent()); if (NTD->hasType()) return true; return typeCheckCompletionDecl(cast(DC)); } if (auto *TLCD = dyn_cast(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()) 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 &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 ExpressionSpecificDecls; using DeducedAssociatedTypes = llvm::DenseMap; std::map DeducedAssociatedTypeCache; Optional 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(In.getPointer())) { FoundFunctionsWithoutFirstKeyword = true; return; } TupleType *InTuple = In->getAs(); 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 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(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(); if (!BGT) return false; for (Type Arg : BGT->getGenericArgs()) { if (!Arg->is()) 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) -> 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()) { // Values of properties that were found on a AnyObject have // Optional 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(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(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()) { 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(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(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(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(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(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() && !FD->isStatic(); break; case LookupKind::ValueInDeclContext: IsImplicitlyCurriedInstanceMethod = InsideStaticMethod && FD->getDeclContext() == CurrentMethod->getDeclContext() && !FD->isStatic(); if (!IsImplicitlyCurriedInstanceMethod) { if (auto Init = dyn_cast(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()) FunctionType = FunctionType->castTo()->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()) Builder.addOptionalMethodCallTail(); llvm::SmallString<32> TypeStr; if (FunctionType->is()) { llvm::raw_svector_ostream OS(TypeStr); FunctionType.print(OS); Builder.addTypeAnnotation(OS.str()); return; } Type FirstInputType = FunctionType->castTo()->getInput(); if (IsImplicitlyCurriedInstanceMethod) { if (auto PT = dyn_cast(FirstInputType.getPointer())) FirstInputType = PT->getUnderlyingType(); Builder.addLeftParen(); Builder.addCallParameter(Ctx.Id_self, FirstInputType, /*IsVarArg*/ false); Builder.addRightParen(); } else { Builder.addLeftParen(); addParamPatternFromFunction(Builder, FunctionType->castTo(), FD, includeDefaultArgs); Builder.addRightParen(); } Type ResultType = FunctionType->castTo()->getResult(); // Build type annotation. { llvm::raw_svector_ostream OS(TypeStr); for (unsigned i = FirstIndex + 1, e = FD->getBodyParamPatterns().size(); i != e; ++i) { ResultType->castTo()->getInput()->print(OS); ResultType = ResultType->castTo()->getResult(); OS << " -> "; } // What's left is the result type. if (ResultType->isVoid()) OS << "Void"; else ResultType.print(OS); } Builder.addTypeAnnotation(TypeStr); }; if (!FunctionType->is() && hasInterestingDefaultValues(FunctionType->castTo())) { 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()) ConstructorType = MemberType->castTo() ->getResult() ->castTo(); // 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(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()) { 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 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(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 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()); 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()) { 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(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(D)) { if (ExprType->is()) { if (HaveDot) return; addConstructorCall(CD, Reason); } if (IsSuperRefExpr) { if (!isa(CurrDeclContext)) return; addConstructorCall(CD, Reason); } return; } if (HaveLParen) return; if (auto *VD = dyn_cast(D)) { addVarDeclRef(VD, Reason); return; } if (auto *FD = dyn_cast(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(D)) { addNominalTypeRef(NTD, Reason); addConstructorCallsForType(NTD->getType(), NTD->getName(), Reason); return; } if (auto *TAD = dyn_cast(D)) { addTypeAliasRef(TAD, Reason); addConstructorCallsForType(TAD->getUnderlyingType(), TAD->getName(), Reason); return; } if (auto *GP = dyn_cast(D)) { addGenericTypeParamRef(GP, Reason); for (auto *protocol : GP->getProtocols()) addConstructorCallsForType(protocol->getType(), GP->getName(), Reason); return; } if (auto *AT = dyn_cast(D)) { addAssociatedTypeRef(AT, Reason); return; } if (auto *EED = dyn_cast(D)) { addEnumElementRef(EED, Reason, /*HasTypeContext=*/false); } if (HaveDot) return; if (auto *SD = dyn_cast(D)) { if (ExprType->is()) return; addSubscriptCall(SD, Reason); return; } return; case LookupKind::ValueInDeclContext: case LookupKind::ImportFromModule: if (auto *VD = dyn_cast(D)) { addVarDeclRef(VD, Reason); return; } if (auto *FD = dyn_cast(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(D)) { addNominalTypeRef(NTD, Reason); addConstructorCallsForType(NTD->getType(), NTD->getName(), Reason); return; } if (auto *TAD = dyn_cast(D)) { addTypeAliasRef(TAD, Reason); addConstructorCallsForType(TAD->getUnderlyingType(), TAD->getName(), Reason); return; } if (auto *GP = dyn_cast(D)) { addGenericTypeParamRef(GP, Reason); for (auto *protocol : GP->getProtocols()) addConstructorCallsForType(protocol->getType(), GP->getName(), Reason); return; } if (auto *AT = dyn_cast(D)) { addAssociatedTypeRef(AT, Reason); return; } return; case LookupKind::EnumElement: if (auto *EED = dyn_cast(D)) { addEnumElementRef(EED, Reason, /*HasTypeContext=*/true); } return; case LookupKind::Type: case LookupKind::TypeInDeclContext: if (auto *NTD = dyn_cast(D)) { addNominalTypeRef(NTD, Reason); return; } if (auto *TAD = dyn_cast(D)) { addTypeAliasRef(TAD, Reason); return; } if (auto *GP = dyn_cast(D)) { addGenericTypeParamRef(GP, Reason); return; } if (auto *AT = dyn_cast(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()) { if (auto *AFD = dyn_cast_or_null(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 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()) { 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()) { 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(VD)) { auto ContainsErrorProtocol = [&](ArrayRef 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(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 ChangeLookupKind( Kind, LookupKind::EnumElement); NeedLeadingDot = !HaveDot; const DeclContext *FunctionDC = CurrDeclContext; const AbstractFunctionDecl *CurrentFunction = nullptr; while (FunctionDC->isLocalContext()) { if (auto *AFD = dyn_cast(FunctionDC)) { CurrentFunction = AFD; break; } FunctionDC = FunctionDC->getParent(); } if (!CurrentFunction) return; auto *Switch = cast_or_null( findNearestStmt(CurrentFunction, Loc, StmtKind::Switch)); if (!Switch) return; auto Ty = Switch->getSubjectExpr()->getType(); if (!Ty) return; auto *TheEnumDecl = dyn_cast_or_null(Ty->getAnyNominal()); if (!TheEnumDecl) return; for (auto Element : TheEnumDecl->getAllElements()) { foundDecl(Element, DeclVisibilityKind::MemberOfCurrentNominal); } } class FilteredDeclConsumer : public VisibleDeclConsumer { llvm::function_ref Filter; VisibleDeclConsumer &RealConsumer; public: FilteredDeclConsumer(llvm::function_ref 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 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 AccessPath, bool ResultsHaveLeadingDot) { Kind = LookupKind::ImportFromModule; NeedLeadingDot = ResultsHaveLeadingDot; llvm::SmallVector, 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 &ParsedKeywords; public: CompletionOverrideLookup(CodeCompletionResultSink &Sink, ASTContext &Ctx, const DeclContext *CurrDeclContext, SmallVectorImpl &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 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(CurrDeclContext)) AccessibilityOfContext = NTD->getFormalAccess(); else AccessibilityOfContext = cast(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()) return; if (!D->hasType()) TypeResolver->resolveDeclSignature(D); if (auto *FD = dyn_cast(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(D)) { if (!isa(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(NTD); if (!CD) return; if (!CD->getSuperclass()) return; CD = CD->getSuperclass()->getClassOrBoundGenericClass(); for (const auto *Member : CD->getMembers()) { const auto *Constructor = dyn_cast(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 &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()) 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"); 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 Predicate; public: llvm::SmallVector Ancestors; Expr *ParentExpr = nullptr; NearestExprParentFinder(Expr* ChildExpr, llvm::function_ref Predicate) : ChildExpr(ChildExpr), Predicate(Predicate){} std::pair 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(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(ParsedExpr)) { if (auto *DRE = dyn_cast(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](CodeCompletionCacheImpl &Cache, const CodeCompletionCacheImpl::Key &K, const Module *TheModule) { auto V = Cache.getResultSinkFor(K); CompletionLookup Lookup(V->Sink, SwiftContext, &SF); Lookup.getVisibleDeclsOfModule(TheModule, K.AccessPath, K.ResultsHaveLeadingDot); Cache.storeResults(K, V); return V; }; auto &Request = Lookup.RequestedCachedResults.getValue(); llvm::DenseSet 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 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, SF.hasTestableImport(TheModule)}; std::pair 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(Request.TheModule) ->forAllVisibleModules({}, handleImport); } else { // Add results from current module. Lookup.getToplevelCompletions(Request.OnlyTypes); Lookup.discardTypeResolver(); // Add results for all imported modules. SmallVector 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 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); }