brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.2 KiB · dc7030c Raw
387 lines · cpp
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-exception6//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 30namespace clang {31 32const std::string CodeCompletionFileName = "input_line_[Completion]";33 34clang::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 43class ReplCompletionConsumer : public CodeCompleteConsumer {44public: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, we52  // create a `Context`-based handler (see classes defined below) to handle each53  // 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 62private: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 of70/// which handles one type of completion result.71/// Its derived classes are used to create concrete handlers based on72/// \c CodeCompletionContext.73class CompletionContextHandler {74protected:75  CodeCompletionContext CCC;76  std::vector<std::string> &Results;77 78private:79  Sema &S;80 81public: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 then88  /// 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());93      return;94    }95 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,103                                           &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 then119  /// 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-aware123    // situation.124    if (!CCC.getBaseType().isNull() || !CCC.getPreferredType().isNull())125      return;126    if (StringRef(Result.Keyword).starts_with(Prefix))127      Results.push_back(Result.Keyword);128  }129 130  /// Converts a Pattern completion result to a completion string, and then131  /// stores it in Results.132  virtual void handlePattern(const CodeCompletionResult &Result) {}133 134  /// Converts a Macro completion result to a completion string, and then stores135  /// it in Results.136  virtual void handleMacro(const CodeCompletionResult &Result) {}137};138 139class DotMemberAccessHandler : public CompletionContextHandler {140public: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>(Result.Declaration))149      return;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());156    }157  }158 159  void handleKeyword(const CodeCompletionResult &Result) override {}160};161 162void 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 code172  // 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(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(Results.begin(), Results.end());207}208 209class IncrementalSyntaxOnlyAction : public SyntaxOnlyAction {210  const CompilerInstance *ParentCI;211 212public:213  IncrementalSyntaxOnlyAction(const CompilerInstance *ParentCI)214      : ParentCI(ParentCI) {}215 216protected:217  void ExecuteAction() override;218};219 220class ExternalSource : public clang::ExternalASTSource {221  TranslationUnitDecl *ChildTUDeclCtxt;222  ASTContext &ParentASTCtxt;223  TranslationUnitDecl *ParentTUDeclCtxt;224 225  std::unique_ptr<ASTImporter> Importer;226 227public: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  void234  completeVisibleDeclsMap(const clang::DeclContext *childDeclContext) override;235};236 237// This method is intended to set up `ExternalASTSource` to the running238// compiler instance before the super `ExecuteAction` triggers parsing239void IncrementalSyntaxOnlyAction::ExecuteAction() {240  CompilerInstance &CI = getCompilerInstance();241  auto astContextExternalSource = llvm::makeIntrusiveRefCnt<ExternalSource>(242      CI.getASTContext(), CI.getFileManager(), ParentCI->getASTContext(),243      ParentCI->getFileManager());244  CI.getASTContext().setExternalSource(astContextExternalSource);245  CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(246      true);247 248  // Load all external decls into current context. Under the hood, it calls249  // ExternalSource::completeVisibleDeclsMap, which make all decls on the redecl250  // chain visible.251  //252  // This is crucial to code completion on dot members, since a bound variable253  // before "." would be otherwise treated out-of-scope.254  //255  // clang-repl> Foo f1;256  // clang-repl> f1.<tab>257  CI.getASTContext().getTranslationUnitDecl()->lookups();258  SyntaxOnlyAction::ExecuteAction();259}260 261ExternalSource::ExternalSource(ASTContext &ChildASTCtxt, FileManager &ChildFM,262                               ASTContext &ParentASTCtxt, FileManager &ParentFM)263    : ChildTUDeclCtxt(ChildASTCtxt.getTranslationUnitDecl()),264      ParentASTCtxt(ParentASTCtxt),265      ParentTUDeclCtxt(ParentASTCtxt.getTranslationUnitDecl()) {266  ASTImporter *importer =267      new ASTImporter(ChildASTCtxt, ChildFM, ParentASTCtxt, ParentFM,268                      /*MinimalImport : ON*/ true);269  Importer.reset(importer);270}271 272bool ExternalSource::FindExternalVisibleDeclsByName(273    const DeclContext *DC, DeclarationName Name,274    const DeclContext *OriginalDC) {275 276  IdentifierTable &ParentIdTable = ParentASTCtxt.Idents;277 278  auto ParentDeclName =279      DeclarationName(&(ParentIdTable.get(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 290void 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 been309        // imported or the decl is a result of a failed import.  But in our310        // case, every import is fresh each time code completion is311        // triggered. So Import usually doesn't fail. If it does, it just means312        // the related decl can't be used in code completion and we can safely313        // 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 above334        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 355void 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 consumer = ReplCompletionConsumer(CCResults, *this);361 362  auto diag = InterpCI->getDiagnosticsPtr();363  std::unique_ptr<ASTUnit> AU(ASTUnit::LoadFromCompilerInvocationAction(364      InterpCI->getInvocationPtr(), std::make_shared<PCHContainerOperations>(),365      nullptr, diag));366  llvm::SmallVector<clang::StoredDiagnostic, 8> sd = {};367  llvm::SmallVector<const llvm::MemoryBuffer *, 1> tb = {};368  InterpCI->getFrontendOpts().Inputs[0] = FrontendInputFile(369      CodeCompletionFileName, Language::CXX, InputKind::Source);370  auto Act = std::make_unique<IncrementalSyntaxOnlyAction>(ParentCI);371  std::unique_ptr<llvm::MemoryBuffer> MB =372      llvm::MemoryBuffer::getMemBufferCopy(Content, CodeCompletionFileName);373  llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles;374 375  RemappedFiles.push_back(std::make_pair(CodeCompletionFileName, MB.get()));376  // we don't want the AU destructor to release the memory buffer that MB377  // owns twice, because MB handles its resource on its own.378  AU->setOwnsRemappedFileBuffers(false);379  AU->CodeComplete(CodeCompletionFileName, 1, Col, RemappedFiles, false, false,380                   false, consumer,381                   std::make_shared<clang::PCHContainerOperations>(), diag,382                   InterpCI->getLangOpts(), AU->getSourceManagerPtr(),383                   AU->getFileManagerPtr(), sd, tb, std::move(Act));384}385 386} // namespace clang387