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/DeclLookups.h"
16 #include "clang/AST/DeclarationName.h"
17 #include "clang/AST/ExternalASTSource.h"
18 #include "clang/Basic/IdentifierTable.h"
19 #include "clang/Frontend/ASTUnit.h"
20 #include "clang/Frontend/CompilerInstance.h"
21 #include "clang/Frontend/FrontendActions.h"
22 #include "clang/Interpreter/Interpreter.h"
23 #include "clang/Lex/PreprocessorOptions.h"
24 #include "clang/Sema/CodeCompleteConsumer.h"
25 #include "clang/Sema/CodeCompleteOptions.h"
26 #include "clang/Sema/Sema.h"
27 #include "llvm/Support/Debug.h"
28 #define DEBUG_TYPE "REPLCC"
32 const std::string CodeCompletionFileName
= "input_line_[Completion]";
34 clang::CodeCompleteOptions
getClangCompleteOpts() {
35 clang::CodeCompleteOptions Opts
;
36 Opts
.IncludeCodePatterns
= true;
37 Opts
.IncludeMacros
= true;
38 Opts
.IncludeGlobals
= true;
39 Opts
.IncludeBriefComments
= true;
43 class ReplCompletionConsumer
: public CodeCompleteConsumer
{
45 ReplCompletionConsumer(std::vector
<std::string
> &Results
,
46 ReplCodeCompleter
&CC
)
47 : CodeCompleteConsumer(getClangCompleteOpts()),
48 CCAllocator(std::make_shared
<GlobalCodeCompletionAllocator
>()),
49 CCTUInfo(CCAllocator
), Results(Results
), CC(CC
) {}
51 // The entry of handling code completion. When the function is called, we
52 // create a `Context`-based handler (see classes defined below) to handle each
54 void ProcessCodeCompleteResults(class Sema
&S
, CodeCompletionContext Context
,
55 CodeCompletionResult
*InResults
,
56 unsigned NumResults
) final
;
58 CodeCompletionAllocator
&getAllocator() override
{ return *CCAllocator
; }
60 CodeCompletionTUInfo
&getCodeCompletionTUInfo() override
{ return CCTUInfo
; }
63 std::shared_ptr
<GlobalCodeCompletionAllocator
> CCAllocator
;
64 CodeCompletionTUInfo CCTUInfo
;
65 std::vector
<std::string
> &Results
;
66 ReplCodeCompleter
&CC
;
69 /// The class CompletionContextHandler contains four interfaces, each of
70 /// which handles one type of completion result.
71 /// Its derived classes are used to create concrete handlers based on
72 /// \c CodeCompletionContext.
73 class CompletionContextHandler
{
75 CodeCompletionContext CCC
;
76 std::vector
<std::string
> &Results
;
82 CompletionContextHandler(Sema
&S
, CodeCompletionContext CCC
,
83 std::vector
<std::string
> &Results
)
84 : CCC(CCC
), Results(Results
), S(S
) {}
86 virtual ~CompletionContextHandler() = default;
87 /// Converts a Declaration completion result to a completion string, and then
88 /// stores it in Results.
89 virtual void handleDeclaration(const CodeCompletionResult
&Result
) {
90 auto PreferredType
= CCC
.getPreferredType();
91 if (PreferredType
.isNull()) {
92 Results
.push_back(Result
.Declaration
->getName().str());
96 if (auto *VD
= dyn_cast
<VarDecl
>(Result
.Declaration
)) {
97 auto ArgumentType
= VD
->getType();
98 if (PreferredType
->isReferenceType()) {
99 QualType RT
= PreferredType
->castAs
<ReferenceType
>()->getPointeeType();
100 Sema::ReferenceConversions RefConv
;
101 Sema::ReferenceCompareResult RefRelationship
=
102 S
.CompareReferenceRelationship(SourceLocation(), RT
, ArgumentType
,
104 switch (RefRelationship
) {
105 case Sema::Ref_Compatible
:
106 case Sema::Ref_Related
:
107 Results
.push_back(VD
->getName().str());
109 case Sema::Ref_Incompatible
:
112 } else if (S
.Context
.hasSameType(ArgumentType
, PreferredType
)) {
113 Results
.push_back(VD
->getName().str());
118 /// Converts a Keyword completion result to a completion string, and then
119 /// stores it in Results.
120 virtual void handleKeyword(const CodeCompletionResult
&Result
) {
121 auto Prefix
= S
.getPreprocessor().getCodeCompletionFilter();
122 // Add keyword to the completion results only if we are in a type-aware
124 if (!CCC
.getBaseType().isNull() || !CCC
.getPreferredType().isNull())
126 if (StringRef(Result
.Keyword
).starts_with(Prefix
))
127 Results
.push_back(Result
.Keyword
);
130 /// Converts a Pattern completion result to a completion string, and then
131 /// stores it in Results.
132 virtual void handlePattern(const CodeCompletionResult
&Result
) {}
134 /// Converts a Macro completion result to a completion string, and then stores
136 virtual void handleMacro(const CodeCompletionResult
&Result
) {}
139 class DotMemberAccessHandler
: public CompletionContextHandler
{
141 DotMemberAccessHandler(Sema
&S
, CodeCompletionContext CCC
,
142 std::vector
<std::string
> &Results
)
143 : CompletionContextHandler(S
, CCC
, Results
) {}
144 void handleDeclaration(const CodeCompletionResult
&Result
) override
{
145 auto *ID
= Result
.Declaration
->getIdentifier();
148 if (!isa
<CXXMethodDecl
>(Result
.Declaration
))
150 const auto *Fun
= cast
<CXXMethodDecl
>(Result
.Declaration
);
151 if (Fun
->getParent()->getCanonicalDecl() ==
152 CCC
.getBaseType()->getAsCXXRecordDecl()->getCanonicalDecl()) {
153 LLVM_DEBUG(llvm::dbgs() << "[In HandleCodeCompleteDOT] Name : "
154 << ID
->getName() << "\n");
155 Results
.push_back(ID
->getName().str());
159 void handleKeyword(const CodeCompletionResult
&Result
) override
{}
162 void ReplCompletionConsumer::ProcessCodeCompleteResults(
163 class Sema
&S
, CodeCompletionContext Context
,
164 CodeCompletionResult
*InResults
, unsigned NumResults
) {
166 auto Prefix
= S
.getPreprocessor().getCodeCompletionFilter();
169 std::unique_ptr
<CompletionContextHandler
> CCH
;
171 // initialize fine-grained code completion handler based on the code
172 // completion context.
173 switch (Context
.getKind()) {
174 case CodeCompletionContext::CCC_DotMemberAccess
:
175 CCH
.reset(new DotMemberAccessHandler(S
, Context
, this->Results
));
178 CCH
.reset(new CompletionContextHandler(S
, Context
, this->Results
));
181 for (unsigned I
= 0; I
< NumResults
; I
++) {
182 auto &Result
= InResults
[I
];
183 switch (Result
.Kind
) {
184 case CodeCompletionResult::RK_Declaration
:
188 if (!Result
.Declaration
->getDeclName().isIdentifier() ||
189 !Result
.Declaration
->getName().starts_with(Prefix
)) {
192 CCH
->handleDeclaration(Result
);
194 case CodeCompletionResult::RK_Keyword
:
195 CCH
->handleKeyword(Result
);
197 case CodeCompletionResult::RK_Macro
:
198 CCH
->handleMacro(Result
);
200 case CodeCompletionResult::RK_Pattern
:
201 CCH
->handlePattern(Result
);
206 std::sort(Results
.begin(), Results
.end());
209 class IncrementalSyntaxOnlyAction
: public SyntaxOnlyAction
{
210 const CompilerInstance
*ParentCI
;
213 IncrementalSyntaxOnlyAction(const CompilerInstance
*ParentCI
)
214 : ParentCI(ParentCI
) {}
217 void ExecuteAction() override
;
220 class ExternalSource
: public clang::ExternalASTSource
{
221 TranslationUnitDecl
*ChildTUDeclCtxt
;
222 ASTContext
&ParentASTCtxt
;
223 TranslationUnitDecl
*ParentTUDeclCtxt
;
225 std::unique_ptr
<ASTImporter
> Importer
;
228 ExternalSource(ASTContext
&ChildASTCtxt
, FileManager
&ChildFM
,
229 ASTContext
&ParentASTCtxt
, FileManager
&ParentFM
);
230 bool FindExternalVisibleDeclsByName(const DeclContext
*DC
,
231 DeclarationName Name
) override
;
233 completeVisibleDeclsMap(const clang::DeclContext
*childDeclContext
) override
;
236 // This method is intended to set up `ExternalASTSource` to the running
237 // compiler instance before the super `ExecuteAction` triggers parsing
238 void IncrementalSyntaxOnlyAction::ExecuteAction() {
239 CompilerInstance
&CI
= getCompilerInstance();
240 ExternalSource
*myExternalSource
=
241 new ExternalSource(CI
.getASTContext(), CI
.getFileManager(),
242 ParentCI
->getASTContext(), ParentCI
->getFileManager());
243 llvm::IntrusiveRefCntPtr
<clang::ExternalASTSource
> astContextExternalSource(
245 CI
.getASTContext().setExternalSource(astContextExternalSource
);
246 CI
.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(
249 // Load all external decls into current context. Under the hood, it calls
250 // ExternalSource::completeVisibleDeclsMap, which make all decls on the redecl
253 // This is crucial to code completion on dot members, since a bound variable
254 // before "." would be otherwise treated out-of-scope.
256 // clang-repl> Foo f1;
257 // clang-repl> f1.<tab>
258 CI
.getASTContext().getTranslationUnitDecl()->lookups();
259 SyntaxOnlyAction::ExecuteAction();
262 ExternalSource::ExternalSource(ASTContext
&ChildASTCtxt
, FileManager
&ChildFM
,
263 ASTContext
&ParentASTCtxt
, FileManager
&ParentFM
)
264 : ChildTUDeclCtxt(ChildASTCtxt
.getTranslationUnitDecl()),
265 ParentASTCtxt(ParentASTCtxt
),
266 ParentTUDeclCtxt(ParentASTCtxt
.getTranslationUnitDecl()) {
267 ASTImporter
*importer
=
268 new ASTImporter(ChildASTCtxt
, ChildFM
, ParentASTCtxt
, ParentFM
,
269 /*MinimalImport : ON*/ true);
270 Importer
.reset(importer
);
273 bool ExternalSource::FindExternalVisibleDeclsByName(const DeclContext
*DC
,
274 DeclarationName Name
) {
276 IdentifierTable
&ParentIdTable
= ParentASTCtxt
.Idents
;
278 auto ParentDeclName
=
279 DeclarationName(&(ParentIdTable
.get(Name
.getAsString())));
281 DeclContext::lookup_result lookup_result
=
282 ParentTUDeclCtxt
->lookup(ParentDeclName
);
284 if (!lookup_result
.empty()) {
290 void ExternalSource::completeVisibleDeclsMap(
291 const DeclContext
*ChildDeclContext
) {
292 assert(ChildDeclContext
&& ChildDeclContext
== ChildTUDeclCtxt
&&
293 "No child decl context!");
295 if (!ChildDeclContext
->hasExternalVisibleStorage())
298 for (auto *DeclCtxt
= ParentTUDeclCtxt
; DeclCtxt
!= nullptr;
299 DeclCtxt
= DeclCtxt
->getPreviousDecl()) {
300 for (auto &IDeclContext
: DeclCtxt
->decls()) {
301 if (!llvm::isa
<NamedDecl
>(IDeclContext
))
304 NamedDecl
*Decl
= llvm::cast
<NamedDecl
>(IDeclContext
);
306 auto DeclOrErr
= Importer
->Import(Decl
);
308 // if an error happens, it usually means the decl has already been
309 // imported or the decl is a result of a failed import. But in our
310 // case, every import is fresh each time code completion is
311 // triggered. So Import usually doesn't fail. If it does, it just means
312 // the related decl can't be used in code completion and we can safely
314 llvm::consumeError(DeclOrErr
.takeError());
318 if (!llvm::isa
<NamedDecl
>(*DeclOrErr
))
321 NamedDecl
*importedNamedDecl
= llvm::cast
<NamedDecl
>(*DeclOrErr
);
323 SetExternalVisibleDeclsForName(ChildDeclContext
,
324 importedNamedDecl
->getDeclName(),
327 if (!llvm::isa
<CXXRecordDecl
>(importedNamedDecl
))
330 auto *Record
= llvm::cast
<CXXRecordDecl
>(importedNamedDecl
);
332 if (auto Err
= Importer
->ImportDefinition(Decl
)) {
334 consumeError(std::move(Err
));
338 Record
->setHasLoadedFieldsFromExternalStorage(true);
339 LLVM_DEBUG(llvm::dbgs()
340 << "\nCXXRecrod : " << Record
->getName() << " size(methods): "
341 << std::distance(Record
->method_begin(), Record
->method_end())
342 << " has def?: " << Record
->hasDefinition()
344 << std::distance(Record
->getDefinition()->method_begin(),
345 Record
->getDefinition()->method_end())
347 for (auto *Meth
: Record
->methods())
348 SetExternalVisibleDeclsForName(ChildDeclContext
, Meth
->getDeclName(),
351 ChildDeclContext
->setHasExternalLexicalStorage(false);
355 void ReplCodeCompleter::codeComplete(CompilerInstance
*InterpCI
,
356 llvm::StringRef Content
, unsigned Line
,
358 const CompilerInstance
*ParentCI
,
359 std::vector
<std::string
> &CCResults
) {
360 auto DiagOpts
= DiagnosticOptions();
361 auto consumer
= ReplCompletionConsumer(CCResults
, *this);
363 auto diag
= InterpCI
->getDiagnosticsPtr();
364 std::unique_ptr
<ASTUnit
> AU(ASTUnit::LoadFromCompilerInvocationAction(
365 InterpCI
->getInvocationPtr(), std::make_shared
<PCHContainerOperations
>(),
367 llvm::SmallVector
<clang::StoredDiagnostic
, 8> sd
= {};
368 llvm::SmallVector
<const llvm::MemoryBuffer
*, 1> tb
= {};
369 InterpCI
->getFrontendOpts().Inputs
[0] = FrontendInputFile(
370 CodeCompletionFileName
, Language::CXX
, InputKind::Source
);
371 auto Act
= std::make_unique
<IncrementalSyntaxOnlyAction
>(ParentCI
);
372 std::unique_ptr
<llvm::MemoryBuffer
> MB
=
373 llvm::MemoryBuffer::getMemBufferCopy(Content
, CodeCompletionFileName
);
374 llvm::SmallVector
<ASTUnit::RemappedFile
, 4> RemappedFiles
;
376 RemappedFiles
.push_back(std::make_pair(CodeCompletionFileName
, MB
.get()));
377 // we don't want the AU destructor to release the memory buffer that MB
378 // owns twice, because MB handles its resource on its own.
379 AU
->setOwnsRemappedFileBuffers(false);
380 AU
->CodeComplete(CodeCompletionFileName
, 1, Col
, RemappedFiles
, false, false,
382 std::make_shared
<clang::PCHContainerOperations
>(), *diag
,
383 InterpCI
->getLangOpts(), AU
->getSourceManager(),
384 AU
->getFileManager(), sd
, tb
, std::move(Act
));