#include "swift/IDE/CodeCompletion.h" #include "swift/AST/NameLookup.h" #include "swift/Basic/LLVM.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Subsystems.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "clang/Sema/Lookup.h" #include #include using namespace swift; using namespace code_completion; std::string swift::code_completion::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() + 1; continue; } if (C == '#' && Ptr <= End - 2 && Ptr[1] == '^') { do { Ptr++; } while(*Ptr != '#'); continue; } CleanFile += C; } return CleanFile; } 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()) { if (C.getNestingLevel() < PrevNestingLevel) { OS << "#}"; } switch (C.getKind()) { case Chunk::ChunkKind::Text: case Chunk::ChunkKind::LeftParen: case Chunk::ChunkKind::RightParen: case Chunk::ChunkKind::LeftBracket: case Chunk::ChunkKind::RightBracket: case Chunk::ChunkKind::Dot: case Chunk::ChunkKind::Comma: case Chunk::ChunkKind::CallParameterName: case Chunk::ChunkKind::CallParameterColon: case Chunk::ChunkKind::CallParameterType: OS << C.getText(); break; case Chunk::ChunkKind::OptionalBegin: case Chunk::ChunkKind::CallParameterBegin: OS << "{#"; break; case Chunk::ChunkKind::TypeAnnotation: OS << "[#"; OS << C.getText(); OS << "#]"; } PrevNestingLevel = C.getNestingLevel(); } while (PrevNestingLevel > 0) { OS << "#}"; PrevNestingLevel--; } } void CodeCompletionString::dump() const { print(llvm::errs()); } void CodeCompletionResult::print(raw_ostream &OS) const { switch (Kind) { case ResultKind::SwiftDeclaration: OS << "SwiftDecl: "; break; case ResultKind::ClangDeclaration: OS << "ClangDecl: "; break; case ResultKind::Keyword: OS << "Keyword: "; break; case ResultKind::Pattern: OS << "Pattern: "; break; } CompletionString->print(OS); } void CodeCompletionResult::dump() const { print(llvm::errs()); } void CodeCompletionResultBuilder::addChunkWithText( CodeCompletionString::Chunk::ChunkKind Kind, StringRef Text) { Chunks.push_back(CodeCompletionString::Chunk::createWithText( Kind, CurrentNestingLevel, Context.copyString(Text))); } CodeCompletionResult *CodeCompletionResultBuilder::takeResult() { void *Mem = Context.Allocator .Allocate(sizeof(CodeCompletionResult) + Chunks.size() * sizeof(CodeCompletionString::Chunk), llvm::alignOf()); switch (Kind) { case CodeCompletionResult::ResultKind::SwiftDeclaration: return new (Context.Allocator) CodeCompletionResult(new (Mem) CodeCompletionString(Chunks), AssociatedDecl.get()); case CodeCompletionResult::ResultKind::ClangDeclaration: return new (Context.Allocator) CodeCompletionResult(new (Mem) CodeCompletionString(Chunks), AssociatedDecl.get()); case CodeCompletionResult::ResultKind::Keyword: case CodeCompletionResult::ResultKind::Pattern: return new (Context.Allocator) CodeCompletionResult(Kind, new (Mem) CodeCompletionString(Chunks)); } } void CodeCompletionResultBuilder::finishResult() { Context.CurrentCompletionResults.push_back(takeResult()); } StringRef CodeCompletionContext::copyString(StringRef String) { char *Mem = Allocator.Allocate(String.size()); std::copy(String.begin(), String.end(), Mem); return StringRef(Mem, String.size()); } ArrayRef CodeCompletionContext::takeResults() { const size_t Count = CurrentCompletionResults.size(); CodeCompletionResult **Results = Allocator.Allocate(Count); std::copy(CurrentCompletionResults.begin(), CurrentCompletionResults.end(), Results); CurrentCompletionResults.clear(); return llvm::makeArrayRef(Results, Count); } namespace { class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { CodeCompletionContext &CompletionContext; CodeCompletionConsumer &Consumer; TranslationUnit *const TU; void typecheckExpr(Expr *&E) { assert(E && "should have an expression"); DEBUG(llvm::dbgs() << "\nparsed:\n"; E->print(llvm::dbgs()); llvm::dbgs() << "\n"); if (!typeCheckCompletionContextExpr(TU, E)) return; DEBUG(llvm::dbgs() << "\type checked:\n"; E->print(llvm::dbgs()); llvm::dbgs() << "\n"); } public: CodeCompletionCallbacksImpl(Parser &P, CodeCompletionContext &CompletionContext, CodeCompletionConsumer &Consumer) : CodeCompletionCallbacks(P), CompletionContext(CompletionContext), Consumer(Consumer), TU(P.TU) { } void completeExpr() override; void completeDotExpr(Expr *E) override; void completePostfixExpr(Expr *E) override; }; } // end unnamed namespace void CodeCompletionCallbacksImpl::completeExpr() { Parser::ParserPositionRAII RestorePosition(P); P.restoreParserPosition(ExprBeginPosition); // FIXME: implement fallback code completion. Consumer.handleResults(CompletionContext.takeResults()); } namespace { /// Build completions by doing visible decl lookup from a context. class CompletionLookup : swift::VisibleDeclConsumer, clang::VisibleDeclConsumer { CodeCompletionContext &CompletionContext; ASTContext &SwiftContext; enum class LookupKind { ValueExpr, DeclContext }; LookupKind Kind; Type ExprType; bool HaveDot = false; public: CompletionLookup(CodeCompletionContext &CompletionContext, ASTContext &SwiftContext) : CompletionContext(CompletionContext), SwiftContext(SwiftContext) { } void setHaveDot() { HaveDot = true; } void addSwiftVarDeclRef(const VarDecl *VD) { StringRef Name = VD->getName().get(); assert(!Name.empty() && "name should not be empty"); CodeCompletionResultBuilder Builder( CompletionContext, CodeCompletionResult::ResultKind::SwiftDeclaration); Builder.setAssociatedSwiftDecl(VD); if (!HaveDot) Builder.addDot(); Builder.addTextChunk(Name); Builder.addTypeAnnotation(VD->getType().getString()); } void addTuplePatternParameters(CodeCompletionResultBuilder &Builder, const TuplePattern *TP) { bool NeedComma = false; for (auto TupleElt : TP->getFields()) { if (NeedComma) Builder.addComma(", "); Builder.addCallParameter(TupleElt.getPattern()->getBoundName().str(), TupleElt.getPattern()->getType().getString()); NeedComma = true; } } void addSwiftMethodCall(const FuncDecl *FD) { StringRef Name = FD->getName().get(); assert(!Name.empty() && "name should not be empty"); CodeCompletionResultBuilder Builder( CompletionContext, CodeCompletionResult::ResultKind::SwiftDeclaration); Builder.setAssociatedSwiftDecl(FD); if (!HaveDot) Builder.addDot(); Builder.addTextChunk(Name); Builder.addLeftParen(); auto *FE = FD->getBody(); auto Patterns = FE->getArgParamPatterns(); unsigned FirstIndex = 0; if (Patterns[0]->isImplicit()) FirstIndex = 1; addTuplePatternParameters(Builder, cast(Patterns[FirstIndex])); Builder.addRightParen(); // FIXME: Pattern should pretty-print itself. llvm::SmallString<32> Type; for (unsigned i = FirstIndex + 1, e = Patterns.size(); i != e; ++i) { Type += "("; for (auto TupleElt : cast(Patterns[i])->getFields()) { Type += TupleElt.getPattern()->getBoundName().str(); Type += ": "; Type += TupleElt.getPattern()->getType().getString(); } Type += ") -> "; } Type += FE->getResultType(SwiftContext).getString(); Builder.addTypeAnnotation(Type); // TODO: skip arguments with default parameters? } void addSwiftConstructorCall(const ConstructorDecl *CD) { assert(!HaveDot && "can not add a constructor call after a dot"); CodeCompletionResultBuilder Builder( CompletionContext, CodeCompletionResult::ResultKind::SwiftDeclaration); Builder.setAssociatedSwiftDecl(CD); Builder.addLeftParen(); addTuplePatternParameters(Builder, cast(CD->getArguments())); Builder.addRightParen(); Builder.addTypeAnnotation(CD->getResultType().getString()); } void addSwiftSubscriptCall(const SubscriptDecl *SD) { assert(!HaveDot && "can not add a subscript after a dot"); CodeCompletionResultBuilder Builder( CompletionContext, CodeCompletionResult::ResultKind::SwiftDeclaration); Builder.setAssociatedSwiftDecl(SD); Builder.addLeftBracket(); addTuplePatternParameters(Builder, cast(SD->getIndices())); Builder.addRightBracket(); Builder.addTypeAnnotation(SD->getElementType().getString()); } void addClangDecl(const clang::NamedDecl *ND) { // FIXME: Eventually, we'll import the Clang Decl and format it as a Swift // declaration. Right now we don't do this because of performance reasons. StringRef Name = ND->getName(); if (Name.empty()) return; CodeCompletionResultBuilder Builder( CompletionContext, CodeCompletionResult::ResultKind::ClangDeclaration); Builder.setAssociatedClangDecl(ND); Builder.addTextChunk(Name); } void addKeyword(StringRef Name) { CodeCompletionResultBuilder Builder( CompletionContext, CodeCompletionResult::ResultKind::Keyword); if (!HaveDot) Builder.addDot(); Builder.addTextChunk(Name); } // Implement swift::VisibleDeclConsumer void foundDecl(ValueDecl *D) override { if (Kind == LookupKind::ValueExpr) { if (auto *VD = dyn_cast(D)) { // Swift does not have class variables. // FIXME: add code completion results when class variables are added. if (ExprType->is()) return; addSwiftVarDeclRef(VD); return; } if (auto *FD = dyn_cast(D)) { // We can not call getters or setters. We use VarDecls and // SubscriptDecls to produce completions that refer to getters and // setters. if (FD->isGetterOrSetter()) return; // Don't complete class methods of instances; don't complete instance // methods of metatypes. if (FD->isStatic() != ExprType->is()) return; addSwiftMethodCall(FD); return; } if (HaveDot) return; // FIXME: super.constructor if (auto *CD = dyn_cast(D)) { if (!ExprType->is()) return; addSwiftConstructorCall(CD); return; } if (auto *SD = dyn_cast(D)) { if (ExprType->is()) return; addSwiftSubscriptCall(SD); return; } } } // Implement clang::VisibleDeclConsumer void FoundDecl(clang::NamedDecl *ND, clang::NamedDecl *Hiding, clang::DeclContext *Ctx, bool InBaseClass) override { addClangDecl(ND); } void getValueExprCompletions(Type ExprType) { Kind = LookupKind::ValueExpr; this->ExprType = ExprType; if (ExprType->is()) { // FIXME: produce a call. } else { lookupVisibleDecls(*this, ExprType); } // Add the special qualified keyword 'metatype' so that, for example, // 'Int.metatype' can be completed. addKeyword("metatype"); } void getCompletionsInDeclContext(DeclContext *DC, SourceLoc Loc) { Kind = LookupKind::DeclContext; lookupVisibleDecls(*this, DC, Loc); if (auto Importer = SwiftContext.getClangModuleLoader()) static_cast(*Importer).lookupVisibleDecls(*this); } }; } // end unnamed namespace void CodeCompletionCallbacksImpl::completeDotExpr(Expr *E) { typecheckExpr(E); CompletionLookup Lookup(CompletionContext, TU->Ctx); Lookup.setHaveDot(); Lookup.getValueExprCompletions(E->getType()); Consumer.handleResults(CompletionContext.takeResults()); } void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E) { typecheckExpr(E); CompletionLookup Lookup(CompletionContext, TU->Ctx); Lookup.getValueExprCompletions(E->getType()); Consumer.handleResults(CompletionContext.takeResults()); } void PrintingCodeCompletionConsumer::handleResults( ArrayRef Results) { OS << "Begin completions\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::code_completion::makeCodeCompletionCallbacksFactory( CodeCompletionContext &CompletionContext, CodeCompletionConsumer &Consumer) { return new CodeCompletionCallbacksFactoryImpl(CompletionContext, Consumer); }