| 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(x: VD->getName().str()); |
| 108 | break; |
| 109 | case Sema::Ref_Incompatible: |
| 110 | break; |
| 111 | } |
| 112 | } else if (S.Context.hasSameType(T1: ArgumentType, T2: PreferredType)) { |
| 113 | Results.push_back(x: 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(p: 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, |
| 232 | const DeclContext *OriginalDC) override; |
| 233 | void |
| 234 | completeVisibleDeclsMap(const clang::DeclContext *childDeclContext) override; |
| 235 | }; |
| 236 | |
| 237 | // This method is intended to set up `ExternalASTSource` to the running |
| 238 | // compiler instance before the super `ExecuteAction` triggers parsing |
| 239 | void IncrementalSyntaxOnlyAction::ExecuteAction() { |
| 240 | CompilerInstance &CI = getCompilerInstance(); |
| 241 | ExternalSource *myExternalSource = |
| 242 | new ExternalSource(CI.getASTContext(), CI.getFileManager(), |
| 243 | ParentCI->getASTContext(), ParentCI->getFileManager()); |
| 244 | llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> astContextExternalSource( |
| 245 | myExternalSource); |
| 246 | CI.getASTContext().setExternalSource(astContextExternalSource); |
| 247 | CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage( |
| 248 | true); |
| 249 | |
| 250 | // Load all external decls into current context. Under the hood, it calls |
| 251 | // ExternalSource::completeVisibleDeclsMap, which make all decls on the redecl |
| 252 | // chain visible. |
| 253 | // |
| 254 | // This is crucial to code completion on dot members, since a bound variable |
| 255 | // before "." would be otherwise treated out-of-scope. |
| 256 | // |
| 257 | // clang-repl> Foo f1; |
| 258 | // clang-repl> f1.<tab> |
| 259 | CI.getASTContext().getTranslationUnitDecl()->lookups(); |
| 260 | SyntaxOnlyAction::ExecuteAction(); |
| 261 | } |
| 262 | |
| 263 | ExternalSource::ExternalSource(ASTContext &ChildASTCtxt, FileManager &ChildFM, |
| 264 | ASTContext &ParentASTCtxt, FileManager &ParentFM) |
| 265 | : ChildTUDeclCtxt(ChildASTCtxt.getTranslationUnitDecl()), |
| 266 | ParentASTCtxt(ParentASTCtxt), |
| 267 | ParentTUDeclCtxt(ParentASTCtxt.getTranslationUnitDecl()) { |
| 268 | ASTImporter *importer = |
| 269 | new ASTImporter(ChildASTCtxt, ChildFM, ParentASTCtxt, ParentFM, |
| 270 | /*MinimalImport : ON*/ true); |
| 271 | Importer.reset(p: importer); |
| 272 | } |
| 273 | |
| 274 | bool ExternalSource::FindExternalVisibleDeclsByName( |
| 275 | const DeclContext *DC, DeclarationName Name, |
| 276 | const DeclContext *OriginalDC) { |
| 277 | |
| 278 | IdentifierTable &ParentIdTable = ParentASTCtxt.Idents; |
| 279 | |
| 280 | auto ParentDeclName = |
| 281 | DeclarationName(&(ParentIdTable.get(Name: Name.getAsString()))); |
| 282 | |
| 283 | DeclContext::lookup_result lookup_result = |
| 284 | ParentTUDeclCtxt->lookup(Name: ParentDeclName); |
| 285 | |
| 286 | if (!lookup_result.empty()) { |
| 287 | return true; |
| 288 | } |
| 289 | return false; |
| 290 | } |
| 291 | |
| 292 | void ExternalSource::completeVisibleDeclsMap( |
| 293 | const DeclContext *ChildDeclContext) { |
| 294 | assert(ChildDeclContext && ChildDeclContext == ChildTUDeclCtxt && |
| 295 | "No child decl context!" ); |
| 296 | |
| 297 | if (!ChildDeclContext->hasExternalVisibleStorage()) |
| 298 | return; |
| 299 | |
| 300 | for (auto *DeclCtxt = ParentTUDeclCtxt; DeclCtxt != nullptr; |
| 301 | DeclCtxt = DeclCtxt->getPreviousDecl()) { |
| 302 | for (auto &IDeclContext : DeclCtxt->decls()) { |
| 303 | if (!llvm::isa<NamedDecl>(Val: IDeclContext)) |
| 304 | continue; |
| 305 | |
| 306 | NamedDecl *Decl = llvm::cast<NamedDecl>(Val: IDeclContext); |
| 307 | |
| 308 | auto DeclOrErr = Importer->Import(FromD: Decl); |
| 309 | if (!DeclOrErr) { |
| 310 | // if an error happens, it usually means the decl has already been |
| 311 | // imported or the decl is a result of a failed import. But in our |
| 312 | // case, every import is fresh each time code completion is |
| 313 | // triggered. So Import usually doesn't fail. If it does, it just means |
| 314 | // the related decl can't be used in code completion and we can safely |
| 315 | // drop it. |
| 316 | llvm::consumeError(Err: DeclOrErr.takeError()); |
| 317 | continue; |
| 318 | } |
| 319 | |
| 320 | if (!llvm::isa<NamedDecl>(Val: *DeclOrErr)) |
| 321 | continue; |
| 322 | |
| 323 | NamedDecl *importedNamedDecl = llvm::cast<NamedDecl>(Val: *DeclOrErr); |
| 324 | |
| 325 | SetExternalVisibleDeclsForName(DC: ChildDeclContext, |
| 326 | Name: importedNamedDecl->getDeclName(), |
| 327 | Decls: importedNamedDecl); |
| 328 | |
| 329 | if (!llvm::isa<CXXRecordDecl>(Val: importedNamedDecl)) |
| 330 | continue; |
| 331 | |
| 332 | auto *Record = llvm::cast<CXXRecordDecl>(Val: importedNamedDecl); |
| 333 | |
| 334 | if (auto Err = Importer->ImportDefinition(From: Decl)) { |
| 335 | // the same as above |
| 336 | consumeError(Err: std::move(Err)); |
| 337 | continue; |
| 338 | } |
| 339 | |
| 340 | Record->setHasLoadedFieldsFromExternalStorage(true); |
| 341 | LLVM_DEBUG(llvm::dbgs() |
| 342 | << "\nCXXRecrod : " << Record->getName() << " size(methods): " |
| 343 | << std::distance(Record->method_begin(), Record->method_end()) |
| 344 | << " has def?: " << Record->hasDefinition() |
| 345 | << " # (methods): " |
| 346 | << std::distance(Record->getDefinition()->method_begin(), |
| 347 | Record->getDefinition()->method_end()) |
| 348 | << "\n" ); |
| 349 | for (auto *Meth : Record->methods()) |
| 350 | SetExternalVisibleDeclsForName(DC: ChildDeclContext, Name: Meth->getDeclName(), |
| 351 | Decls: Meth); |
| 352 | } |
| 353 | ChildDeclContext->setHasExternalLexicalStorage(false); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | void ReplCodeCompleter::codeComplete(CompilerInstance *InterpCI, |
| 358 | llvm::StringRef Content, unsigned Line, |
| 359 | unsigned Col, |
| 360 | const CompilerInstance *ParentCI, |
| 361 | std::vector<std::string> &CCResults) { |
| 362 | auto consumer = ReplCompletionConsumer(CCResults, *this); |
| 363 | |
| 364 | auto diag = InterpCI->getDiagnosticsPtr(); |
| 365 | std::unique_ptr<ASTUnit> AU(ASTUnit::LoadFromCompilerInvocationAction( |
| 366 | CI: InterpCI->getInvocationPtr(), PCHContainerOps: std::make_shared<PCHContainerOperations>(), |
| 367 | DiagOpts: nullptr, Diags: diag)); |
| 368 | llvm::SmallVector<clang::StoredDiagnostic, 8> sd = {}; |
| 369 | llvm::SmallVector<const llvm::MemoryBuffer *, 1> tb = {}; |
| 370 | InterpCI->getFrontendOpts().Inputs[0] = FrontendInputFile( |
| 371 | CodeCompletionFileName, Language::CXX, InputKind::Source); |
| 372 | auto Act = std::make_unique<IncrementalSyntaxOnlyAction>(args&: 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&: AU->getSourceManager(), |
| 385 | FileMgr&: AU->getFileManager(), StoredDiagnostics&: sd, OwnedBuffers&: tb, Act: std::move(Act)); |
| 386 | } |
| 387 | |
| 388 | } // namespace clang |
| 389 | |