diff options
Diffstat (limited to 'clangd/ClangdUnit.cpp')
-rw-r--r-- | clangd/ClangdUnit.cpp | 197 |
1 files changed, 134 insertions, 63 deletions
diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp index 5add1179..c8c41330 100644 --- a/clangd/ClangdUnit.cpp +++ b/clangd/ClangdUnit.cpp @@ -1,9 +1,8 @@ //===--- ClangdUnit.cpp ------------------------------------------*- C++-*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -12,11 +11,17 @@ #include "../clang-tidy/ClangTidyModuleRegistry.h" #include "Compiler.h" #include "Diagnostics.h" +#include "Headers.h" +#include "IncludeFixer.h" #include "Logger.h" #include "SourceCode.h" #include "Trace.h" +#include "index/CanonicalIncludes.h" +#include "index/Index.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" @@ -29,12 +34,15 @@ #include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/Sema.h" #include "clang/Serialization/ASTWriter.h" +#include "clang/Serialization/PCHContainerOperations.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> +#include <memory> namespace clang { namespace clangd { @@ -88,18 +96,55 @@ private: std::vector<Decl *> TopLevelDecls; }; +class CollectMainFileMacros : public PPCallbacks { +public: + explicit CollectMainFileMacros(const SourceManager &SM, + std::vector<std::string> *Out) + : SM(SM), Out(Out) {} + + void FileChanged(SourceLocation Loc, FileChangeReason, + SrcMgr::CharacteristicKind, FileID Prev) { + InMainFile = SM.isWrittenInMainFile(Loc); + } + + void MacroDefined(const Token &MacroName, const MacroDirective *MD) { + if (InMainFile) + MainFileMacros.insert(MacroName.getIdentifierInfo()->getName()); + } + + void EndOfMainFile() { + for (const auto& Entry : MainFileMacros) + Out->push_back(Entry.getKey()); + llvm::sort(*Out); + } + + private: + const SourceManager &SM; + bool InMainFile = true; + llvm::StringSet<> MainFileMacros; + std::vector<std::string> *Out; +}; + class CppFilePreambleCallbacks : public PreambleCallbacks { public: CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback) - : File(File), ParsedCallback(ParsedCallback) {} + : File(File), ParsedCallback(ParsedCallback) { + addSystemHeadersMapping(&CanonIncludes); + } IncludeStructure takeIncludes() { return std::move(Includes); } + std::vector<std::string> takeMainFileMacros() { + return std::move(MainFileMacros); + } + + CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); } + void AfterExecute(CompilerInstance &CI) override { if (!ParsedCallback) return; trace::Span Tracer("Running PreambleCallback"); - ParsedCallback(CI.getASTContext(), CI.getPreprocessorPtr()); + ParsedCallback(CI.getASTContext(), CI.getPreprocessorPtr(), CanonIncludes); } void BeforeExecute(CompilerInstance &CI) override { @@ -108,13 +153,23 @@ public: std::unique_ptr<PPCallbacks> createPPCallbacks() override { assert(SourceMgr && "SourceMgr must be set at this point"); - return collectIncludeStructureCallback(*SourceMgr, &Includes); + return llvm::make_unique<PPChainedCallbacks>( + collectIncludeStructureCallback(*SourceMgr, &Includes), + llvm::make_unique<CollectMainFileMacros>(*SourceMgr, &MainFileMacros)); + } + + CommentHandler *getCommentHandler() override { + IWYUHandler = collectIWYUHeaderMaps(&CanonIncludes); + return IWYUHandler.get(); } private: PathRef File; PreambleParsedCallback ParsedCallback; IncludeStructure Includes; + CanonicalIncludes CanonIncludes; + std::vector<std::string> MainFileMacros; + std::unique_ptr<CommentHandler> IWYUHandler = nullptr; SourceManager *SourceMgr = nullptr; }; @@ -133,6 +188,9 @@ public: CompilerInstance &Clang) { auto &PP = Clang.getPreprocessor(); auto *ExistingCallbacks = PP.getPPCallbacks(); + // No need to replay events if nobody is listening. + if (!ExistingCallbacks) + return; PP.addPPCallbacks(std::unique_ptr<PPCallbacks>( new ReplayPreamble(Includes, ExistingCallbacks, Clang.getSourceManager(), PP, Clang.getLangOpts()))); @@ -227,8 +285,8 @@ llvm::Optional<ParsedAST> ParsedAST::build(std::unique_ptr<CompilerInvocation> CI, std::shared_ptr<const PreambleData> Preamble, std::unique_ptr<llvm::MemoryBuffer> Buffer, - std::shared_ptr<PCHContainerOperations> PCHs, - llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + const SymbolIndex *Index, const ParseOptions &Opts) { assert(CI); // Command-line parsing sets DisableFree to true by default, but we don't want // to leak memory in clangd. @@ -237,9 +295,10 @@ ParsedAST::build(std::unique_ptr<CompilerInvocation> CI, Preamble ? &Preamble->Preamble : nullptr; StoreDiags ASTDiags; - auto Clang = - prepareCompilerInstance(std::move(CI), PreamblePCH, std::move(Buffer), - std::move(PCHs), std::move(VFS), ASTDiags); + std::string Content = Buffer->getBuffer(); + + auto Clang = prepareCompilerInstance(std::move(CI), PreamblePCH, + std::move(Buffer), VFS, ASTDiags); if (!Clang) return None; @@ -262,30 +321,48 @@ ParsedAST::build(std::unique_ptr<CompilerInvocation> CI, llvm::Optional<tidy::ClangTidyContext> CTContext; { trace::Span Tracer("ClangTidyInit"); + dlog("ClangTidy configuration for file {0}: {1}", MainInput.getFile(), + tidy::configurationAsText(Opts.ClangTidyOpts)); tidy::ClangTidyCheckFactories CTFactories; for (const auto &E : tidy::ClangTidyModuleRegistry::entries()) E.instantiate()->addCheckFactories(CTFactories); - auto CTOpts = tidy::ClangTidyOptions::getDefaults(); - // FIXME: this needs to be configurable, and we need to support .clang-tidy - // files and other options providers. - // These checks exercise the matcher- and preprocessor-based hooks. - CTOpts.Checks = "bugprone-sizeof-expression," - "bugprone-macro-repeated-side-effects," - "modernize-deprecated-headers"; CTContext.emplace(llvm::make_unique<tidy::DefaultOptionsProvider>( - tidy::ClangTidyGlobalOptions(), CTOpts)); + tidy::ClangTidyGlobalOptions(), Opts.ClangTidyOpts)); CTContext->setDiagnosticsEngine(&Clang->getDiagnostics()); CTContext->setASTContext(&Clang->getASTContext()); CTContext->setCurrentFile(MainInput.getFile()); CTFactories.createChecks(CTContext.getPointer(), CTChecks); + Preprocessor *PP = &Clang->getPreprocessor(); for (const auto &Check : CTChecks) { // FIXME: the PP callbacks skip the entire preamble. // Checks that want to see #includes in the main file do not see them. - Check->registerPPCallbacks(*Clang); + Check->registerPPCallbacks(Clang->getSourceManager(), PP, PP); Check->registerMatchers(&CTFinder); } } + // Add IncludeFixer which can recorver diagnostics caused by missing includes + // (e.g. incomplete type) and attach include insertion fixes to diagnostics. + llvm::Optional<IncludeFixer> FixIncludes; + auto BuildDir = VFS->getCurrentWorkingDirectory(); + if (Opts.SuggestMissingIncludes && Index && !BuildDir.getError()) { + auto Style = getFormatStyleForFile(MainInput.getFile(), Content, VFS.get()); + auto Inserter = std::make_shared<IncludeInserter>( + MainInput.getFile(), Content, Style, BuildDir.get(), + &Clang->getPreprocessor().getHeaderSearchInfo()); + if (Preamble) { + for (const auto &Inc : Preamble->Includes.MainFileIncludes) + Inserter->addExisting(Inc); + } + FixIncludes.emplace(MainInput.getFile(), Inserter, *Index, + /*IndexRequestLimit=*/5); + ASTDiags.contributeFixes([&FixIncludes](DiagnosticsEngine::Level DiagLevl, + const clang::Diagnostic &Info) { + return FixIncludes->fix(DiagLevl, Info); + }); + Clang->setExternalSemaSource(FixIncludes->unresolvedNameRecorder()); + } + // Copy over the includes from the preamble, then combine with the // non-preamble includes below. auto Includes = Preamble ? Preamble->Includes : IncludeStructure{}; @@ -298,6 +375,17 @@ ParsedAST::build(std::unique_ptr<CompilerInvocation> CI, Clang->getPreprocessor().addPPCallbacks( collectIncludeStructureCallback(Clang->getSourceManager(), &Includes)); + // Copy over the includes from the preamble, then combine with the + // non-preamble includes below. + CanonicalIncludes CanonIncludes; + if (Preamble) + CanonIncludes = Preamble->CanonIncludes; + else + addSystemHeadersMapping(&CanonIncludes); + std::unique_ptr<CommentHandler> IWYUHandler = + collectIWYUHeaderMaps(&CanonIncludes); + Clang->getPreprocessor().addCommentHandler(IWYUHandler.get()); + if (!Action->Execute()) log("Execute() failed when building AST for {0}", MainInput.getFile()); @@ -321,13 +409,13 @@ ParsedAST::build(std::unique_ptr<CompilerInvocation> CI, // So just inform the preprocessor of EOF, while keeping everything alive. Clang->getPreprocessor().EndSourceFile(); - std::vector<Diag> Diags = ASTDiags.take(); + std::vector<Diag> Diags = ASTDiags.take(CTContext.getPointer()); // 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)); + std::move(Includes), std::move(CanonIncludes)); } ParsedAST::ParsedAST(ParsedAST &&Other) = default; @@ -403,59 +491,39 @@ const IncludeStructure &ParsedAST::getIncludeStructure() const { return Includes; } +const CanonicalIncludes &ParsedAST::getCanonicalIncludes() const { + return CanonIncludes; +} + PreambleData::PreambleData(PrecompiledPreamble Preamble, std::vector<Diag> Diags, IncludeStructure Includes, - std::unique_ptr<PreambleFileStatusCache> StatCache) + std::vector<std::string> MainFileMacros, + std::unique_ptr<PreambleFileStatusCache> StatCache, + CanonicalIncludes CanonIncludes) : Preamble(std::move(Preamble)), Diags(std::move(Diags)), - Includes(std::move(Includes)), StatCache(std::move(StatCache)) {} + Includes(std::move(Includes)), MainFileMacros(std::move(MainFileMacros)), + StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) { +} 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) + std::vector<Diag> Diags, IncludeStructure Includes, + CanonicalIncludes CanonIncludes) : Preamble(std::move(Preamble)), Clang(std::move(Clang)), Action(std::move(Action)), Diags(std::move(Diags)), LocalTopLevelDecls(std::move(LocalTopLevelDecls)), - Includes(std::move(Includes)) { + Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) { assert(this->Clang); assert(this->Action); } -std::unique_ptr<CompilerInvocation> -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; - llvm::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> 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, + const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback) { // Note that we don't need to copy the input contents, preamble can live // without those. @@ -500,7 +568,8 @@ buildPreamble(PathRef FileName, CompilerInvocation &CI, auto StatCache = llvm::make_unique<PreambleFileStatusCache>(AbsFileName); auto BuiltPreamble = PrecompiledPreamble::Build( CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, - StatCache->getProducingFS(Inputs.FS), PCHs, StoreInMemory, + StatCache->getProducingFS(Inputs.FS), + std::make_shared<PCHContainerOperations>(), StoreInMemory, SerializedDeclsCollector); // When building the AST for the main file, we do want the function @@ -510,9 +579,12 @@ buildPreamble(PathRef FileName, CompilerInvocation &CI, if (BuiltPreamble) { vlog("Built preamble of size {0} for file {1}", BuiltPreamble->getSize(), FileName); + std::vector<Diag> Diags = PreambleDiagnostics.take(); return std::make_shared<PreambleData>( - std::move(*BuiltPreamble), PreambleDiagnostics.take(), - SerializedDeclsCollector.takeIncludes(), std::move(StatCache)); + std::move(*BuiltPreamble), std::move(Diags), + SerializedDeclsCollector.takeIncludes(), + SerializedDeclsCollector.takeMainFileMacros(), std::move(StatCache), + SerializedDeclsCollector.takeCanonicalIncludes()); } else { elog("Could not build a preamble for file {0}", FileName); return nullptr; @@ -522,8 +594,7 @@ buildPreamble(PathRef FileName, CompilerInvocation &CI, llvm::Optional<ParsedAST> buildAST(PathRef FileName, std::unique_ptr<CompilerInvocation> Invocation, const ParseInputs &Inputs, - std::shared_ptr<const PreambleData> Preamble, - std::shared_ptr<PCHContainerOperations> PCHs) { + std::shared_ptr<const PreambleData> Preamble) { trace::Span Tracer("BuildAST"); SPAN_ATTACH(Tracer, "File", FileName); @@ -539,7 +610,7 @@ buildAST(PathRef FileName, std::unique_ptr<CompilerInvocation> Invocation, return ParsedAST::build(llvm::make_unique<CompilerInvocation>(*Invocation), Preamble, llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents), - PCHs, std::move(VFS)); + std::move(VFS), Inputs.Index, Inputs.Opts); } SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos, |