llvm-project/clang-tools-extra/clangd/Preamble.h

181 lines
7.9 KiB
C++

//===--- Preamble.h - Reusing expensive parts of the AST ---------*- C++-*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// The vast majority of code in a typical translation unit is in the headers
// included at the top of the file.
//
// The preamble optimization says that we can parse this code once, and reuse
// the result multiple times. The preamble is invalidated by changes to the
// code in the preamble region, to the compile command, or to files on disk.
//
// This is the most important optimization in clangd: it allows operations like
// code-completion to have sub-second latency. It is supported by the
// PrecompiledPreamble functionality in clang, which wraps the techniques used
// by PCH files, modules etc into a convenient interface.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PREAMBLE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PREAMBLE_H
#include "CollectMacros.h"
#include "Compiler.h"
#include "Diagnostics.h"
#include "FS.h"
#include "Headers.h"
#include "index/CanonicalIncludes.h"
#include "support/Path.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PrecompiledPreamble.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/ADT/StringRef.h"
#include <memory>
#include <string>
#include <vector>
namespace clang {
namespace clangd {
/// The parsed preamble and associated data.
///
/// As we must avoid re-parsing the preamble, any information that can only
/// be obtained during parsing must be eagerly captured and stored here.
struct PreambleData {
PreambleData(PrecompiledPreamble Preamble) : Preamble(std::move(Preamble)) {}
// Version of the ParseInputs this preamble was built from.
std::string Version;
tooling::CompileCommand CompileCommand;
PrecompiledPreamble Preamble;
std::vector<Diag> Diags;
// Processes like code completions and go-to-definitions will need #include
// information, and their compile action skips preamble range.
IncludeStructure Includes;
// Macros defined in the preamble section of the main file.
// Users care about headers vs main-file, not preamble vs non-preamble.
// These should be treated as main-file entities e.g. for code completion.
MainFileMacros Macros;
// Pragma marks defined in the preamble section of the main file.
std::vector<PragmaMark> Marks;
// Cache of FS operations performed when building the preamble.
// When reusing a preamble, this cache can be consumed to save IO.
std::unique_ptr<PreambleFileStatusCache> StatCache;
CanonicalIncludes CanonIncludes;
// Whether there was a (possibly-incomplete) include-guard on the main file.
// We need to propagate this information "by hand" to subsequent parses.
bool MainIsIncludeGuarded = false;
};
using PreambleParsedCallback = std::function<void(ASTContext &, Preprocessor &,
const CanonicalIncludes &)>;
/// Timings and statistics from the premble build. Unlike PreambleData, these
/// do not need to be stored for later, but can be useful for logging, metrics,
/// etc.
struct PreambleBuildStats {
/// Total wall time it took to build preamble, in seconds.
double TotalBuildTime;
/// Time spent in filesystem operations during the build, in seconds.
double FileSystemTime;
/// Estimate of the memory used while building the preamble.
/// This memory has been released when buildPreamble returns.
/// For example, this includes the size of the in-memory AST (ASTContext).
size_t BuildSize;
/// The serialized size of the preamble.
/// This storage is needed while the preamble is used (but may be on disk).
size_t SerializedSize;
};
/// Build a preamble for the new inputs unless an old one can be reused.
/// If \p PreambleCallback is set, it will be run on top of the AST while
/// building the preamble.
/// If Stats is not non-null, build statistics will be exported there.
std::shared_ptr<const PreambleData>
buildPreamble(PathRef FileName, CompilerInvocation CI,
const ParseInputs &Inputs, bool StoreInMemory,
PreambleParsedCallback PreambleCallback,
PreambleBuildStats *Stats = nullptr);
/// Returns true if \p Preamble is reusable for \p Inputs. Note that it will
/// return true when some missing headers are now available.
/// FIXME: Should return more information about the delta between \p Preamble
/// and \p Inputs, e.g. new headers.
bool isPreambleCompatible(const PreambleData &Preamble,
const ParseInputs &Inputs, PathRef FileName,
const CompilerInvocation &CI);
/// Stores information required to parse a TU using a (possibly stale) Baseline
/// preamble. Later on this information can be injected into the main file by
/// updating compiler invocation with \c apply. This injected section
/// approximately reflects additions to the preamble in Modified contents, e.g.
/// new include directives.
class PreamblePatch {
public:
enum class PatchType { MacroDirectives, All };
/// \p Preamble is used verbatim.
static PreamblePatch unmodified(const PreambleData &Preamble);
/// Builds a patch that contains new PP directives introduced to the preamble
/// section of \p Modified compared to \p Baseline.
/// FIXME: This only handles include directives, we should at least handle
/// define/undef.
static PreamblePatch createFullPatch(llvm::StringRef FileName,
const ParseInputs &Modified,
const PreambleData &Baseline);
static PreamblePatch createMacroPatch(llvm::StringRef FileName,
const ParseInputs &Modified,
const PreambleData &Baseline);
/// Adjusts CI (which compiles the modified inputs) to be used with the
/// baseline preamble. This is done by inserting an artifical include to the
/// \p CI that contains new directives calculated in create.
void apply(CompilerInvocation &CI) const;
/// Returns #include directives from the \c Modified preamble that were
/// resolved using the \c Baseline preamble. This covers the new locations of
/// inclusions that were moved around, but not inclusions of new files. Those
/// will be recorded when parsing the main file: the includes in the injected
/// section will be resolved back to their spelled positions in the main file
/// using the presumed-location mechanism.
std::vector<Inclusion> preambleIncludes() const;
/// Returns preamble bounds for the Modified.
PreambleBounds modifiedBounds() const { return ModifiedBounds; }
/// Returns textual patch contents.
llvm::StringRef text() const { return PatchContents; }
/// Whether diagnostics generated using this patch are trustable.
bool preserveDiagnostics() const { return PatchContents.empty(); }
private:
static PreamblePatch create(llvm::StringRef FileName,
const ParseInputs &Modified,
const PreambleData &Baseline,
PatchType PatchType);
PreamblePatch() = default;
std::string PatchContents;
std::string PatchFileName;
/// Includes that are present in both \p Baseline and \p Modified. Used for
/// patching includes of baseline preamble.
std::vector<Inclusion> PreambleIncludes;
PreambleBounds ModifiedBounds = {0, false};
};
/// Translates locations inside preamble patch to their main-file equivalent
/// using presumed locations. Returns \p Loc if it isn't inside preamble patch.
SourceLocation translatePreamblePatchLocation(SourceLocation Loc,
const SourceManager &SM);
} // namespace clangd
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_PREAMBLE_H