1 //===------ CodeCompletion.cpp - Code Completion for ClangRepl -------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements the classes which performs code completion at the REPL. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/Interpreter/CodeCompletion.h" 14 #include "clang/AST/ASTImporter.h" 15 #include "clang/AST/DeclarationName.h" 16 #include "clang/AST/ExternalASTSource.h" 17 #include "clang/Basic/IdentifierTable.h" 18 #include "clang/Frontend/ASTUnit.h" 19 #include "clang/Frontend/CompilerInstance.h" 20 #include "clang/Frontend/FrontendActions.h" 21 #include "clang/Interpreter/Interpreter.h" 22 #include "clang/Lex/PreprocessorOptions.h" 23 #include "clang/Sema/CodeCompleteConsumer.h" 24 #include "clang/Sema/CodeCompleteOptions.h" 25 #include "clang/Sema/Sema.h" 26 27 namespace clang { 28 29 const std::string CodeCompletionFileName = "input_line_[Completion]"; 30 31 clang::CodeCompleteOptions getClangCompleteOpts() { 32 clang::CodeCompleteOptions Opts; 33 Opts.IncludeCodePatterns = true; 34 Opts.IncludeMacros = true; 35 Opts.IncludeGlobals = true; 36 Opts.IncludeBriefComments = true; 37 return Opts; 38 } 39 40 class ReplCompletionConsumer : public CodeCompleteConsumer { 41 public: 42 ReplCompletionConsumer(std::vector<std::string> &Results) 43 : CodeCompleteConsumer(getClangCompleteOpts()), 44 CCAllocator(std::make_shared<GlobalCodeCompletionAllocator>()), 45 CCTUInfo(CCAllocator), Results(Results){}; 46 47 void ProcessCodeCompleteResults(class Sema &S, CodeCompletionContext Context, 48 CodeCompletionResult *InResults, 49 unsigned NumResults) final; 50 51 CodeCompletionAllocator &getAllocator() override { return *CCAllocator; } 52 53 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } 54 55 private: 56 std::shared_ptr<GlobalCodeCompletionAllocator> CCAllocator; 57 CodeCompletionTUInfo CCTUInfo; 58 std::vector<std::string> &Results; 59 }; 60 61 void ReplCompletionConsumer::ProcessCodeCompleteResults( 62 class Sema &S, CodeCompletionContext Context, 63 CodeCompletionResult *InResults, unsigned NumResults) { 64 for (unsigned I = 0; I < NumResults; ++I) { 65 auto &Result = InResults[I]; 66 switch (Result.Kind) { 67 case CodeCompletionResult::RK_Declaration: 68 if (auto *ID = Result.Declaration->getIdentifier()) { 69 Results.push_back(ID->getName().str()); 70 } 71 break; 72 case CodeCompletionResult::RK_Keyword: 73 Results.push_back(Result.Keyword); 74 break; 75 default: 76 break; 77 } 78 } 79 } 80 81 class IncrementalSyntaxOnlyAction : public SyntaxOnlyAction { 82 const CompilerInstance *ParentCI; 83 84 public: 85 IncrementalSyntaxOnlyAction(const CompilerInstance *ParentCI) 86 : ParentCI(ParentCI) {} 87 88 protected: 89 void ExecuteAction() override; 90 }; 91 92 class ExternalSource : public clang::ExternalASTSource { 93 TranslationUnitDecl *ChildTUDeclCtxt; 94 ASTContext &ParentASTCtxt; 95 TranslationUnitDecl *ParentTUDeclCtxt; 96 97 std::unique_ptr<ASTImporter> Importer; 98 99 public: 100 ExternalSource(ASTContext &ChildASTCtxt, FileManager &ChildFM, 101 ASTContext &ParentASTCtxt, FileManager &ParentFM); 102 bool FindExternalVisibleDeclsByName(const DeclContext *DC, 103 DeclarationName Name) override; 104 void 105 completeVisibleDeclsMap(const clang::DeclContext *childDeclContext) override; 106 }; 107 108 // This method is intended to set up `ExternalASTSource` to the running 109 // compiler instance before the super `ExecuteAction` triggers parsing 110 void IncrementalSyntaxOnlyAction::ExecuteAction() { 111 CompilerInstance &CI = getCompilerInstance(); 112 ExternalSource *myExternalSource = 113 new ExternalSource(CI.getASTContext(), CI.getFileManager(), 114 ParentCI->getASTContext(), ParentCI->getFileManager()); 115 llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> astContextExternalSource( 116 myExternalSource); 117 CI.getASTContext().setExternalSource(astContextExternalSource); 118 CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage( 119 true); 120 121 SyntaxOnlyAction::ExecuteAction(); 122 } 123 124 ExternalSource::ExternalSource(ASTContext &ChildASTCtxt, FileManager &ChildFM, 125 ASTContext &ParentASTCtxt, FileManager &ParentFM) 126 : ChildTUDeclCtxt(ChildASTCtxt.getTranslationUnitDecl()), 127 ParentASTCtxt(ParentASTCtxt), 128 ParentTUDeclCtxt(ParentASTCtxt.getTranslationUnitDecl()) { 129 ASTImporter *importer = 130 new ASTImporter(ChildASTCtxt, ChildFM, ParentASTCtxt, ParentFM, 131 /*MinimalImport : ON*/ true); 132 Importer.reset(importer); 133 } 134 135 bool ExternalSource::FindExternalVisibleDeclsByName(const DeclContext *DC, 136 DeclarationName Name) { 137 IdentifierTable &ParentIdTable = ParentASTCtxt.Idents; 138 139 auto ParentDeclName = 140 DeclarationName(&(ParentIdTable.get(Name.getAsString()))); 141 142 DeclContext::lookup_result lookup_result = 143 ParentTUDeclCtxt->lookup(ParentDeclName); 144 145 if (!lookup_result.empty()) { 146 return true; 147 } 148 return false; 149 } 150 151 void ExternalSource::completeVisibleDeclsMap( 152 const DeclContext *ChildDeclContext) { 153 assert(ChildDeclContext && ChildDeclContext == ChildTUDeclCtxt && 154 "No child decl context!"); 155 156 if (!ChildDeclContext->hasExternalVisibleStorage()) 157 return; 158 159 for (auto *DeclCtxt = ParentTUDeclCtxt; DeclCtxt != nullptr; 160 DeclCtxt = DeclCtxt->getPreviousDecl()) { 161 for (auto &IDeclContext : DeclCtxt->decls()) { 162 if (NamedDecl *Decl = llvm::dyn_cast<NamedDecl>(IDeclContext)) { 163 if (auto DeclOrErr = Importer->Import(Decl)) { 164 if (NamedDecl *importedNamedDecl = 165 llvm::dyn_cast<NamedDecl>(*DeclOrErr)) { 166 SetExternalVisibleDeclsForName(ChildDeclContext, 167 importedNamedDecl->getDeclName(), 168 importedNamedDecl); 169 } 170 171 } else { 172 llvm::consumeError(DeclOrErr.takeError()); 173 } 174 } 175 } 176 ChildDeclContext->setHasExternalLexicalStorage(false); 177 } 178 } 179 180 void codeComplete(CompilerInstance *InterpCI, llvm::StringRef Content, 181 unsigned Line, unsigned Col, const CompilerInstance *ParentCI, 182 std::vector<std::string> &CCResults) { 183 auto DiagOpts = DiagnosticOptions(); 184 auto consumer = ReplCompletionConsumer(CCResults); 185 186 auto diag = InterpCI->getDiagnosticsPtr(); 187 std::unique_ptr<ASTUnit> AU(ASTUnit::LoadFromCompilerInvocationAction( 188 InterpCI->getInvocationPtr(), std::make_shared<PCHContainerOperations>(), 189 diag)); 190 llvm::SmallVector<clang::StoredDiagnostic, 8> sd = {}; 191 llvm::SmallVector<const llvm::MemoryBuffer *, 1> tb = {}; 192 InterpCI->getFrontendOpts().Inputs[0] = FrontendInputFile( 193 CodeCompletionFileName, Language::CXX, InputKind::Source); 194 auto Act = std::unique_ptr<IncrementalSyntaxOnlyAction>( 195 new IncrementalSyntaxOnlyAction(ParentCI)); 196 std::unique_ptr<llvm::MemoryBuffer> MB = 197 llvm::MemoryBuffer::getMemBufferCopy(Content, CodeCompletionFileName); 198 llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles; 199 200 RemappedFiles.push_back(std::make_pair(CodeCompletionFileName, MB.get())); 201 // we don't want the AU destructor to release the memory buffer that MB 202 // owns twice, because MB handles its resource on its own. 203 AU->setOwnsRemappedFileBuffers(false); 204 AU->CodeComplete(CodeCompletionFileName, 1, Col, RemappedFiles, false, false, 205 false, consumer, 206 std::make_shared<clang::PCHContainerOperations>(), *diag, 207 InterpCI->getLangOpts(), InterpCI->getSourceManager(), 208 InterpCI->getFileManager(), sd, tb, std::move(Act)); 209 } 210 211 } // namespace clang 212