2019-09-04 15:35:00 +08:00
|
|
|
//===--- Preamble.cpp - Reusing expensive parts of the AST ----------------===//
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "Preamble.h"
|
2020-03-11 23:34:01 +08:00
|
|
|
#include "Compiler.h"
|
2019-09-04 15:35:00 +08:00
|
|
|
#include "Logger.h"
|
|
|
|
#include "Trace.h"
|
|
|
|
#include "clang/Basic/SourceLocation.h"
|
|
|
|
#include "clang/Lex/PPCallbacks.h"
|
|
|
|
#include "clang/Lex/PreprocessorOptions.h"
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
class CppFilePreambleCallbacks : public PreambleCallbacks {
|
|
|
|
public:
|
|
|
|
CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback)
|
2019-09-24 19:14:06 +08:00
|
|
|
: File(File), ParsedCallback(ParsedCallback) {}
|
2019-09-04 15:35:00 +08:00
|
|
|
|
|
|
|
IncludeStructure takeIncludes() { return std::move(Includes); }
|
|
|
|
|
2019-09-24 19:14:06 +08:00
|
|
|
MainFileMacros takeMacros() { return std::move(Macros); }
|
2019-09-04 15:35:00 +08:00
|
|
|
|
|
|
|
CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); }
|
|
|
|
|
|
|
|
void AfterExecute(CompilerInstance &CI) override {
|
|
|
|
if (!ParsedCallback)
|
|
|
|
return;
|
|
|
|
trace::Span Tracer("Running PreambleCallback");
|
|
|
|
ParsedCallback(CI.getASTContext(), CI.getPreprocessorPtr(), CanonIncludes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BeforeExecute(CompilerInstance &CI) override {
|
2019-09-09 23:32:51 +08:00
|
|
|
CanonIncludes.addSystemHeadersMapping(CI.getLangOpts());
|
2019-09-24 19:14:06 +08:00
|
|
|
LangOpts = &CI.getLangOpts();
|
2019-09-04 15:35:00 +08:00
|
|
|
SourceMgr = &CI.getSourceManager();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<PPCallbacks> createPPCallbacks() override {
|
2019-09-24 19:14:06 +08:00
|
|
|
assert(SourceMgr && LangOpts &&
|
|
|
|
"SourceMgr and LangOpts must be set at this point");
|
|
|
|
|
2019-09-04 15:35:00 +08:00
|
|
|
return std::make_unique<PPChainedCallbacks>(
|
|
|
|
collectIncludeStructureCallback(*SourceMgr, &Includes),
|
2020-03-01 23:05:12 +08:00
|
|
|
std::make_unique<CollectMainFileMacros>(*SourceMgr, Macros));
|
2019-09-04 15:35:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
CommentHandler *getCommentHandler() override {
|
|
|
|
IWYUHandler = collectIWYUHeaderMaps(&CanonIncludes);
|
|
|
|
return IWYUHandler.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
PathRef File;
|
|
|
|
PreambleParsedCallback ParsedCallback;
|
|
|
|
IncludeStructure Includes;
|
|
|
|
CanonicalIncludes CanonIncludes;
|
2019-09-24 19:14:06 +08:00
|
|
|
MainFileMacros Macros;
|
2019-09-04 15:35:00 +08:00
|
|
|
std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
|
2019-09-24 19:14:06 +08:00
|
|
|
const clang::LangOptions *LangOpts = nullptr;
|
|
|
|
const SourceManager *SourceMgr = nullptr;
|
2019-09-04 15:35:00 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2020-03-11 23:34:01 +08:00
|
|
|
PreambleData::PreambleData(const ParseInputs &Inputs,
|
[clangd] Track document versions, include them with diags, enhance logs
Summary:
This ties to an LSP feature (diagnostic versioning) but really a lot
of the value is in being able to log what's happening with file versions
and queues more descriptively and clearly.
As such it's fairly invasive, for a logging patch :-\
Key decisions:
- at the LSP layer, we don't reqire the client to provide versions (LSP
makes it mandatory but we never enforced it). If not provided,
versions start at 0 and increment. DraftStore handles this.
- don't propagate magically using contexts, but rather manually:
addDocument -> ParseInputs -> (ParsedAST, Preamble, various callbacks)
Context-propagation would hide the versions from ClangdServer, which
would make producing good log messages hard
- within ClangdServer, treat versions as opaque and unordered.
std::string is a convenient type for this, and allows richer versions
for embedders. They're "mandatory" but "null" is a reasonable default.
Subscribers: ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D75582
2020-03-04 07:33:29 +08:00
|
|
|
PrecompiledPreamble Preamble,
|
2019-09-04 15:35:00 +08:00
|
|
|
std::vector<Diag> Diags, IncludeStructure Includes,
|
2019-09-24 19:14:06 +08:00
|
|
|
MainFileMacros Macros,
|
2019-09-04 15:35:00 +08:00
|
|
|
std::unique_ptr<PreambleFileStatusCache> StatCache,
|
|
|
|
CanonicalIncludes CanonIncludes)
|
2020-03-11 23:34:01 +08:00
|
|
|
: Version(Inputs.Version), CompileCommand(Inputs.CompileCommand),
|
|
|
|
Preamble(std::move(Preamble)), Diags(std::move(Diags)),
|
2019-09-24 19:14:06 +08:00
|
|
|
Includes(std::move(Includes)), Macros(std::move(Macros)),
|
2019-09-04 15:35:00 +08:00
|
|
|
StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<const PreambleData>
|
2020-03-13 18:52:19 +08:00
|
|
|
buildPreamble(PathRef FileName, CompilerInvocation CI,
|
2019-09-04 15:35:00 +08:00
|
|
|
const ParseInputs &Inputs, 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, FileName);
|
|
|
|
auto Bounds =
|
|
|
|
ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
|
|
|
|
|
|
|
|
trace::Span Tracer("BuildPreamble");
|
|
|
|
SPAN_ATTACH(Tracer, "File", FileName);
|
|
|
|
StoreDiags PreambleDiagnostics;
|
|
|
|
llvm::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;
|
|
|
|
|
2020-03-31 22:09:49 +08:00
|
|
|
// Recovery expression currently only works for C++.
|
|
|
|
if (CI.getLangOpts()->CPlusPlus)
|
|
|
|
CI.getLangOpts()->RecoveryAST = Inputs.Opts.BuildRecoveryAST;
|
|
|
|
|
2019-09-04 15:35:00 +08:00
|
|
|
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.
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::SmallString<32> AbsFileName(FileName);
|
|
|
|
Inputs.FS->makeAbsolute(AbsFileName);
|
|
|
|
auto StatCache = std::make_unique<PreambleFileStatusCache>(AbsFileName);
|
|
|
|
auto BuiltPreamble = PrecompiledPreamble::Build(
|
|
|
|
CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine,
|
|
|
|
StatCache->getProducingFS(Inputs.FS),
|
|
|
|
std::make_shared<PCHContainerOperations>(), StoreInMemory,
|
|
|
|
SerializedDeclsCollector);
|
|
|
|
|
|
|
|
// When building the AST for the main file, we do want the function
|
|
|
|
// bodies.
|
|
|
|
CI.getFrontendOpts().SkipFunctionBodies = false;
|
|
|
|
|
|
|
|
if (BuiltPreamble) {
|
[clangd] Track document versions, include them with diags, enhance logs
Summary:
This ties to an LSP feature (diagnostic versioning) but really a lot
of the value is in being able to log what's happening with file versions
and queues more descriptively and clearly.
As such it's fairly invasive, for a logging patch :-\
Key decisions:
- at the LSP layer, we don't reqire the client to provide versions (LSP
makes it mandatory but we never enforced it). If not provided,
versions start at 0 and increment. DraftStore handles this.
- don't propagate magically using contexts, but rather manually:
addDocument -> ParseInputs -> (ParsedAST, Preamble, various callbacks)
Context-propagation would hide the versions from ClangdServer, which
would make producing good log messages hard
- within ClangdServer, treat versions as opaque and unordered.
std::string is a convenient type for this, and allows richer versions
for embedders. They're "mandatory" but "null" is a reasonable default.
Subscribers: ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D75582
2020-03-04 07:33:29 +08:00
|
|
|
vlog("Built preamble of size {0} for file {1} version {2}",
|
|
|
|
BuiltPreamble->getSize(), FileName, Inputs.Version);
|
2019-09-04 15:35:00 +08:00
|
|
|
std::vector<Diag> Diags = PreambleDiagnostics.take();
|
|
|
|
return std::make_shared<PreambleData>(
|
2020-03-11 23:34:01 +08:00
|
|
|
Inputs, std::move(*BuiltPreamble), std::move(Diags),
|
2019-09-04 15:35:00 +08:00
|
|
|
SerializedDeclsCollector.takeIncludes(),
|
2019-09-24 19:14:06 +08:00
|
|
|
SerializedDeclsCollector.takeMacros(), std::move(StatCache),
|
2019-09-04 15:35:00 +08:00
|
|
|
SerializedDeclsCollector.takeCanonicalIncludes());
|
|
|
|
} else {
|
2020-03-19 22:09:28 +08:00
|
|
|
elog("Could not build a preamble for file {0} version {1}", FileName,
|
[clangd] Track document versions, include them with diags, enhance logs
Summary:
This ties to an LSP feature (diagnostic versioning) but really a lot
of the value is in being able to log what's happening with file versions
and queues more descriptively and clearly.
As such it's fairly invasive, for a logging patch :-\
Key decisions:
- at the LSP layer, we don't reqire the client to provide versions (LSP
makes it mandatory but we never enforced it). If not provided,
versions start at 0 and increment. DraftStore handles this.
- don't propagate magically using contexts, but rather manually:
addDocument -> ParseInputs -> (ParsedAST, Preamble, various callbacks)
Context-propagation would hide the versions from ClangdServer, which
would make producing good log messages hard
- within ClangdServer, treat versions as opaque and unordered.
std::string is a convenient type for this, and allows richer versions
for embedders. They're "mandatory" but "null" is a reasonable default.
Subscribers: ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D75582
2020-03-04 07:33:29 +08:00
|
|
|
Inputs.Version);
|
2019-09-04 15:35:00 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-16 04:43:00 +08:00
|
|
|
bool isPreambleCompatible(const PreambleData &Preamble,
|
|
|
|
const ParseInputs &Inputs, PathRef FileName,
|
|
|
|
const CompilerInvocation &CI) {
|
|
|
|
auto ContentsBuffer =
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
|
|
|
|
auto Bounds =
|
|
|
|
ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
|
|
|
|
return compileCommandsAreEqual(Inputs.CompileCommand,
|
|
|
|
Preamble.CompileCommand) &&
|
|
|
|
Preamble.Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
|
|
|
|
Inputs.FS.get());
|
|
|
|
}
|
2019-09-04 15:35:00 +08:00
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|