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/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" |
29 | |
30 | namespace clang { |
31 | |
32 | const std::string CodeCompletionFileName = "input_line_[Completion]" ; |
33 | |
34 | clang::CodeCompleteOptions getClangCompleteOpts() { |
35 | clang::CodeCompleteOptions Opts; |
36 | Opts.IncludeCodePatterns = true; |
37 | Opts.IncludeMacros = true; |
38 | Opts.IncludeGlobals = true; |
39 | Opts.IncludeBriefComments = true; |
40 | return Opts; |
41 | } |
42 | |
43 | class ReplCompletionConsumer : public CodeCompleteConsumer { |
44 | public: |
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) {} |
50 | |
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 |
53 | // completion result. |
54 | void ProcessCodeCompleteResults(class Sema &S, CodeCompletionContext Context, |
55 | CodeCompletionResult *InResults, |
56 | unsigned NumResults) final; |
57 | |
58 | CodeCompletionAllocator &getAllocator() override { return *CCAllocator; } |
59 | |
60 | CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } |
61 | |
62 | private: |
63 | std::shared_ptr<GlobalCodeCompletionAllocator> CCAllocator; |
64 | CodeCompletionTUInfo CCTUInfo; |
65 | std::vector<std::string> &Results; |
66 | ReplCodeCompleter &CC; |
67 | }; |
68 | |
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 { |
74 | protected: |
75 | CodeCompletionContext CCC; |
76 | std::vector<std::string> &Results; |
77 | |
78 | private: |
79 | Sema &S; |
80 | |
81 | public: |
82 | CompletionContextHandler(Sema &S, CodeCompletionContext CCC, |
83 | std::vector<std::string> &Results) |
84 | : CCC(CCC), Results(Results), S(S) {} |
85 | |
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(x: Result.Declaration->getName().str()); |
93 | return; |
94 | } |
95 | |
96 | if (auto *VD = dyn_cast<VarDecl>(Val: 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(Loc: SourceLocation(), T1: RT, T2: ArgumentType, |
103 | Conv: &RefConv); |
104 | switch (RefRelationship) { |
105 | case Sema::Ref_Compatible: |
106 | case Sema::Ref_Related: |
107 | Results.push_back(VD->getName().str()); |
108 | break; |
109 | case Sema::Ref_Incompatible: |
110 | break; |
111 | } |
112 | } else if (S.Context.hasSameType(ArgumentType, PreferredType)) { |
113 | Results.push_back(VD->getName().str()); |
114 | } |
115 | } |
116 | } |
117 | |
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 |
123 | // situation. |
124 | if (!CCC.getBaseType().isNull() || !CCC.getPreferredType().isNull()) |
125 | return; |
126 | if (StringRef(Result.Keyword).starts_with(Prefix)) |
127 | Results.push_back(x: Result.Keyword); |
128 | } |
129 | |
130 | /// Converts a Pattern completion result to a completion string, and then |
131 | /// stores it in Results. |
132 | virtual void handlePattern(const CodeCompletionResult &Result) {} |
133 | |
134 | /// Converts a Macro completion result to a completion string, and then stores |
135 | /// it in Results. |
136 | virtual void handleMacro(const CodeCompletionResult &Result) {} |
137 | }; |
138 | |
139 | class DotMemberAccessHandler : public CompletionContextHandler { |
140 | public: |
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(); |
146 | if (!ID) |
147 | return; |
148 | if (!isa<CXXMethodDecl>(Val: Result.Declaration)) |
149 | return; |
150 | const auto *Fun = cast<CXXMethodDecl>(Val: 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(x: ID->getName().str()); |
156 | } |
157 | } |
158 | |
159 | void handleKeyword(const CodeCompletionResult &Result) override {} |
160 | }; |
161 | |
162 | void ReplCompletionConsumer::ProcessCodeCompleteResults( |
163 | class Sema &S, CodeCompletionContext Context, |
164 | CodeCompletionResult *InResults, unsigned NumResults) { |
165 | |
166 | auto Prefix = S.getPreprocessor().getCodeCompletionFilter(); |
167 | CC.Prefix = Prefix; |
168 | |
169 | std::unique_ptr<CompletionContextHandler> CCH; |
170 | |
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)); |
176 | break; |
177 | default: |
178 | CCH.reset(p: new CompletionContextHandler(S, Context, this->Results)); |
179 | }; |
180 | |
181 | for (unsigned I = 0; I < NumResults; I++) { |
182 | auto &Result = InResults[I]; |
183 | switch (Result.Kind) { |
184 | case CodeCompletionResult::RK_Declaration: |
185 | if (Result.Hidden) { |
186 | break; |
187 | } |
188 | if (!Result.Declaration->getDeclName().isIdentifier() || |
189 | !Result.Declaration->getName().starts_with(Prefix)) { |
190 | break; |
191 | } |
192 | CCH->handleDeclaration(Result); |
193 | break; |
194 | case CodeCompletionResult::RK_Keyword: |
195 | CCH->handleKeyword(Result); |
196 | break; |
197 | case CodeCompletionResult::RK_Macro: |
198 | CCH->handleMacro(Result); |
199 | break; |
200 | case CodeCompletionResult::RK_Pattern: |
201 | CCH->handlePattern(Result); |
202 | break; |
203 | } |
204 | } |
205 | |
206 | std::sort(first: Results.begin(), last: Results.end()); |
207 | } |
208 | |
209 | class IncrementalSyntaxOnlyAction : public SyntaxOnlyAction { |
210 | const CompilerInstance *ParentCI; |
211 | |
212 | public: |
213 | IncrementalSyntaxOnlyAction(const CompilerInstance *ParentCI) |
214 | : ParentCI(ParentCI) {} |
215 | |
216 | protected: |
217 | void ExecuteAction() override; |
218 | }; |
219 | |
220 | class ExternalSource : public clang::ExternalASTSource { |
221 | TranslationUnitDecl *ChildTUDeclCtxt; |
222 | ASTContext &ParentASTCtxt; |
223 | TranslationUnitDecl *ParentTUDeclCtxt; |
224 | |
225 | std::unique_ptr<ASTImporter> Importer; |
226 | |
227 | public: |
228 | ExternalSource(ASTContext &ChildASTCtxt, FileManager &ChildFM, |
229 | ASTContext &ParentASTCtxt, FileManager &ParentFM); |
230 | bool FindExternalVisibleDeclsByName(const DeclContext *DC, |
231 | DeclarationName Name) override; |
232 | void |
233 | completeVisibleDeclsMap(const clang::DeclContext *childDeclContext) override; |
234 | }; |
235 | |
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( |
244 | myExternalSource); |
245 | CI.getASTContext().setExternalSource(astContextExternalSource); |
246 | CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage( |
247 | true); |
248 | |
249 | // Load all external decls into current context. Under the hood, it calls |
250 | // ExternalSource::completeVisibleDeclsMap, which make all decls on the redecl |
251 | // chain visible. |
252 | // |
253 | // This is crucial to code completion on dot members, since a bound variable |
254 | // before "." would be otherwise treated out-of-scope. |
255 | // |
256 | // clang-repl> Foo f1; |
257 | // clang-repl> f1.<tab> |
258 | CI.getASTContext().getTranslationUnitDecl()->lookups(); |
259 | SyntaxOnlyAction::ExecuteAction(); |
260 | } |
261 | |
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(p: importer); |
271 | } |
272 | |
273 | bool ExternalSource::FindExternalVisibleDeclsByName(const DeclContext *DC, |
274 | DeclarationName Name) { |
275 | |
276 | IdentifierTable &ParentIdTable = ParentASTCtxt.Idents; |
277 | |
278 | auto ParentDeclName = |
279 | DeclarationName(&(ParentIdTable.get(Name: Name.getAsString()))); |
280 | |
281 | DeclContext::lookup_result lookup_result = |
282 | ParentTUDeclCtxt->lookup(ParentDeclName); |
283 | |
284 | if (!lookup_result.empty()) { |
285 | return true; |
286 | } |
287 | return false; |
288 | } |
289 | |
290 | void ExternalSource::completeVisibleDeclsMap( |
291 | const DeclContext *ChildDeclContext) { |
292 | assert(ChildDeclContext && ChildDeclContext == ChildTUDeclCtxt && |
293 | "No child decl context!" ); |
294 | |
295 | if (!ChildDeclContext->hasExternalVisibleStorage()) |
296 | return; |
297 | |
298 | for (auto *DeclCtxt = ParentTUDeclCtxt; DeclCtxt != nullptr; |
299 | DeclCtxt = DeclCtxt->getPreviousDecl()) { |
300 | for (auto &IDeclContext : DeclCtxt->decls()) { |
301 | if (!llvm::isa<NamedDecl>(IDeclContext)) |
302 | continue; |
303 | |
304 | NamedDecl *Decl = llvm::cast<NamedDecl>(IDeclContext); |
305 | |
306 | auto DeclOrErr = Importer->Import(Decl); |
307 | if (!DeclOrErr) { |
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 |
313 | // drop it. |
314 | llvm::consumeError(DeclOrErr.takeError()); |
315 | continue; |
316 | } |
317 | |
318 | if (!llvm::isa<NamedDecl>(*DeclOrErr)) |
319 | continue; |
320 | |
321 | NamedDecl *importedNamedDecl = llvm::cast<NamedDecl>(*DeclOrErr); |
322 | |
323 | SetExternalVisibleDeclsForName(ChildDeclContext, |
324 | importedNamedDecl->getDeclName(), |
325 | importedNamedDecl); |
326 | |
327 | if (!llvm::isa<CXXRecordDecl>(importedNamedDecl)) |
328 | continue; |
329 | |
330 | auto *Record = llvm::cast<CXXRecordDecl>(importedNamedDecl); |
331 | |
332 | if (auto Err = Importer->ImportDefinition(Decl)) { |
333 | // the same as above |
334 | consumeError(std::move(Err)); |
335 | continue; |
336 | } |
337 | |
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() |
343 | << " # (methods): " |
344 | << std::distance(Record->getDefinition()->method_begin(), |
345 | Record->getDefinition()->method_end()) |
346 | << "\n" ); |
347 | for (auto *Meth : Record->methods()) |
348 | SetExternalVisibleDeclsForName(ChildDeclContext, Meth->getDeclName(), |
349 | Meth); |
350 | } |
351 | ChildDeclContext->setHasExternalLexicalStorage(false); |
352 | } |
353 | } |
354 | |
355 | void ReplCodeCompleter::codeComplete(CompilerInstance *InterpCI, |
356 | llvm::StringRef Content, unsigned Line, |
357 | unsigned Col, |
358 | const CompilerInstance *ParentCI, |
359 | std::vector<std::string> &CCResults) { |
360 | auto DiagOpts = DiagnosticOptions(); |
361 | auto consumer = ReplCompletionConsumer(CCResults, *this); |
362 | |
363 | auto diag = InterpCI->getDiagnosticsPtr(); |
364 | std::unique_ptr<ASTUnit> AU(ASTUnit::LoadFromCompilerInvocationAction( |
365 | CI: InterpCI->getInvocationPtr(), PCHContainerOps: std::make_shared<PCHContainerOperations>(), |
366 | Diags: diag)); |
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::unique_ptr<IncrementalSyntaxOnlyAction>( |
372 | new IncrementalSyntaxOnlyAction(ParentCI)); |
373 | std::unique_ptr<llvm::MemoryBuffer> MB = |
374 | llvm::MemoryBuffer::getMemBufferCopy(InputData: Content, BufferName: CodeCompletionFileName); |
375 | llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles; |
376 | |
377 | RemappedFiles.push_back(Elt: std::make_pair(x: CodeCompletionFileName, y: MB.get())); |
378 | // we don't want the AU destructor to release the memory buffer that MB |
379 | // owns twice, because MB handles its resource on its own. |
380 | AU->setOwnsRemappedFileBuffers(false); |
381 | AU->CodeComplete(File: CodeCompletionFileName, Line: 1, Column: Col, RemappedFiles, IncludeMacros: false, IncludeCodePatterns: false, |
382 | IncludeBriefComments: false, Consumer&: consumer, |
383 | PCHContainerOps: std::make_shared<clang::PCHContainerOperations>(), Diag&: *diag, |
384 | LangOpts&: InterpCI->getLangOpts(), SourceMgr&: InterpCI->getSourceManager(), |
385 | FileMgr&: InterpCI->getFileManager(), StoredDiagnostics&: sd, OwnedBuffers&: tb, Act: std::move(Act)); |
386 | } |
387 | |
388 | } // namespace clang |
389 | |