| //===--- ClangdUnit.cpp -----------------------------------------*- C++-*-===// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===---------------------------------------------------------------------===// | 
 |  | 
 | #include "ClangdUnit.h" | 
 | #include "Compiler.h" | 
 | #include "Diagnostics.h" | 
 | #include "Logger.h" | 
 | #include "SourceCode.h" | 
 | #include "Trace.h" | 
 | #include "clang/AST/ASTContext.h" | 
 | #include "clang/Basic/LangOptions.h" | 
 | #include "clang/Frontend/CompilerInstance.h" | 
 | #include "clang/Frontend/CompilerInvocation.h" | 
 | #include "clang/Frontend/FrontendActions.h" | 
 | #include "clang/Frontend/Utils.h" | 
 | #include "clang/Index/IndexDataConsumer.h" | 
 | #include "clang/Index/IndexingAction.h" | 
 | #include "clang/Lex/Lexer.h" | 
 | #include "clang/Lex/MacroInfo.h" | 
 | #include "clang/Lex/Preprocessor.h" | 
 | #include "clang/Lex/PreprocessorOptions.h" | 
 | #include "clang/Sema/Sema.h" | 
 | #include "clang/Serialization/ASTWriter.h" | 
 | #include "clang/Tooling/CompilationDatabase.h" | 
 | #include "llvm/ADT/ArrayRef.h" | 
 | #include "llvm/ADT/SmallVector.h" | 
 | #include "llvm/Support/CrashRecoveryContext.h" | 
 | #include "llvm/Support/raw_ostream.h" | 
 | #include <algorithm> | 
 |  | 
 | using namespace clang::clangd; | 
 | using namespace clang; | 
 |  | 
 | namespace { | 
 |  | 
 | bool compileCommandsAreEqual(const tooling::CompileCommand &LHS, | 
 |                              const tooling::CompileCommand &RHS) { | 
 |   // We don't check for Output, it should not matter to clangd. | 
 |   return LHS.Directory == RHS.Directory && LHS.Filename == RHS.Filename && | 
 |          llvm::makeArrayRef(LHS.CommandLine).equals(RHS.CommandLine); | 
 | } | 
 |  | 
 | template <class T> std::size_t getUsedBytes(const std::vector<T> &Vec) { | 
 |   return Vec.capacity() * sizeof(T); | 
 | } | 
 |  | 
 | class DeclTrackingASTConsumer : public ASTConsumer { | 
 | public: | 
 |   DeclTrackingASTConsumer(std::vector<Decl *> &TopLevelDecls) | 
 |       : TopLevelDecls(TopLevelDecls) {} | 
 |  | 
 |   bool HandleTopLevelDecl(DeclGroupRef DG) override { | 
 |     for (Decl *D : DG) { | 
 |       // ObjCMethodDecl are not actually top-level decls. | 
 |       if (isa<ObjCMethodDecl>(D)) | 
 |         continue; | 
 |  | 
 |       TopLevelDecls.push_back(D); | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 | private: | 
 |   std::vector<Decl *> &TopLevelDecls; | 
 | }; | 
 |  | 
 | class ClangdFrontendAction : public SyntaxOnlyAction { | 
 | public: | 
 |   std::vector<Decl *> takeTopLevelDecls() { return std::move(TopLevelDecls); } | 
 |  | 
 | protected: | 
 |   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, | 
 |                                                  StringRef InFile) override { | 
 |     return llvm::make_unique<DeclTrackingASTConsumer>(/*ref*/ TopLevelDecls); | 
 |   } | 
 |  | 
 | private: | 
 |   std::vector<Decl *> TopLevelDecls; | 
 | }; | 
 |  | 
 | class CppFilePreambleCallbacks : public PreambleCallbacks { | 
 | public: | 
 |   CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback) | 
 |       : File(File), ParsedCallback(ParsedCallback) {} | 
 |  | 
 |   IncludeStructure takeIncludes() { return std::move(Includes); } | 
 |  | 
 |   void AfterExecute(CompilerInstance &CI) override { | 
 |     if (!ParsedCallback) | 
 |       return; | 
 |     trace::Span Tracer("Running PreambleCallback"); | 
 |     ParsedCallback(File, CI.getASTContext(), CI.getPreprocessorPtr()); | 
 |   } | 
 |  | 
 |   void BeforeExecute(CompilerInstance &CI) override { | 
 |     SourceMgr = &CI.getSourceManager(); | 
 |   } | 
 |  | 
 |   std::unique_ptr<PPCallbacks> createPPCallbacks() override { | 
 |     assert(SourceMgr && "SourceMgr must be set at this point"); | 
 |     return collectIncludeStructureCallback(*SourceMgr, &Includes); | 
 |   } | 
 |  | 
 | private: | 
 |   PathRef File; | 
 |   PreambleParsedCallback ParsedCallback; | 
 |   IncludeStructure Includes; | 
 |   SourceManager *SourceMgr = nullptr; | 
 | }; | 
 |  | 
 | } // namespace | 
 |  | 
 | void clangd::dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) { | 
 |   AST.getASTContext().getTranslationUnitDecl()->dump(OS, true); | 
 | } | 
 |  | 
 | llvm::Optional<ParsedAST> | 
 | ParsedAST::build(std::unique_ptr<clang::CompilerInvocation> CI, | 
 |                  std::shared_ptr<const PreambleData> Preamble, | 
 |                  std::unique_ptr<llvm::MemoryBuffer> Buffer, | 
 |                  std::shared_ptr<PCHContainerOperations> PCHs, | 
 |                  IntrusiveRefCntPtr<vfs::FileSystem> VFS) { | 
 |   assert(CI); | 
 |   // Command-line parsing sets DisableFree to true by default, but we don't want | 
 |   // to leak memory in clangd. | 
 |   CI->getFrontendOpts().DisableFree = false; | 
 |   const PrecompiledPreamble *PreamblePCH = | 
 |       Preamble ? &Preamble->Preamble : nullptr; | 
 |  | 
 |   StoreDiags ASTDiags; | 
 |   auto Clang = | 
 |       prepareCompilerInstance(std::move(CI), PreamblePCH, std::move(Buffer), | 
 |                               std::move(PCHs), std::move(VFS), ASTDiags); | 
 |   if (!Clang) | 
 |     return llvm::None; | 
 |  | 
 |   // Recover resources if we crash before exiting this method. | 
 |   llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup( | 
 |       Clang.get()); | 
 |  | 
 |   auto Action = llvm::make_unique<ClangdFrontendAction>(); | 
 |   const FrontendInputFile &MainInput = Clang->getFrontendOpts().Inputs[0]; | 
 |   if (!Action->BeginSourceFile(*Clang, MainInput)) { | 
 |     log("BeginSourceFile() failed when building AST for {0}", | 
 |         MainInput.getFile()); | 
 |     return llvm::None; | 
 |   } | 
 |  | 
 |   // Copy over the includes from the preamble, then combine with the | 
 |   // non-preamble includes below. | 
 |   auto Includes = Preamble ? Preamble->Includes : IncludeStructure{}; | 
 |   Clang->getPreprocessor().addPPCallbacks( | 
 |       collectIncludeStructureCallback(Clang->getSourceManager(), &Includes)); | 
 |  | 
 |   if (!Action->Execute()) | 
 |     log("Execute() failed when building AST for {0}", MainInput.getFile()); | 
 |  | 
 |   // UnitDiagsConsumer is local, we can not store it in CompilerInstance that | 
 |   // has a longer lifetime. | 
 |   Clang->getDiagnostics().setClient(new IgnoreDiagnostics); | 
 |   // CompilerInstance won't run this callback, do it directly. | 
 |   ASTDiags.EndSourceFile(); | 
 |  | 
 |   std::vector<Decl *> ParsedDecls = Action->takeTopLevelDecls(); | 
 |   std::vector<Diag> Diags = ASTDiags.take(); | 
 |   // Add diagnostics from the preamble, if any. | 
 |   if (Preamble) | 
 |     Diags.insert(Diags.begin(), Preamble->Diags.begin(), Preamble->Diags.end()); | 
 |   return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action), | 
 |                    std::move(ParsedDecls), std::move(Diags), | 
 |                    std::move(Includes)); | 
 | } | 
 |  | 
 | ParsedAST::ParsedAST(ParsedAST &&Other) = default; | 
 |  | 
 | ParsedAST &ParsedAST::operator=(ParsedAST &&Other) = default; | 
 |  | 
 | ParsedAST::~ParsedAST() { | 
 |   if (Action) { | 
 |     Action->EndSourceFile(); | 
 |   } | 
 | } | 
 |  | 
 | ASTContext &ParsedAST::getASTContext() { return Clang->getASTContext(); } | 
 |  | 
 | const ASTContext &ParsedAST::getASTContext() const { | 
 |   return Clang->getASTContext(); | 
 | } | 
 |  | 
 | Preprocessor &ParsedAST::getPreprocessor() { return Clang->getPreprocessor(); } | 
 |  | 
 | std::shared_ptr<Preprocessor> ParsedAST::getPreprocessorPtr() { | 
 |   return Clang->getPreprocessorPtr(); | 
 | } | 
 |  | 
 | const Preprocessor &ParsedAST::getPreprocessor() const { | 
 |   return Clang->getPreprocessor(); | 
 | } | 
 |  | 
 | ArrayRef<Decl *> ParsedAST::getLocalTopLevelDecls() { | 
 |   return LocalTopLevelDecls; | 
 | } | 
 |  | 
 | const std::vector<Diag> &ParsedAST::getDiagnostics() const { return Diags; } | 
 |  | 
 | std::size_t ParsedAST::getUsedBytes() const { | 
 |   auto &AST = getASTContext(); | 
 |   // FIXME(ibiryukov): we do not account for the dynamically allocated part of | 
 |   // Message and Fixes inside each diagnostic. | 
 |   std::size_t Total = | 
 |       ::getUsedBytes(LocalTopLevelDecls) + ::getUsedBytes(Diags); | 
 |  | 
 |   // FIXME: the rest of the function is almost a direct copy-paste from | 
 |   // libclang's clang_getCXTUResourceUsage. We could share the implementation. | 
 |  | 
 |   // Sum up variaous allocators inside the ast context and the preprocessor. | 
 |   Total += AST.getASTAllocatedMemory(); | 
 |   Total += AST.getSideTableAllocatedMemory(); | 
 |   Total += AST.Idents.getAllocator().getTotalMemory(); | 
 |   Total += AST.Selectors.getTotalMemory(); | 
 |  | 
 |   Total += AST.getSourceManager().getContentCacheSize(); | 
 |   Total += AST.getSourceManager().getDataStructureSizes(); | 
 |   Total += AST.getSourceManager().getMemoryBufferSizes().malloc_bytes; | 
 |  | 
 |   if (ExternalASTSource *Ext = AST.getExternalSource()) | 
 |     Total += Ext->getMemoryBufferSizes().malloc_bytes; | 
 |  | 
 |   const Preprocessor &PP = getPreprocessor(); | 
 |   Total += PP.getTotalMemory(); | 
 |   if (PreprocessingRecord *PRec = PP.getPreprocessingRecord()) | 
 |     Total += PRec->getTotalMemory(); | 
 |   Total += PP.getHeaderSearchInfo().getTotalMemory(); | 
 |  | 
 |   return Total; | 
 | } | 
 |  | 
 | const IncludeStructure &ParsedAST::getIncludeStructure() const { | 
 |   return Includes; | 
 | } | 
 |  | 
 | PreambleData::PreambleData(PrecompiledPreamble Preamble, | 
 |                            std::vector<Diag> Diags, IncludeStructure Includes) | 
 |     : Preamble(std::move(Preamble)), Diags(std::move(Diags)), | 
 |       Includes(std::move(Includes)) {} | 
 |  | 
 | ParsedAST::ParsedAST(std::shared_ptr<const PreambleData> Preamble, | 
 |                      std::unique_ptr<CompilerInstance> Clang, | 
 |                      std::unique_ptr<FrontendAction> Action, | 
 |                      std::vector<Decl *> LocalTopLevelDecls, | 
 |                      std::vector<Diag> Diags, IncludeStructure Includes) | 
 |     : Preamble(std::move(Preamble)), Clang(std::move(Clang)), | 
 |       Action(std::move(Action)), Diags(std::move(Diags)), | 
 |       LocalTopLevelDecls(std::move(LocalTopLevelDecls)), | 
 |       Includes(std::move(Includes)) { | 
 |   assert(this->Clang); | 
 |   assert(this->Action); | 
 | } | 
 |  | 
 | std::unique_ptr<CompilerInvocation> | 
 | clangd::buildCompilerInvocation(const ParseInputs &Inputs) { | 
 |   std::vector<const char *> ArgStrs; | 
 |   for (const auto &S : Inputs.CompileCommand.CommandLine) | 
 |     ArgStrs.push_back(S.c_str()); | 
 |  | 
 |   if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) { | 
 |     log("Couldn't set working directory when creating compiler invocation."); | 
 |     // We proceed anyway, our lit-tests rely on results for non-existing working | 
 |     // dirs. | 
 |   } | 
 |  | 
 |   // FIXME(ibiryukov): store diagnostics from CommandLine when we start | 
 |   // reporting them. | 
 |   IgnoreDiagnostics IgnoreDiagnostics; | 
 |   IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine = | 
 |       CompilerInstance::createDiagnostics(new DiagnosticOptions, | 
 |                                           &IgnoreDiagnostics, false); | 
 |   std::unique_ptr<CompilerInvocation> CI = createInvocationFromCommandLine( | 
 |       ArgStrs, CommandLineDiagsEngine, Inputs.FS); | 
 |   if (!CI) | 
 |     return nullptr; | 
 |   // createInvocationFromCommandLine sets DisableFree. | 
 |   CI->getFrontendOpts().DisableFree = false; | 
 |   CI->getLangOpts()->CommentOpts.ParseAllComments = true; | 
 |   return CI; | 
 | } | 
 |  | 
 | std::shared_ptr<const PreambleData> clangd::buildPreamble( | 
 |     PathRef FileName, CompilerInvocation &CI, | 
 |     std::shared_ptr<const PreambleData> OldPreamble, | 
 |     const tooling::CompileCommand &OldCompileCommand, const ParseInputs &Inputs, | 
 |     std::shared_ptr<PCHContainerOperations> PCHs, bool StoreInMemory, | 
 |     PreambleParsedCallback PreambleCallback) { | 
 |   // Note that we don't need to copy the input contents, preamble can live | 
 |   // without those. | 
 |   auto ContentsBuffer = llvm::MemoryBuffer::getMemBuffer(Inputs.Contents); | 
 |   auto Bounds = | 
 |       ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0); | 
 |  | 
 |   if (OldPreamble && | 
 |       compileCommandsAreEqual(Inputs.CompileCommand, OldCompileCommand) && | 
 |       OldPreamble->Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds, | 
 |                                      Inputs.FS.get())) { | 
 |     vlog("Reusing preamble for file {0}", Twine(FileName)); | 
 |     return OldPreamble; | 
 |   } | 
 |   vlog("Preamble for file {0} cannot be reused. Attempting to rebuild it.", | 
 |        FileName); | 
 |  | 
 |   trace::Span Tracer("BuildPreamble"); | 
 |   SPAN_ATTACH(Tracer, "File", FileName); | 
 |   StoreDiags PreambleDiagnostics; | 
 |   IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine = | 
 |       CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), | 
 |                                           &PreambleDiagnostics, false); | 
 |  | 
 |   // Skip function bodies when building the preamble to speed up building | 
 |   // the preamble and make it smaller. | 
 |   assert(!CI.getFrontendOpts().SkipFunctionBodies); | 
 |   CI.getFrontendOpts().SkipFunctionBodies = true; | 
 |   // We don't want to write comment locations into PCH. They are racy and slow | 
 |   // to read back. We rely on dynamic index for the comments instead. | 
 |   CI.getPreprocessorOpts().WriteCommentListToPCH = false; | 
 |  | 
 |   CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback); | 
 |   if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) { | 
 |     log("Couldn't set working directory when building the preamble."); | 
 |     // We proceed anyway, our lit-tests rely on results for non-existing working | 
 |     // dirs. | 
 |   } | 
 |   auto BuiltPreamble = PrecompiledPreamble::Build( | 
 |       CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, Inputs.FS, PCHs, | 
 |       StoreInMemory, SerializedDeclsCollector); | 
 |  | 
 |   // When building the AST for the main file, we do want the function | 
 |   // bodies. | 
 |   CI.getFrontendOpts().SkipFunctionBodies = false; | 
 |  | 
 |   if (BuiltPreamble) { | 
 |     vlog("Built preamble of size {0} for file {1}", BuiltPreamble->getSize(), | 
 |          FileName); | 
 |     return std::make_shared<PreambleData>( | 
 |         std::move(*BuiltPreamble), PreambleDiagnostics.take(), | 
 |         SerializedDeclsCollector.takeIncludes()); | 
 |   } else { | 
 |     elog("Could not build a preamble for file {0}", FileName); | 
 |     return nullptr; | 
 |   } | 
 | } | 
 |  | 
 | llvm::Optional<ParsedAST> clangd::buildAST( | 
 |     PathRef FileName, std::unique_ptr<CompilerInvocation> Invocation, | 
 |     const ParseInputs &Inputs, std::shared_ptr<const PreambleData> Preamble, | 
 |     std::shared_ptr<PCHContainerOperations> PCHs) { | 
 |   trace::Span Tracer("BuildAST"); | 
 |   SPAN_ATTACH(Tracer, "File", FileName); | 
 |  | 
 |   if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) { | 
 |     log("Couldn't set working directory when building the preamble."); | 
 |     // We proceed anyway, our lit-tests rely on results for non-existing working | 
 |     // dirs. | 
 |   } | 
 |  | 
 |   return ParsedAST::build( | 
 |       llvm::make_unique<CompilerInvocation>(*Invocation), Preamble, | 
 |       llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents), PCHs, Inputs.FS); | 
 | } | 
 |  | 
 | SourceLocation clangd::getBeginningOfIdentifier(ParsedAST &Unit, | 
 |                                                 const Position &Pos, | 
 |                                                 const FileID FID) { | 
 |   const ASTContext &AST = Unit.getASTContext(); | 
 |   const SourceManager &SourceMgr = AST.getSourceManager(); | 
 |   auto Offset = positionToOffset(SourceMgr.getBufferData(FID), Pos); | 
 |   if (!Offset) { | 
 |     log("getBeginningOfIdentifier: {0}", Offset.takeError()); | 
 |     return SourceLocation(); | 
 |   } | 
 |   SourceLocation InputLoc = SourceMgr.getComposedLoc(FID, *Offset); | 
 |  | 
 |   // GetBeginningOfToken(pos) is almost what we want, but does the wrong thing | 
 |   // if the cursor is at the end of the identifier. | 
 |   // Instead, we lex at GetBeginningOfToken(pos - 1). The cases are: | 
 |   //  1) at the beginning of an identifier, we'll be looking at something | 
 |   //  that isn't an identifier. | 
 |   //  2) at the middle or end of an identifier, we get the identifier. | 
 |   //  3) anywhere outside an identifier, we'll get some non-identifier thing. | 
 |   // We can't actually distinguish cases 1 and 3, but returning the original | 
 |   // location is correct for both! | 
 |   if (*Offset == 0) // Case 1 or 3. | 
 |     return SourceMgr.getMacroArgExpandedLocation(InputLoc); | 
 |   SourceLocation Before = | 
 |       SourceMgr.getMacroArgExpandedLocation(InputLoc.getLocWithOffset(-1)); | 
 |   Before = Lexer::GetBeginningOfToken(Before, SourceMgr, AST.getLangOpts()); | 
 |   Token Tok; | 
 |   if (Before.isValid() && | 
 |       !Lexer::getRawToken(Before, Tok, SourceMgr, AST.getLangOpts(), false) && | 
 |       Tok.is(tok::raw_identifier)) | 
 |     return Before;                                        // Case 2. | 
 |   return SourceMgr.getMacroArgExpandedLocation(InputLoc); // Case 1 or 3. | 
 | } |