summaryrefslogtreecommitdiffstats
path: root/clangd/ClangdUnit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clangd/ClangdUnit.cpp')
-rw-r--r--clangd/ClangdUnit.cpp197
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,