1 //===------ CodeCompletion.cpp - Code Completion for ClangRepl -------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements the classes which performs code completion at the REPL.
11 //===----------------------------------------------------------------------===//
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"
29 const std::string CodeCompletionFileName
= "input_line_[Completion]";
31 clang::CodeCompleteOptions
getClangCompleteOpts() {
32 clang::CodeCompleteOptions Opts
;
33 Opts
.IncludeCodePatterns
= true;
34 Opts
.IncludeMacros
= true;
35 Opts
.IncludeGlobals
= true;
36 Opts
.IncludeBriefComments
= true;
40 class ReplCompletionConsumer
: public CodeCompleteConsumer
{
42 ReplCompletionConsumer(std::vector
<std::string
> &Results
)
43 : CodeCompleteConsumer(getClangCompleteOpts()),
44 CCAllocator(std::make_shared
<GlobalCodeCompletionAllocator
>()),
45 CCTUInfo(CCAllocator
), Results(Results
){};
47 void ProcessCodeCompleteResults(class Sema
&S
, CodeCompletionContext Context
,
48 CodeCompletionResult
*InResults
,
49 unsigned NumResults
) final
;
51 CodeCompletionAllocator
&getAllocator() override
{ return *CCAllocator
; }
53 CodeCompletionTUInfo
&getCodeCompletionTUInfo() override
{ return CCTUInfo
; }
56 std::shared_ptr
<GlobalCodeCompletionAllocator
> CCAllocator
;
57 CodeCompletionTUInfo CCTUInfo
;
58 std::vector
<std::string
> &Results
;
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());
72 case CodeCompletionResult::RK_Keyword
:
73 Results
.push_back(Result
.Keyword
);
81 class IncrementalSyntaxOnlyAction
: public SyntaxOnlyAction
{
82 const CompilerInstance
*ParentCI
;
85 IncrementalSyntaxOnlyAction(const CompilerInstance
*ParentCI
)
86 : ParentCI(ParentCI
) {}
89 void ExecuteAction() override
;
92 class ExternalSource
: public clang::ExternalASTSource
{
93 TranslationUnitDecl
*ChildTUDeclCtxt
;
94 ASTContext
&ParentASTCtxt
;
95 TranslationUnitDecl
*ParentTUDeclCtxt
;
97 std::unique_ptr
<ASTImporter
> Importer
;
100 ExternalSource(ASTContext
&ChildASTCtxt
, FileManager
&ChildFM
,
101 ASTContext
&ParentASTCtxt
, FileManager
&ParentFM
);
102 bool FindExternalVisibleDeclsByName(const DeclContext
*DC
,
103 DeclarationName Name
) override
;
105 completeVisibleDeclsMap(const clang::DeclContext
*childDeclContext
) override
;
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(
117 CI
.getASTContext().setExternalSource(astContextExternalSource
);
118 CI
.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(
121 SyntaxOnlyAction::ExecuteAction();
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
);
135 bool ExternalSource::FindExternalVisibleDeclsByName(const DeclContext
*DC
,
136 DeclarationName Name
) {
137 IdentifierTable
&ParentIdTable
= ParentASTCtxt
.Idents
;
139 auto ParentDeclName
=
140 DeclarationName(&(ParentIdTable
.get(Name
.getAsString())));
142 DeclContext::lookup_result lookup_result
=
143 ParentTUDeclCtxt
->lookup(ParentDeclName
);
145 if (!lookup_result
.empty()) {
151 void ExternalSource::completeVisibleDeclsMap(
152 const DeclContext
*ChildDeclContext
) {
153 assert(ChildDeclContext
&& ChildDeclContext
== ChildTUDeclCtxt
&&
154 "No child decl context!");
156 if (!ChildDeclContext
->hasExternalVisibleStorage())
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(),
172 llvm::consumeError(DeclOrErr
.takeError());
176 ChildDeclContext
->setHasExternalLexicalStorage(false);
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
);
186 auto diag
= InterpCI
->getDiagnosticsPtr();
187 std::unique_ptr
<ASTUnit
> AU(ASTUnit::LoadFromCompilerInvocationAction(
188 InterpCI
->getInvocationPtr(), std::make_shared
<PCHContainerOperations
>(),
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
;
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,
206 std::make_shared
<clang::PCHContainerOperations
>(), *diag
,
207 InterpCI
->getLangOpts(), InterpCI
->getSourceManager(),
208 InterpCI
->getFileManager(), sd
, tb
, std::move(Act
));