mirror of
https://github.com/intel/llvm.git
synced 2026-01-26 12:26:52 +08:00
[clangd] Update symbol collector to use include-cleaner.
Differential Revision: https://reviews.llvm.org/D152900
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
#include "SourceCode.h"
|
||||
#include "TUScheduler.h"
|
||||
#include "XRefs.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/FileIndex.h"
|
||||
#include "index/Merge.h"
|
||||
#include "index/StdLib.h"
|
||||
@@ -53,6 +53,7 @@
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
@@ -69,13 +70,13 @@ struct UpdateIndexCallbacks : public ParsingCallbacks {
|
||||
const ThreadsafeFS &TFS, AsyncTaskRunner *Tasks,
|
||||
bool CollectInactiveRegions,
|
||||
const ClangdServer::Options &Opts)
|
||||
: FIndex(FIndex), ServerCallbacks(ServerCallbacks),
|
||||
TFS(TFS), Stdlib{std::make_shared<StdLibSet>()}, Tasks(Tasks),
|
||||
: FIndex(FIndex), ServerCallbacks(ServerCallbacks), TFS(TFS),
|
||||
Stdlib{std::make_shared<StdLibSet>()}, Tasks(Tasks),
|
||||
CollectInactiveRegions(CollectInactiveRegions), Opts(Opts) {}
|
||||
|
||||
void onPreambleAST(
|
||||
PathRef Path, llvm::StringRef Version, CapturedASTCtx ASTCtx,
|
||||
const std::shared_ptr<const CanonicalIncludes> CanonIncludes) override {
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes> PI) override {
|
||||
|
||||
if (!FIndex)
|
||||
return;
|
||||
@@ -87,11 +88,10 @@ struct UpdateIndexCallbacks : public ParsingCallbacks {
|
||||
|
||||
// FIndex outlives the UpdateIndexCallbacks.
|
||||
auto Task = [FIndex(FIndex), Path(Path.str()), Version(Version.str()),
|
||||
ASTCtx(std::move(ASTCtx)),
|
||||
CanonIncludes(CanonIncludes)]() mutable {
|
||||
ASTCtx(std::move(ASTCtx)), PI(std::move(PI))]() mutable {
|
||||
trace::Span Tracer("PreambleIndexing");
|
||||
FIndex->updatePreamble(Path, Version, ASTCtx.getASTContext(),
|
||||
ASTCtx.getPreprocessor(), *CanonIncludes);
|
||||
ASTCtx.getPreprocessor(), *PI);
|
||||
};
|
||||
|
||||
if (Opts.AsyncPreambleIndexing && Tasks) {
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "clang-include-cleaner/IncludeSpeller.h"
|
||||
#include "clang-include-cleaner/Types.h"
|
||||
#include "index/SymbolCollector.h"
|
||||
#include "support/Logger.h"
|
||||
#include "support/Markup.h"
|
||||
#include "support/Trace.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
@@ -1190,7 +1189,7 @@ void maybeAddSymbolProviders(ParsedAST &AST, HoverInfo &HI,
|
||||
|
||||
const SourceManager &SM = AST.getSourceManager();
|
||||
llvm::SmallVector<include_cleaner::Header> RankedProviders =
|
||||
include_cleaner::headersForSymbol(Sym, SM, AST.getPragmaIncludes());
|
||||
include_cleaner::headersForSymbol(Sym, SM, AST.getPragmaIncludes().get());
|
||||
if (RankedProviders.empty())
|
||||
return;
|
||||
|
||||
@@ -1254,7 +1253,7 @@ void maybeAddUsedSymbols(ParsedAST &AST, HoverInfo &HI, const Inclusion &Inc) {
|
||||
llvm::DenseSet<include_cleaner::Symbol> UsedSymbols;
|
||||
include_cleaner::walkUsed(
|
||||
AST.getLocalTopLevelDecls(), collectMacroReferences(AST),
|
||||
AST.getPragmaIncludes(), SM,
|
||||
AST.getPragmaIncludes().get(), SM,
|
||||
[&](const include_cleaner::SymbolReference &Ref,
|
||||
llvm::ArrayRef<include_cleaner::Header> Providers) {
|
||||
if (Ref.RT != include_cleaner::RefType::Explicit ||
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "llvm/Support/Regex.h"
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -71,8 +72,9 @@ bool isIgnored(llvm::StringRef HeaderPath, HeaderFilter IgnoreHeaders) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST,
|
||||
const include_cleaner::PragmaIncludes *PI) {
|
||||
bool mayConsiderUnused(
|
||||
const Inclusion &Inc, ParsedAST &AST,
|
||||
const include_cleaner::PragmaIncludes *PI) {
|
||||
// FIXME(kirillbobyrev): We currently do not support the umbrella headers.
|
||||
// System headers are likely to be standard library headers.
|
||||
// Until we have good support for umbrella headers, don't warn about them.
|
||||
@@ -312,7 +314,7 @@ getUnused(ParsedAST &AST,
|
||||
auto IncludeID = static_cast<IncludeStructure::HeaderID>(*MFI.HeaderID);
|
||||
if (ReferencedFiles.contains(IncludeID))
|
||||
continue;
|
||||
if (!mayConsiderUnused(MFI, AST, AST.getPragmaIncludes())) {
|
||||
if (!mayConsiderUnused(MFI, AST, AST.getPragmaIncludes().get())) {
|
||||
dlog("{0} was not used, but is not eligible to be diagnosed as unused",
|
||||
MFI.Written);
|
||||
continue;
|
||||
@@ -395,7 +397,7 @@ IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST) {
|
||||
trace::Span Tracer("include_cleaner::walkUsed");
|
||||
include_cleaner::walkUsed(
|
||||
AST.getLocalTopLevelDecls(), /*MacroRefs=*/Macros,
|
||||
AST.getPragmaIncludes(), SM,
|
||||
AST.getPragmaIncludes().get(), SM,
|
||||
[&](const include_cleaner::SymbolReference &Ref,
|
||||
llvm::ArrayRef<include_cleaner::Header> Providers) {
|
||||
bool Satisfied = false;
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "SourceCode.h"
|
||||
#include "TidyProvider.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "index/Symbol.h"
|
||||
#include "support/Logger.h"
|
||||
#include "support/Path.h"
|
||||
@@ -655,16 +654,8 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs,
|
||||
PP.addPPCallbacks(
|
||||
collectPragmaMarksCallback(Clang->getSourceManager(), Marks));
|
||||
|
||||
// Copy over the includes from the preamble, then combine with the
|
||||
// non-preamble includes below.
|
||||
CanonicalIncludes CanonIncludes;
|
||||
if (Preamble)
|
||||
CanonIncludes = *Preamble->CanonIncludes;
|
||||
else
|
||||
CanonIncludes.addSystemHeadersMapping(Clang->getLangOpts());
|
||||
std::unique_ptr<CommentHandler> IWYUHandler =
|
||||
collectIWYUHeaderMaps(&CanonIncludes);
|
||||
PP.addCommentHandler(IWYUHandler.get());
|
||||
// FIXME: Attach a comment handler to take care of
|
||||
// keep/export/no_include etc. IWYU pragmas.
|
||||
|
||||
// Collect tokens of the main file.
|
||||
syntax::TokenCollector CollectTokens(PP);
|
||||
@@ -719,8 +710,7 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs,
|
||||
ParsedAST Result(Filename, Inputs.Version, std::move(Preamble),
|
||||
std::move(Clang), std::move(Action), std::move(Tokens),
|
||||
std::move(Macros), std::move(Marks), std::move(ParsedDecls),
|
||||
std::move(Diags), std::move(Includes),
|
||||
std::move(CanonIncludes));
|
||||
std::move(Diags), std::move(Includes));
|
||||
llvm::move(getIncludeCleanerDiags(Result, Inputs.Contents),
|
||||
std::back_inserter(Result.Diags));
|
||||
return std::move(Result);
|
||||
@@ -806,10 +796,6 @@ const IncludeStructure &ParsedAST::getIncludeStructure() const {
|
||||
return Includes;
|
||||
}
|
||||
|
||||
const CanonicalIncludes &ParsedAST::getCanonicalIncludes() const {
|
||||
return CanonIncludes;
|
||||
}
|
||||
|
||||
ParsedAST::ParsedAST(PathRef TUPath, llvm::StringRef Version,
|
||||
std::shared_ptr<const PreambleData> Preamble,
|
||||
std::unique_ptr<CompilerInstance> Clang,
|
||||
@@ -817,23 +803,23 @@ ParsedAST::ParsedAST(PathRef TUPath, llvm::StringRef Version,
|
||||
syntax::TokenBuffer Tokens, MainFileMacros Macros,
|
||||
std::vector<PragmaMark> Marks,
|
||||
std::vector<Decl *> LocalTopLevelDecls,
|
||||
std::vector<Diag> Diags, IncludeStructure Includes,
|
||||
CanonicalIncludes CanonIncludes)
|
||||
std::vector<Diag> Diags, IncludeStructure Includes)
|
||||
: TUPath(TUPath), Version(Version), Preamble(std::move(Preamble)),
|
||||
Clang(std::move(Clang)), Action(std::move(Action)),
|
||||
Tokens(std::move(Tokens)), Macros(std::move(Macros)),
|
||||
Marks(std::move(Marks)), Diags(std::move(Diags)),
|
||||
LocalTopLevelDecls(std::move(LocalTopLevelDecls)),
|
||||
Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) {
|
||||
Includes(std::move(Includes)) {
|
||||
Resolver = std::make_unique<HeuristicResolver>(getASTContext());
|
||||
assert(this->Clang);
|
||||
assert(this->Action);
|
||||
}
|
||||
|
||||
const include_cleaner::PragmaIncludes *ParsedAST::getPragmaIncludes() const {
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes>
|
||||
ParsedAST::getPragmaIncludes() const {
|
||||
if (!Preamble)
|
||||
return nullptr;
|
||||
return &Preamble->Pragmas;
|
||||
return Preamble->Pragmas;
|
||||
}
|
||||
|
||||
std::optional<llvm::StringRef> ParsedAST::preambleVersion() const {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "Headers.h"
|
||||
#include "Preamble.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "support/Path.h"
|
||||
#include "clang/Frontend/FrontendAction.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
@@ -95,7 +94,6 @@ public:
|
||||
/// bytes. Does not include the size of the preamble.
|
||||
std::size_t getUsedBytes() const;
|
||||
const IncludeStructure &getIncludeStructure() const;
|
||||
const CanonicalIncludes &getCanonicalIncludes() const;
|
||||
|
||||
/// Gets all macro references (definition, expansions) present in the main
|
||||
/// file, including those in the preamble region.
|
||||
@@ -107,7 +105,8 @@ public:
|
||||
const syntax::TokenBuffer &getTokens() const { return Tokens; }
|
||||
/// Returns the PramaIncludes from the preamble.
|
||||
/// Might be null if AST is built without a preamble.
|
||||
const include_cleaner::PragmaIncludes *getPragmaIncludes() const;
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes>
|
||||
getPragmaIncludes() const;
|
||||
|
||||
/// Returns the version of the ParseInputs this AST was built from.
|
||||
llvm::StringRef version() const { return Version; }
|
||||
@@ -130,8 +129,7 @@ private:
|
||||
std::unique_ptr<FrontendAction> Action, syntax::TokenBuffer Tokens,
|
||||
MainFileMacros Macros, std::vector<PragmaMark> Marks,
|
||||
std::vector<Decl *> LocalTopLevelDecls, std::vector<Diag> Diags,
|
||||
IncludeStructure Includes, CanonicalIncludes CanonIncludes);
|
||||
|
||||
IncludeStructure Includes);
|
||||
Path TUPath;
|
||||
std::string Version;
|
||||
// In-memory preambles must outlive the AST, it is important that this member
|
||||
@@ -161,7 +159,6 @@ private:
|
||||
// top-level decls from the preamble.
|
||||
std::vector<Decl *> LocalTopLevelDecls;
|
||||
IncludeStructure Includes;
|
||||
CanonicalIncludes CanonIncludes;
|
||||
std::unique_ptr<HeuristicResolver> Resolver;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "Protocol.h"
|
||||
#include "SourceCode.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "support/Logger.h"
|
||||
#include "support/Path.h"
|
||||
#include "support/ThreadsafeFS.h"
|
||||
@@ -95,7 +94,6 @@ public:
|
||||
include_cleaner::PragmaIncludes takePragmaIncludes() {
|
||||
return std::move(Pragmas);
|
||||
}
|
||||
CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); }
|
||||
|
||||
std::optional<CapturedASTCtx> takeLife() { return std::move(CapturedCtx); }
|
||||
|
||||
@@ -141,7 +139,6 @@ public:
|
||||
}
|
||||
|
||||
void BeforeExecute(CompilerInstance &CI) override {
|
||||
CanonIncludes.addSystemHeadersMapping(CI.getLangOpts());
|
||||
LangOpts = &CI.getLangOpts();
|
||||
SourceMgr = &CI.getSourceManager();
|
||||
PP = &CI.getPreprocessor();
|
||||
@@ -160,11 +157,6 @@ public:
|
||||
collectPragmaMarksCallback(*SourceMgr, Marks));
|
||||
}
|
||||
|
||||
CommentHandler *getCommentHandler() override {
|
||||
IWYUHandler = collectIWYUHeaderMaps(&CanonIncludes);
|
||||
return IWYUHandler.get();
|
||||
}
|
||||
|
||||
static bool isLikelyForwardingFunction(FunctionTemplateDecl *FT) {
|
||||
const auto *FD = FT->getTemplatedDecl();
|
||||
const auto NumParams = FD->getNumParams();
|
||||
@@ -214,12 +206,10 @@ public:
|
||||
private:
|
||||
PathRef File;
|
||||
IncludeStructure Includes;
|
||||
CanonicalIncludes CanonIncludes;
|
||||
include_cleaner::PragmaIncludes Pragmas;
|
||||
MainFileMacros Macros;
|
||||
std::vector<PragmaMark> Marks;
|
||||
bool IsMainFileIncludeGuarded = false;
|
||||
std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
|
||||
const clang::LangOptions *LangOpts = nullptr;
|
||||
const SourceManager *SourceMgr = nullptr;
|
||||
const Preprocessor *PP = nullptr;
|
||||
@@ -686,11 +676,10 @@ buildPreamble(PathRef FileName, CompilerInvocation CI,
|
||||
Result->CompileCommand = Inputs.CompileCommand;
|
||||
Result->Diags = std::move(Diags);
|
||||
Result->Includes = CapturedInfo.takeIncludes();
|
||||
Result->Pragmas = CapturedInfo.takePragmaIncludes();
|
||||
Result->Pragmas = std::make_shared<const include_cleaner::PragmaIncludes>(
|
||||
CapturedInfo.takePragmaIncludes());
|
||||
Result->Macros = CapturedInfo.takeMacros();
|
||||
Result->Marks = CapturedInfo.takeMarks();
|
||||
Result->CanonIncludes = std::make_shared<const CanonicalIncludes>(
|
||||
(CapturedInfo.takeCanonicalIncludes()));
|
||||
Result->StatCache = StatCache;
|
||||
Result->MainIsIncludeGuarded = CapturedInfo.isMainFileIncludeGuarded();
|
||||
if (PreambleCallback) {
|
||||
@@ -703,7 +692,7 @@ buildPreamble(PathRef FileName, CompilerInvocation CI,
|
||||
// While extending the life of FileMgr and VFS, StatCache should also be
|
||||
// extended.
|
||||
Ctx->setStatCache(Result->StatCache);
|
||||
PreambleCallback(std::move(*Ctx), Result->CanonIncludes);
|
||||
PreambleCallback(std::move(*Ctx), Result->Pragmas);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "FS.h"
|
||||
#include "Headers.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "support/Path.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Frontend/CompilerInvocation.h"
|
||||
@@ -104,7 +103,7 @@ struct PreambleData {
|
||||
// information, and their compile action skips preamble range.
|
||||
IncludeStructure Includes;
|
||||
// Captures #include-mapping information in #included headers.
|
||||
include_cleaner::PragmaIncludes Pragmas;
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes> Pragmas;
|
||||
// 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.
|
||||
@@ -114,7 +113,6 @@ struct PreambleData {
|
||||
// Cache of FS operations performed when building the preamble.
|
||||
// When reusing a preamble, this cache can be consumed to save IO.
|
||||
std::shared_ptr<PreambleFileStatusCache> StatCache;
|
||||
std::shared_ptr<const 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;
|
||||
@@ -122,7 +120,7 @@ struct PreambleData {
|
||||
|
||||
using PreambleParsedCallback =
|
||||
std::function<void(CapturedASTCtx ASTCtx,
|
||||
std::shared_ptr<const CanonicalIncludes> CanonIncludes)>;
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes>)>;
|
||||
|
||||
/// 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,
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
#include "GlobalCompilationDatabase.h"
|
||||
#include "ParsedAST.h"
|
||||
#include "Preamble.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "support/Cancellation.h"
|
||||
#include "support/Context.h"
|
||||
#include "support/Logger.h"
|
||||
@@ -1080,9 +1080,9 @@ void PreambleThread::build(Request Req) {
|
||||
LatestBuild = clang::clangd::buildPreamble(
|
||||
FileName, *Req.CI, Inputs, StoreInMemory,
|
||||
[&](CapturedASTCtx ASTCtx,
|
||||
std::shared_ptr<const CanonicalIncludes> CanonIncludes) {
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes> PI) {
|
||||
Callbacks.onPreambleAST(FileName, Inputs.Version, std::move(ASTCtx),
|
||||
CanonIncludes);
|
||||
std::move(PI));
|
||||
},
|
||||
&Stats);
|
||||
if (!LatestBuild)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "Compiler.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "GlobalCompilationDatabase.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "support/Function.h"
|
||||
#include "support/MemoryTree.h"
|
||||
#include "support/Path.h"
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
@@ -161,10 +162,9 @@ public:
|
||||
/// Called on the AST that was built for emitting the preamble. The built AST
|
||||
/// contains only AST nodes from the #include directives at the start of the
|
||||
/// file. AST node in the current file should be observed on onMainAST call.
|
||||
virtual void onPreambleAST(PathRef Path, llvm::StringRef Version,
|
||||
CapturedASTCtx Ctx,
|
||||
const std::shared_ptr<const CanonicalIncludes>) {}
|
||||
|
||||
virtual void
|
||||
onPreambleAST(PathRef Path, llvm::StringRef Version, CapturedASTCtx Ctx,
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes>) {}
|
||||
/// The argument function is run under the critical section guarding against
|
||||
/// races when closing the files.
|
||||
using PublishFn = llvm::function_ref<void(llvm::function_ref<void()>)>;
|
||||
|
||||
@@ -1334,7 +1334,7 @@ maybeFindIncludeReferences(ParsedAST &AST, Position Pos,
|
||||
auto ReferencedInclude = convertIncludes(SM, Inc);
|
||||
include_cleaner::walkUsed(
|
||||
AST.getLocalTopLevelDecls(), collectMacroReferences(AST),
|
||||
AST.getPragmaIncludes(), SM,
|
||||
AST.getPragmaIncludes().get(), SM,
|
||||
[&](const include_cleaner::SymbolReference &Ref,
|
||||
llvm::ArrayRef<include_cleaner::Header> Providers) {
|
||||
if (Ref.RT != include_cleaner::RefType::Explicit)
|
||||
|
||||
@@ -7,14 +7,10 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CanonicalIncludes.h"
|
||||
#include "Headers.h"
|
||||
#include "clang/Basic/FileEntry.h"
|
||||
#include "clang/Tooling/Inclusions/HeaderAnalysis.h"
|
||||
#include "clang/Tooling/Inclusions/StandardLibrary.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/FileSystem/UniqueID.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
@@ -669,31 +665,21 @@ const std::pair<llvm::StringRef, llvm::StringRef> IncludeMappings[] = {
|
||||
|
||||
} // namespace
|
||||
|
||||
void CanonicalIncludes::addMapping(FileEntryRef Header,
|
||||
llvm::StringRef CanonicalPath) {
|
||||
FullPathMapping[Header.getUniqueID()] = std::string(CanonicalPath);
|
||||
}
|
||||
|
||||
/// The maximum number of path components in a key from StdSuffixHeaderMapping.
|
||||
/// Used to minimize the number of lookups in suffix path mappings.
|
||||
constexpr int MaxSuffixComponents = 3;
|
||||
|
||||
llvm::StringRef CanonicalIncludes::mapHeader(FileEntryRef Header) const {
|
||||
auto MapIt = FullPathMapping.find(Header.getUniqueID());
|
||||
if (MapIt != FullPathMapping.end())
|
||||
return MapIt->second;
|
||||
|
||||
llvm::StringRef CanonicalIncludes::mapHeader(llvm::StringRef HeaderPath) const {
|
||||
if (!StdSuffixHeaderMapping)
|
||||
return "";
|
||||
|
||||
int Components = 1;
|
||||
|
||||
// FIXME: check that this works on Windows and add tests.
|
||||
auto Filename = Header.getName();
|
||||
for (auto It = llvm::sys::path::rbegin(Filename),
|
||||
End = llvm::sys::path::rend(Filename);
|
||||
for (auto It = llvm::sys::path::rbegin(HeaderPath),
|
||||
End = llvm::sys::path::rend(HeaderPath);
|
||||
It != End && Components <= MaxSuffixComponents; ++It, ++Components) {
|
||||
auto SubPath = Filename.substr(It->data() - Filename.begin());
|
||||
auto SubPath = HeaderPath.substr(It->data() - HeaderPath.begin());
|
||||
auto MappingIt = StdSuffixHeaderMapping->find(SubPath);
|
||||
if (MappingIt != StdSuffixHeaderMapping->end())
|
||||
return MappingIt->second;
|
||||
@@ -701,54 +687,6 @@ llvm::StringRef CanonicalIncludes::mapHeader(FileEntryRef Header) const {
|
||||
return "";
|
||||
}
|
||||
|
||||
llvm::StringRef CanonicalIncludes::mapSymbol(llvm::StringRef Scope,
|
||||
llvm::StringRef Name,
|
||||
const LangOptions &L) const {
|
||||
tooling::stdlib::Lang Lang;
|
||||
if (L.CPlusPlus)
|
||||
Lang = tooling::stdlib::Lang::CXX;
|
||||
else if (L.C11)
|
||||
Lang = tooling::stdlib::Lang::C;
|
||||
else
|
||||
return "";
|
||||
// FIXME: remove the following special cases when the tooling stdlib supports
|
||||
// them.
|
||||
// There are two std::move()s, this is by far the most common.
|
||||
if (Scope == "std::" && Name == "move")
|
||||
return "<utility>";
|
||||
if (auto StdSym = tooling::stdlib::Symbol::named(Scope, Name, Lang))
|
||||
if (auto Header = StdSym->header())
|
||||
return Header->name();
|
||||
return "";
|
||||
}
|
||||
|
||||
std::unique_ptr<CommentHandler>
|
||||
collectIWYUHeaderMaps(CanonicalIncludes *Includes) {
|
||||
class PragmaCommentHandler : public clang::CommentHandler {
|
||||
public:
|
||||
PragmaCommentHandler(CanonicalIncludes *Includes) : Includes(Includes) {}
|
||||
|
||||
bool HandleComment(Preprocessor &PP, SourceRange Range) override {
|
||||
auto Pragma = tooling::parseIWYUPragma(
|
||||
PP.getSourceManager().getCharacterData(Range.getBegin()));
|
||||
if (!Pragma || !Pragma->consume_front("private, include "))
|
||||
return false;
|
||||
auto &SM = PP.getSourceManager();
|
||||
// We always insert using the spelling from the pragma.
|
||||
if (auto *FE = SM.getFileEntryForID(SM.getFileID(Range.getBegin())))
|
||||
Includes->addMapping(FE->getLastRef(),
|
||||
isLiteralInclude(*Pragma)
|
||||
? Pragma->str()
|
||||
: ("\"" + *Pragma + "\"").str());
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
CanonicalIncludes *const Includes;
|
||||
};
|
||||
return std::make_unique<PragmaCommentHandler>(Includes);
|
||||
}
|
||||
|
||||
void CanonicalIncludes::addSystemHeadersMapping(const LangOptions &Language) {
|
||||
// FIXME: remove the std header mapping once we support ambiguous symbols, now
|
||||
// it serves as a fallback to disambiguate:
|
||||
|
||||
@@ -20,13 +20,9 @@
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_CANONICALINCLUDES_H
|
||||
|
||||
#include "clang/Basic/FileEntry.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/FileSystem/UniqueID.h"
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
@@ -36,18 +32,10 @@ namespace clangd {
|
||||
/// Only const methods (i.e. mapHeader) in this class are thread safe.
|
||||
class CanonicalIncludes {
|
||||
public:
|
||||
/// Adds a file-to-string mapping from \p ID to \p CanonicalPath.
|
||||
void addMapping(FileEntryRef Header, llvm::StringRef CanonicalPath);
|
||||
|
||||
/// Returns the overridden include for a qualified symbol with, or "".
|
||||
/// \p Scope and \p Name concatenation forms the fully qualified name.
|
||||
/// \p Scope is the qualifier with the trailing "::" (e.g. "std::") or empty
|
||||
/// (for global namespace).
|
||||
llvm::StringRef mapSymbol(llvm::StringRef Scope, llvm::StringRef Name,
|
||||
const LangOptions &L) const;
|
||||
|
||||
/// Returns the overridden include for files in \p Header, or "".
|
||||
llvm::StringRef mapHeader(FileEntryRef Header) const;
|
||||
/// Returns the overridden verbatim spelling for files in \p Header that can
|
||||
/// be directly included (i.e., contains quotes "" or angled brackets <>), or
|
||||
/// "" if the spelling could not be found.
|
||||
llvm::StringRef mapHeader(llvm::StringRef HeaderPath) const;
|
||||
|
||||
/// Adds mapping for system headers and some special symbols (e.g. STL symbols
|
||||
/// in <iosfwd> need to be mapped individually). Approximately, the following
|
||||
@@ -60,37 +48,10 @@ public:
|
||||
void addSystemHeadersMapping(const LangOptions &Language);
|
||||
|
||||
private:
|
||||
/// A map from the private header to a canonical include path.
|
||||
llvm::DenseMap<llvm::sys::fs::UniqueID, std::string> FullPathMapping;
|
||||
/// A map from a suffix (one or components of a path) to a canonical path.
|
||||
/// Used only for mapping standard headers.
|
||||
const llvm::StringMap<llvm::StringRef> *StdSuffixHeaderMapping = nullptr;
|
||||
};
|
||||
|
||||
/// Returns a CommentHandler that parses pragma comment on include files to
|
||||
/// determine when we should include a different header from the header that
|
||||
/// directly defines a symbol. Mappinps are registered with \p Includes.
|
||||
///
|
||||
/// Currently it only supports IWYU private pragma:
|
||||
/// https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md#iwyu-pragma-private
|
||||
///
|
||||
/// We ignore other pragmas:
|
||||
/// - keep: this is common but irrelevant: we do not currently remove includes
|
||||
/// - export: this is common and potentially interesting, there are three cases:
|
||||
/// * Points to a public header (common): we can suppress include2 if you
|
||||
/// already have include1. Only marginally useful.
|
||||
/// * Points to a private header annotated with `private` (somewhat common):
|
||||
/// Not incrementally useful as we support private.
|
||||
/// * Points to a private header without pragmas (rare). This is a reversed
|
||||
/// private pragma, and is valuable but too rare to be worthwhile.
|
||||
/// - no_include: this is about as common as private, but only affects the
|
||||
/// current file, so the value is smaller. We could add support.
|
||||
/// - friend: this is less common than private, has implementation difficulties,
|
||||
/// and affects behavior in a limited scope.
|
||||
/// - associated: extremely rare
|
||||
std::unique_ptr<CommentHandler>
|
||||
collectIWYUHeaderMaps(CanonicalIncludes *Includes);
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "FileIndex.h"
|
||||
#include "CollectMacros.h"
|
||||
#include "ParsedAST.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/Index.h"
|
||||
#include "index/MemIndex.h"
|
||||
#include "index/Merge.h"
|
||||
@@ -46,11 +46,12 @@ namespace {
|
||||
SlabTuple indexSymbols(ASTContext &AST, Preprocessor &PP,
|
||||
llvm::ArrayRef<Decl *> DeclsToIndex,
|
||||
const MainFileMacros *MacroRefsToIndex,
|
||||
const CanonicalIncludes &Includes, bool IsIndexMainAST,
|
||||
llvm::StringRef Version, bool CollectMainFileRefs) {
|
||||
const include_cleaner::PragmaIncludes &PI,
|
||||
bool IsIndexMainAST, llvm::StringRef Version,
|
||||
bool CollectMainFileRefs) {
|
||||
SymbolCollector::Options CollectorOpts;
|
||||
CollectorOpts.CollectIncludePath = true;
|
||||
CollectorOpts.Includes = &Includes;
|
||||
CollectorOpts.PragmaIncludes = &PI;
|
||||
CollectorOpts.CountReferences = false;
|
||||
CollectorOpts.Origin =
|
||||
IsIndexMainAST ? SymbolOrigin::Open : SymbolOrigin::Preamble;
|
||||
@@ -222,18 +223,18 @@ FileShardedIndex::getShard(llvm::StringRef Uri) const {
|
||||
SlabTuple indexMainDecls(ParsedAST &AST) {
|
||||
return indexSymbols(
|
||||
AST.getASTContext(), AST.getPreprocessor(), AST.getLocalTopLevelDecls(),
|
||||
&AST.getMacros(), AST.getCanonicalIncludes(),
|
||||
&AST.getMacros(), *AST.getPragmaIncludes(),
|
||||
/*IsIndexMainAST=*/true, AST.version(), /*CollectMainFileRefs=*/true);
|
||||
}
|
||||
|
||||
SlabTuple indexHeaderSymbols(llvm::StringRef Version, ASTContext &AST,
|
||||
Preprocessor &PP,
|
||||
const CanonicalIncludes &Includes) {
|
||||
const include_cleaner::PragmaIncludes &PI) {
|
||||
std::vector<Decl *> DeclsToIndex(
|
||||
AST.getTranslationUnitDecl()->decls().begin(),
|
||||
AST.getTranslationUnitDecl()->decls().end());
|
||||
return indexSymbols(AST, PP, DeclsToIndex,
|
||||
/*MainFileMacros=*/nullptr, Includes,
|
||||
/*MainFileMacros=*/nullptr, PI,
|
||||
/*IsIndexMainAST=*/false, Version,
|
||||
/*CollectMainFileRefs=*/false);
|
||||
}
|
||||
@@ -458,10 +459,10 @@ void FileIndex::updatePreamble(IndexFileIn IF) {
|
||||
|
||||
void FileIndex::updatePreamble(PathRef Path, llvm::StringRef Version,
|
||||
ASTContext &AST, Preprocessor &PP,
|
||||
const CanonicalIncludes &Includes) {
|
||||
const include_cleaner::PragmaIncludes &PI) {
|
||||
IndexFileIn IF;
|
||||
std::tie(IF.Symbols, std::ignore, IF.Relations) =
|
||||
indexHeaderSymbols(Version, AST, PP, Includes);
|
||||
indexHeaderSymbols(Version, AST, PP, PI);
|
||||
updatePreamble(std::move(IF));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_FILEINDEX_H
|
||||
|
||||
#include "Headers.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/Index.h"
|
||||
#include "index/Merge.h"
|
||||
#include "index/Ref.h"
|
||||
@@ -113,7 +113,8 @@ public:
|
||||
/// Update preamble symbols of file \p Path with all declarations in \p AST
|
||||
/// and macros in \p PP.
|
||||
void updatePreamble(PathRef Path, llvm::StringRef Version, ASTContext &AST,
|
||||
Preprocessor &PP, const CanonicalIncludes &Includes);
|
||||
Preprocessor &PP,
|
||||
const include_cleaner::PragmaIncludes &PI);
|
||||
void updatePreamble(IndexFileIn);
|
||||
|
||||
/// Update symbols and references from main file \p Path with
|
||||
@@ -162,7 +163,7 @@ SlabTuple indexMainDecls(ParsedAST &AST);
|
||||
/// included headers.
|
||||
SlabTuple indexHeaderSymbols(llvm::StringRef Version, ASTContext &AST,
|
||||
Preprocessor &PP,
|
||||
const CanonicalIncludes &Includes);
|
||||
const include_cleaner::PragmaIncludes &PI);
|
||||
|
||||
/// Takes slabs coming from a TU (multiple files) and shards them per
|
||||
/// declaration location.
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "IndexAction.h"
|
||||
#include "AST.h"
|
||||
#include "Headers.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/Relation.h"
|
||||
#include "index/SymbolCollector.h"
|
||||
#include "index/SymbolOrigin.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
@@ -126,7 +128,7 @@ private:
|
||||
class IndexAction : public ASTFrontendAction {
|
||||
public:
|
||||
IndexAction(std::shared_ptr<SymbolCollector> C,
|
||||
std::unique_ptr<CanonicalIncludes> Includes,
|
||||
std::unique_ptr<include_cleaner::PragmaIncludes> PI,
|
||||
const index::IndexingOptions &Opts,
|
||||
std::function<void(SymbolSlab)> SymbolsCallback,
|
||||
std::function<void(RefSlab)> RefsCallback,
|
||||
@@ -135,8 +137,7 @@ public:
|
||||
: SymbolsCallback(SymbolsCallback), RefsCallback(RefsCallback),
|
||||
RelationsCallback(RelationsCallback),
|
||||
IncludeGraphCallback(IncludeGraphCallback), Collector(C),
|
||||
Includes(std::move(Includes)), Opts(Opts),
|
||||
PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {
|
||||
PI(std::move(PI)), Opts(Opts) {
|
||||
this->Opts.ShouldTraverseDecl = [this](const Decl *D) {
|
||||
// Many operations performed during indexing is linear in terms of depth
|
||||
// of the decl (USR generation, name lookups, figuring out role of a
|
||||
@@ -154,8 +155,7 @@ public:
|
||||
|
||||
std::unique_ptr<ASTConsumer>
|
||||
CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
|
||||
CI.getPreprocessor().addCommentHandler(PragmaHandler.get());
|
||||
Includes->addSystemHeadersMapping(CI.getLangOpts());
|
||||
PI->record(CI.getPreprocessor());
|
||||
if (IncludeGraphCallback != nullptr)
|
||||
CI.getPreprocessor().addPPCallbacks(
|
||||
std::make_unique<IncludeGraphCollector>(CI.getSourceManager(), IG));
|
||||
@@ -201,9 +201,8 @@ private:
|
||||
std::function<void(RelationSlab)> RelationsCallback;
|
||||
std::function<void(IncludeGraph)> IncludeGraphCallback;
|
||||
std::shared_ptr<SymbolCollector> Collector;
|
||||
std::unique_ptr<CanonicalIncludes> Includes;
|
||||
std::unique_ptr<include_cleaner::PragmaIncludes> PI;
|
||||
index::IndexingOptions Opts;
|
||||
std::unique_ptr<CommentHandler> PragmaHandler;
|
||||
IncludeGraph IG;
|
||||
};
|
||||
|
||||
@@ -228,12 +227,12 @@ std::unique_ptr<FrontendAction> createStaticIndexingAction(
|
||||
Opts.RefFilter = RefKind::All;
|
||||
Opts.RefsInHeaders = true;
|
||||
}
|
||||
auto Includes = std::make_unique<CanonicalIncludes>();
|
||||
Opts.Includes = Includes.get();
|
||||
return std::make_unique<IndexAction>(
|
||||
std::make_shared<SymbolCollector>(std::move(Opts)), std::move(Includes),
|
||||
IndexOpts, SymbolsCallback, RefsCallback, RelationsCallback,
|
||||
IncludeGraphCallback);
|
||||
auto PragmaIncludes = std::make_unique<include_cleaner::PragmaIncludes>();
|
||||
Opts.PragmaIncludes = PragmaIncludes.get();
|
||||
return std::make_unique<IndexAction>(std::make_shared<SymbolCollector>(Opts),
|
||||
std::move(PragmaIncludes), IndexOpts,
|
||||
SymbolsCallback, RefsCallback,
|
||||
RelationsCallback, IncludeGraphCallback);
|
||||
}
|
||||
|
||||
} // namespace clangd
|
||||
|
||||
@@ -13,8 +13,13 @@
|
||||
#include "ExpectedTypes.h"
|
||||
#include "SourceCode.h"
|
||||
#include "URI.h"
|
||||
#include "clang-include-cleaner/Analysis.h"
|
||||
#include "clang-include-cleaner/IncludeSpeller.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "clang-include-cleaner/Types.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "index/Relation.h"
|
||||
#include "index/Symbol.h"
|
||||
#include "index/SymbolID.h"
|
||||
#include "index/SymbolLocation.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
@@ -22,6 +27,8 @@
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/Basic/FileEntry.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
@@ -30,10 +37,17 @@
|
||||
#include "clang/Lex/Token.h"
|
||||
#include "clang/Tooling/Inclusions/HeaderAnalysis.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
@@ -188,7 +202,7 @@ class SymbolCollector::HeaderFileURICache {
|
||||
// (IndexDataConsumer::setPreprocessor can happen before or after initialize)
|
||||
Preprocessor *&PP;
|
||||
const SourceManager &SM;
|
||||
const CanonicalIncludes *Includes;
|
||||
const include_cleaner::PragmaIncludes *PI;
|
||||
llvm::StringRef FallbackDir;
|
||||
llvm::DenseMap<const FileEntry *, const std::string *> CacheFEToURI;
|
||||
llvm::StringMap<std::string> CachePathToURI;
|
||||
@@ -200,7 +214,7 @@ class SymbolCollector::HeaderFileURICache {
|
||||
public:
|
||||
HeaderFileURICache(Preprocessor *&PP, const SourceManager &SM,
|
||||
const SymbolCollector::Options &Opts)
|
||||
: PP(PP), SM(SM), Includes(Opts.Includes), FallbackDir(Opts.FallbackDir) {
|
||||
: PP(PP), SM(SM), PI(Opts.PragmaIncludes), FallbackDir(Opts.FallbackDir) {
|
||||
}
|
||||
|
||||
// Returns a canonical URI for the file \p FE.
|
||||
@@ -234,6 +248,23 @@ public:
|
||||
return R.first->second;
|
||||
}
|
||||
|
||||
// If a file is mapped by canonical headers, use that mapping, regardless
|
||||
// of whether it's an otherwise-good header (header guards etc).
|
||||
llvm::StringRef mapCanonical(llvm::StringRef HeaderPath) {
|
||||
if (!PP)
|
||||
return "";
|
||||
// Populate the system header mapping as late as possible to
|
||||
// ensure the preprocessor has been set already.
|
||||
CanonicalIncludes SysHeaderMapping;
|
||||
SysHeaderMapping.addSystemHeadersMapping(PP->getLangOpts());
|
||||
auto Canonical = SysHeaderMapping.mapHeader(HeaderPath);
|
||||
if (Canonical.empty())
|
||||
return "";
|
||||
// If we had a mapping, always use it.
|
||||
assert(Canonical.startswith("<") || Canonical.startswith("\""));
|
||||
return Canonical;
|
||||
}
|
||||
|
||||
private:
|
||||
// This takes care of making paths absolute and path->URI caching, but no
|
||||
// FileManager-based canonicalization.
|
||||
@@ -376,19 +407,14 @@ private:
|
||||
const auto FE = SM.getFileEntryRefForID(FID);
|
||||
if (!FE || FE->getName().empty())
|
||||
return "";
|
||||
|
||||
if (auto Verbatim = PI->getPublic(*FE); !Verbatim.empty())
|
||||
return Verbatim;
|
||||
|
||||
llvm::StringRef Filename = FE->getName();
|
||||
// If a file is mapped by canonical headers, use that mapping, regardless
|
||||
// of whether it's an otherwise-good header (header guards etc).
|
||||
if (Includes) {
|
||||
llvm::StringRef Canonical =
|
||||
Includes->mapHeader(*SM.getFileEntryRefForID(FID));
|
||||
if (!Canonical.empty()) {
|
||||
// If we had a mapping, always use it.
|
||||
if (Canonical.startswith("<") || Canonical.startswith("\""))
|
||||
return Canonical;
|
||||
return toURI(Canonical);
|
||||
}
|
||||
}
|
||||
if (auto Canonical = mapCanonical(Filename); !Canonical.empty())
|
||||
return Canonical;
|
||||
|
||||
// Framework headers are spelled as <FrameworkName/Foo.h>, not
|
||||
// "path/FrameworkName.framework/Headers/Foo.h".
|
||||
auto &HS = PP->getHeaderSearchInfo();
|
||||
@@ -764,7 +790,8 @@ bool SymbolCollector::handleMacroOccurrence(const IdentifierInfo *Name,
|
||||
S.CompletionSnippetSuffix = SnippetSuffix;
|
||||
|
||||
IndexedMacros.insert(Name);
|
||||
setIncludeLocation(S, DefLoc);
|
||||
|
||||
setIncludeLocation(S, DefLoc, include_cleaner::Macro{Name, DefLoc});
|
||||
Symbols.insert(S);
|
||||
return true;
|
||||
}
|
||||
@@ -798,13 +825,24 @@ void SymbolCollector::processRelations(
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolCollector::setIncludeLocation(const Symbol &S, SourceLocation Loc) {
|
||||
if (Opts.CollectIncludePath &&
|
||||
shouldCollectIncludePath(S.SymInfo.Kind) != Symbol::Invalid)
|
||||
// Use the expansion location to get the #include header since this is
|
||||
// where the symbol is exposed.
|
||||
IncludeFiles[S.ID] =
|
||||
PP->getSourceManager().getDecomposedExpansionLoc(Loc).first;
|
||||
void SymbolCollector::setIncludeLocation(const Symbol &S, SourceLocation DefLoc,
|
||||
const include_cleaner::Symbol &Sym) {
|
||||
const auto &SM = PP->getSourceManager();
|
||||
if (!Opts.CollectIncludePath ||
|
||||
shouldCollectIncludePath(S.SymInfo.Kind) == Symbol::Invalid)
|
||||
return;
|
||||
|
||||
// Use the expansion location to get the #include header since this is
|
||||
// where the symbol is exposed.
|
||||
IncludeFiles[S.ID] = SM.getDecomposedExpansionLoc(DefLoc).first;
|
||||
|
||||
auto [It, Inserted] = SymbolProviders.try_emplace(S.ID);
|
||||
if (Inserted) {
|
||||
auto Headers =
|
||||
include_cleaner::headersForSymbol(Sym, SM, Opts.PragmaIncludes);
|
||||
if (!Headers.empty())
|
||||
It->second = Headers.front();
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolCollector::finish() {
|
||||
@@ -830,54 +868,84 @@ void SymbolCollector::finish() {
|
||||
}
|
||||
}
|
||||
llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC;
|
||||
llvm::DenseMap<include_cleaner::Header, std::string> HeaderSpelling;
|
||||
// Fill in IncludeHeaders.
|
||||
// We delay this until end of TU so header guards are all resolved.
|
||||
for (const auto &[SID, FID] : IncludeFiles) {
|
||||
if (const Symbol *S = Symbols.find(SID)) {
|
||||
llvm::StringRef IncludeHeader;
|
||||
// Look for an overridden include header for this symbol specifically.
|
||||
if (Opts.Includes) {
|
||||
IncludeHeader =
|
||||
Opts.Includes->mapSymbol(S->Scope, S->Name, ASTCtx->getLangOpts());
|
||||
if (!IncludeHeader.empty()) {
|
||||
if (IncludeHeader.front() != '"' && IncludeHeader.front() != '<')
|
||||
IncludeHeader = HeaderFileURIs->toURI(IncludeHeader);
|
||||
else if (IncludeHeader == "<utility>" && S->Scope == "std::" &&
|
||||
S->Name == "move" && S->Signature.contains(','))
|
||||
IncludeHeader = "<algorithm>";
|
||||
}
|
||||
}
|
||||
// Otherwise find the approprate include header for the defining file.
|
||||
if (IncludeHeader.empty())
|
||||
IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
|
||||
for (const auto &[SID, OptionalProvider] : SymbolProviders) {
|
||||
const Symbol *S = Symbols.find(SID);
|
||||
if (!S)
|
||||
continue;
|
||||
const auto FIDIt = IncludeFiles.find(SID);
|
||||
assert(FIDIt != IncludeFiles.end());
|
||||
|
||||
// Symbols in slabs aren't mutable, insert() has to walk all the strings
|
||||
if (!IncludeHeader.empty()) {
|
||||
Symbol::IncludeDirective Directives = Symbol::Invalid;
|
||||
auto CollectDirectives = shouldCollectIncludePath(S->SymInfo.Kind);
|
||||
if ((CollectDirectives & Symbol::Include) != 0)
|
||||
Directives |= Symbol::Include;
|
||||
// Only allow #import for symbols from ObjC-like files.
|
||||
if ((CollectDirectives & Symbol::Import) != 0) {
|
||||
auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(FID);
|
||||
if (Inserted)
|
||||
It->second = FilesWithObjCConstructs.contains(FID) ||
|
||||
tooling::codeContainsImports(
|
||||
ASTCtx->getSourceManager().getBufferData(FID));
|
||||
if (It->second)
|
||||
Directives |= Symbol::Import;
|
||||
}
|
||||
if (Directives != Symbol::Invalid) {
|
||||
Symbol NewSym = *S;
|
||||
NewSym.IncludeHeaders.push_back({IncludeHeader, 1, Directives});
|
||||
Symbols.insert(NewSym);
|
||||
}
|
||||
const auto FID = IncludeFiles.at(SID);
|
||||
// Determine if the FID is #include'd or #import'ed.
|
||||
Symbol::IncludeDirective Directives = Symbol::Invalid;
|
||||
auto CollectDirectives = shouldCollectIncludePath(S->SymInfo.Kind);
|
||||
if ((CollectDirectives & Symbol::Include) != 0)
|
||||
Directives |= Symbol::Include;
|
||||
// Only allow #import for symbols from ObjC-like files.
|
||||
if ((CollectDirectives & Symbol::Import) != 0) {
|
||||
auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(FID);
|
||||
if (Inserted)
|
||||
It->second = FilesWithObjCConstructs.contains(FID) ||
|
||||
tooling::codeContainsImports(
|
||||
ASTCtx->getSourceManager().getBufferData(FID));
|
||||
if (It->second)
|
||||
Directives |= Symbol::Import;
|
||||
}
|
||||
|
||||
if (Directives == Symbol::Invalid)
|
||||
continue;
|
||||
|
||||
// Use the include location-based logic for Objective-C symbols.
|
||||
if (Directives & Symbol::Import) {
|
||||
if (auto IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
|
||||
!IncludeHeader.empty()) {
|
||||
auto NewSym = *S;
|
||||
NewSym.IncludeHeaders.push_back({IncludeHeader, 1, Directives});
|
||||
Symbols.insert(NewSym);
|
||||
}
|
||||
// FIXME: use providers from include-cleaner library once it's polished
|
||||
// for Objective-C.
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(Directives == Symbol::Include);
|
||||
// For #include's, use the providers computed by the include-cleaner
|
||||
// library.
|
||||
if (!OptionalProvider)
|
||||
continue;
|
||||
const auto &H = *OptionalProvider;
|
||||
const auto [SpellingIt, Inserted] = HeaderSpelling.try_emplace(H);
|
||||
if (Inserted) {
|
||||
auto &SM = ASTCtx->getSourceManager();
|
||||
if (H.kind() == include_cleaner::Header::Kind::Physical) {
|
||||
if (auto Canonical =
|
||||
HeaderFileURIs->mapCanonical(H.physical()->getName());
|
||||
!Canonical.empty())
|
||||
SpellingIt->second = Canonical;
|
||||
else if (tooling::isSelfContainedHeader(H.physical(), SM,
|
||||
PP->getHeaderSearchInfo()))
|
||||
SpellingIt->second =
|
||||
HeaderFileURIs->toURI(H.physical()->getLastRef());
|
||||
} else {
|
||||
SpellingIt->second = include_cleaner::spellHeader(
|
||||
{H, PP->getHeaderSearchInfo(),
|
||||
SM.getFileEntryForID(SM.getMainFileID())});
|
||||
}
|
||||
}
|
||||
|
||||
if (!SpellingIt->second.empty()) {
|
||||
auto NewSym = *S;
|
||||
NewSym.IncludeHeaders.push_back({SpellingIt->second, 1, Directives});
|
||||
Symbols.insert(NewSym);
|
||||
}
|
||||
}
|
||||
|
||||
ReferencedSymbols.clear();
|
||||
IncludeFiles.clear();
|
||||
SymbolProviders.clear();
|
||||
FilesWithObjCConstructs.clear();
|
||||
}
|
||||
|
||||
@@ -951,7 +1019,7 @@ const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, SymbolID ID,
|
||||
}
|
||||
|
||||
Symbols.insert(S);
|
||||
setIncludeLocation(S, ND.getLocation());
|
||||
setIncludeLocation(S, ND.getLocation(), include_cleaner::Symbol{ND});
|
||||
if (S.SymInfo.Lang == index::SymbolLanguage::ObjC)
|
||||
FilesWithObjCConstructs.insert(FID);
|
||||
return Symbols.find(S.ID);
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOLCOLLECTOR_H
|
||||
|
||||
#include "CollectMacros.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "clang-include-cleaner/Types.h"
|
||||
#include "index/Ref.h"
|
||||
#include "index/Relation.h"
|
||||
#include "index/Symbol.h"
|
||||
@@ -22,9 +23,9 @@
|
||||
#include "clang/Index/IndexDataConsumer.h"
|
||||
#include "clang/Index/IndexSymbol.h"
|
||||
#include "clang/Sema/CodeCompleteConsumer.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace clang {
|
||||
@@ -57,8 +58,8 @@ public:
|
||||
std::string FallbackDir;
|
||||
bool CollectIncludePath = false;
|
||||
/// If set, this is used to map symbol #include path to a potentially
|
||||
/// different #include path.
|
||||
const CanonicalIncludes *Includes = nullptr;
|
||||
/// different #include path specified by IWYU pragmas.
|
||||
const include_cleaner::PragmaIncludes *PragmaIncludes = nullptr;
|
||||
// Populate the Symbol.References field.
|
||||
bool CountReferences = false;
|
||||
/// The symbol ref kinds that will be collected.
|
||||
@@ -166,13 +167,22 @@ private:
|
||||
|
||||
// All Symbols collected from the AST.
|
||||
SymbolSlab::Builder Symbols;
|
||||
// File IDs for Symbol.IncludeHeaders.
|
||||
// The final spelling is calculated in finish().
|
||||
// File IDs used to determine if the code contains Obj-C constructs.
|
||||
// For Obj-C symbols, these File IDs are used to compute the include
|
||||
// headers.
|
||||
llvm::DenseMap<SymbolID, FileID> IncludeFiles;
|
||||
void setIncludeLocation(const Symbol &S, SourceLocation,
|
||||
const include_cleaner::Symbol &Sym);
|
||||
|
||||
// Providers for Symbol.IncludeHeaders.
|
||||
// The final spelling is calculated in finish().
|
||||
llvm::DenseMap<SymbolID, std::optional<include_cleaner::Header>>
|
||||
SymbolProviders;
|
||||
|
||||
// Files which contain ObjC symbols.
|
||||
// This is finalized and used in finish().
|
||||
llvm::DenseSet<FileID> FilesWithObjCConstructs;
|
||||
void setIncludeLocation(const Symbol &S, SourceLocation);
|
||||
|
||||
// Indexed macros, to be erased if they turned out to be include guards.
|
||||
llvm::DenseSet<const IdentifierInfo *> IndexedMacros;
|
||||
// All refs collected from the AST. It includes:
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
#include "SourceCode.h"
|
||||
#include "TidyProvider.h"
|
||||
#include "XRefs.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/FileIndex.h"
|
||||
#include "refactor/Tweak.h"
|
||||
#include "support/Context.h"
|
||||
@@ -231,12 +231,12 @@ public:
|
||||
Preamble = buildPreamble(
|
||||
File, *Invocation, Inputs, /*StoreInMemory=*/true,
|
||||
[&](CapturedASTCtx Ctx,
|
||||
const std::shared_ptr<const CanonicalIncludes> Includes) {
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes> PI) {
|
||||
if (!Opts.BuildDynamicSymbolIndex)
|
||||
return;
|
||||
log("Indexing headers...");
|
||||
Index.updatePreamble(File, /*Version=*/"null", Ctx.getASTContext(),
|
||||
Ctx.getPreprocessor(), *Includes);
|
||||
Ctx.getPreprocessor(), *PI);
|
||||
});
|
||||
if (!Preamble) {
|
||||
elog("Failed to build preamble");
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "TestFS.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "clang/Basic/FileEntry.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/FileSystemOptions.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
@@ -30,82 +30,21 @@ FileEntryRef addFile(llvm::vfs::InMemoryFileSystem &FS, FileManager &FM,
|
||||
return *File;
|
||||
}
|
||||
|
||||
TEST(CanonicalIncludesTest, CStandardLibrary) {
|
||||
CanonicalIncludes CI;
|
||||
auto Language = LangOptions();
|
||||
Language.C11 = true;
|
||||
CI.addSystemHeadersMapping(Language);
|
||||
// Usual standard library symbols are mapped correctly.
|
||||
EXPECT_EQ("<stdio.h>", CI.mapSymbol("", "printf", Language));
|
||||
EXPECT_EQ("", CI.mapSymbol("", "unknown_symbol", Language));
|
||||
}
|
||||
|
||||
TEST(CanonicalIncludesTest, CXXStandardLibrary) {
|
||||
CanonicalIncludes CI;
|
||||
auto Language = LangOptions();
|
||||
Language.CPlusPlus = true;
|
||||
CI.addSystemHeadersMapping(Language);
|
||||
|
||||
// Usual standard library symbols are mapped correctly.
|
||||
EXPECT_EQ("<vector>", CI.mapSymbol("std::", "vector", Language));
|
||||
EXPECT_EQ("<cstdio>", CI.mapSymbol("std::", "printf", Language));
|
||||
// std::move is ambiguous, currently always mapped to <utility>
|
||||
EXPECT_EQ("<utility>", CI.mapSymbol("std::", "move", Language));
|
||||
EXPECT_EQ("<cstddef>", CI.mapSymbol("std::", "size_t", Language));
|
||||
// Unknown std symbols aren't mapped.
|
||||
EXPECT_EQ("", CI.mapSymbol("std::", "notathing", Language));
|
||||
// iosfwd declares some symbols it doesn't own.
|
||||
EXPECT_EQ("<ostream>", CI.mapSymbol("std::", "ostream", Language));
|
||||
// And (for now) we assume it owns the others.
|
||||
TEST(CanonicalIncludesTest, SystemHeaderMap) {
|
||||
auto InMemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
|
||||
FileManager Files(FileSystemOptions(), InMemFS);
|
||||
auto File = addFile(*InMemFS, Files, testPath("iosfwd"));
|
||||
EXPECT_EQ("<iosfwd>", CI.mapHeader(File));
|
||||
}
|
||||
|
||||
TEST(CanonicalIncludesTest, PathMapping) {
|
||||
auto InMemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
|
||||
FileManager Files(FileSystemOptions(), InMemFS);
|
||||
std::string BarPath = testPath("foo/bar");
|
||||
auto Bar = addFile(*InMemFS, Files, BarPath);
|
||||
auto Other = addFile(*InMemFS, Files, testPath("foo/baz"));
|
||||
// As used for IWYU pragmas.
|
||||
CanonicalIncludes CI;
|
||||
CI.addMapping(Bar, "<baz>");
|
||||
|
||||
// We added a mapping for baz.
|
||||
EXPECT_EQ("<baz>", CI.mapHeader(Bar));
|
||||
// Other file doesn't have a mapping.
|
||||
EXPECT_EQ("", CI.mapHeader(Other));
|
||||
|
||||
// Add hard link to "foo/bar" and check that it is also mapped to <baz>, hence
|
||||
// does not depend on the header name.
|
||||
std::string HardLinkPath = testPath("hard/link");
|
||||
InMemFS->addHardLink(HardLinkPath, BarPath);
|
||||
auto HardLinkFile = Files.getFileRef(HardLinkPath);
|
||||
ASSERT_THAT_EXPECTED(HardLinkFile, llvm::Succeeded());
|
||||
EXPECT_EQ("<baz>", CI.mapHeader(*HardLinkFile));
|
||||
}
|
||||
|
||||
TEST(CanonicalIncludesTest, Precedence) {
|
||||
auto InMemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
|
||||
FileManager Files(FileSystemOptions(), InMemFS);
|
||||
auto File = addFile(*InMemFS, Files, testPath("some/path"));
|
||||
|
||||
CanonicalIncludes CI;
|
||||
CI.addMapping(File, "<path>");
|
||||
LangOptions Language;
|
||||
Language.CPlusPlus = true;
|
||||
CI.addSystemHeadersMapping(Language);
|
||||
|
||||
// We added a mapping from some/path to <path>.
|
||||
ASSERT_EQ("<path>", CI.mapHeader(File));
|
||||
// We should have a path from 'bits/stl_vector.h' to '<vector>'.
|
||||
// FIXME: The Standrad Library map in CanonicalIncludes expects forward
|
||||
// slashes and Windows would use backward slashes instead, so the headers are
|
||||
// not matched appropriately.
|
||||
auto STLVectorFile = addFile(*InMemFS, Files, "bits/stl_vector.h");
|
||||
ASSERT_EQ("<vector>", CI.mapHeader(STLVectorFile));
|
||||
ASSERT_EQ("<vector>", CI.mapHeader(STLVectorFile.getName()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "TestTU.h"
|
||||
#include "TestWorkspace.h"
|
||||
#include "URI.h"
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/FileIndex.h"
|
||||
#include "index/Index.h"
|
||||
#include "index/Ref.h"
|
||||
@@ -25,12 +25,12 @@
|
||||
#include "index/SymbolID.h"
|
||||
#include "support/Threading.h"
|
||||
#include "clang/Frontend/CompilerInvocation.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Tooling/CompilationDatabase.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -60,6 +60,11 @@ MATCHER_P(qName, N, "") { return (arg.Scope + arg.Name).str() == N; }
|
||||
MATCHER_P(numReferences, N, "") { return arg.References == N; }
|
||||
MATCHER_P(hasOrign, O, "") { return bool(arg.Origin & O); }
|
||||
|
||||
MATCHER_P(includeHeader, P, "") {
|
||||
return (arg.IncludeHeaders.size() == 1) &&
|
||||
(arg.IncludeHeaders.begin()->IncludeHeader == P);
|
||||
}
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
namespace {
|
||||
@@ -171,7 +176,7 @@ void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) {
|
||||
auto AST = File.build();
|
||||
M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
|
||||
AST.getASTContext(), AST.getPreprocessor(),
|
||||
AST.getCanonicalIncludes());
|
||||
*AST.getPragmaIncludes());
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, CustomizedURIScheme) {
|
||||
@@ -234,6 +239,32 @@ TEST(FileIndexTest, IncludeCollected) {
|
||||
"<the/good/header.h>");
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, IWYUPragmaExport) {
|
||||
FileIndex M;
|
||||
|
||||
TestTU File;
|
||||
File.Code = R"cpp(#pragma once
|
||||
#include "exporter.h"
|
||||
)cpp";
|
||||
File.HeaderFilename = "exporter.h";
|
||||
File.HeaderCode = R"cpp(#pragma once
|
||||
#include "private.h" // IWYU pragma: export
|
||||
)cpp";
|
||||
File.AdditionalFiles["private.h"] = "class Foo{};";
|
||||
auto AST = File.build();
|
||||
M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
|
||||
AST.getASTContext(), AST.getPreprocessor(),
|
||||
*AST.getPragmaIncludes());
|
||||
|
||||
auto Symbols = runFuzzyFind(M, "");
|
||||
EXPECT_THAT(
|
||||
Symbols,
|
||||
UnorderedElementsAre(AllOf(
|
||||
qName("Foo"),
|
||||
includeHeader(URI::create(testPath(File.HeaderFilename)).toString()),
|
||||
declURI(URI::create(testPath("private.h")).toString()))));
|
||||
}
|
||||
|
||||
TEST(FileIndexTest, HasSystemHeaderMappingsInPreamble) {
|
||||
TestTU TU;
|
||||
TU.HeaderCode = "class Foo{};";
|
||||
@@ -309,13 +340,12 @@ TEST(FileIndexTest, RebuildWithPreamble) {
|
||||
FooCpp, *CI, PI,
|
||||
/*StoreInMemory=*/true,
|
||||
[&](CapturedASTCtx ASTCtx,
|
||||
const std::shared_ptr<const CanonicalIncludes> CanonIncludes) {
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes> PI) {
|
||||
auto &Ctx = ASTCtx.getASTContext();
|
||||
auto &PP = ASTCtx.getPreprocessor();
|
||||
EXPECT_FALSE(IndexUpdated) << "Expected only a single index update";
|
||||
IndexUpdated = true;
|
||||
Index.updatePreamble(FooCpp, /*Version=*/"null", Ctx, PP,
|
||||
*CanonIncludes);
|
||||
Index.updatePreamble(FooCpp, /*Version=*/"null", Ctx, PP, *PI);
|
||||
});
|
||||
ASSERT_TRUE(IndexUpdated);
|
||||
|
||||
@@ -416,7 +446,7 @@ TEST(FileIndexTest, Relations) {
|
||||
FileIndex Index;
|
||||
Index.updatePreamble(testPath(TU.Filename), /*Version=*/"null",
|
||||
AST.getASTContext(), AST.getPreprocessor(),
|
||||
AST.getCanonicalIncludes());
|
||||
*AST.getPragmaIncludes());
|
||||
SymbolID A = findSymbol(TU.headerSymbols(), "A").ID;
|
||||
uint32_t Results = 0;
|
||||
RelationsRequest Req;
|
||||
@@ -537,7 +567,7 @@ TEST(FileIndexTest, StalePreambleSymbolsDeleted) {
|
||||
auto AST = File.build();
|
||||
M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
|
||||
AST.getASTContext(), AST.getPreprocessor(),
|
||||
AST.getCanonicalIncludes());
|
||||
*AST.getPragmaIncludes());
|
||||
EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("a")));
|
||||
|
||||
File.Filename = "f2.cpp";
|
||||
@@ -545,7 +575,7 @@ TEST(FileIndexTest, StalePreambleSymbolsDeleted) {
|
||||
AST = File.build();
|
||||
M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
|
||||
AST.getASTContext(), AST.getPreprocessor(),
|
||||
AST.getCanonicalIncludes());
|
||||
*AST.getPragmaIncludes());
|
||||
EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("b")));
|
||||
}
|
||||
|
||||
@@ -690,7 +720,7 @@ TEST(FileIndexTest, Profile) {
|
||||
auto AST = TestTU::withHeaderCode("int a;").build();
|
||||
FI.updateMain(FileName, AST);
|
||||
FI.updatePreamble(FileName, "v1", AST.getASTContext(), AST.getPreprocessor(),
|
||||
AST.getCanonicalIncludes());
|
||||
*AST.getPragmaIncludes());
|
||||
|
||||
llvm::BumpPtrAllocator Alloc;
|
||||
MemoryTree MT(&Alloc);
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "Annotations.h"
|
||||
#include "TestFS.h"
|
||||
#include "TestTU.h"
|
||||
#include "URI.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/SymbolCollector.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/FileSystemOptions.h"
|
||||
@@ -20,7 +22,6 @@
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/VirtualFileSystem.h"
|
||||
#include "llvm/Testing/Support/Error.h"
|
||||
#include "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
@@ -28,6 +29,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
@@ -226,23 +228,21 @@ TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
|
||||
|
||||
class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
|
||||
public:
|
||||
SymbolIndexActionFactory(SymbolCollector::Options COpts,
|
||||
CommentHandler *PragmaHandler)
|
||||
: COpts(std::move(COpts)), PragmaHandler(PragmaHandler) {}
|
||||
SymbolIndexActionFactory(SymbolCollector::Options COpts)
|
||||
: COpts(std::move(COpts)) {}
|
||||
|
||||
std::unique_ptr<FrontendAction> create() override {
|
||||
class IndexAction : public ASTFrontendAction {
|
||||
public:
|
||||
IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
|
||||
const index::IndexingOptions &Opts,
|
||||
CommentHandler *PragmaHandler)
|
||||
std::shared_ptr<include_cleaner::PragmaIncludes> PI)
|
||||
: DataConsumer(std::move(DataConsumer)), Opts(Opts),
|
||||
PragmaHandler(PragmaHandler) {}
|
||||
PI(std::move(PI)) {}
|
||||
|
||||
std::unique_ptr<ASTConsumer>
|
||||
CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
|
||||
if (PragmaHandler)
|
||||
CI.getPreprocessor().addCommentHandler(PragmaHandler);
|
||||
PI->record(CI);
|
||||
return createIndexingASTConsumer(DataConsumer, Opts,
|
||||
CI.getPreprocessorPtr());
|
||||
}
|
||||
@@ -256,20 +256,22 @@ public:
|
||||
private:
|
||||
std::shared_ptr<index::IndexDataConsumer> DataConsumer;
|
||||
index::IndexingOptions Opts;
|
||||
CommentHandler *PragmaHandler;
|
||||
std::shared_ptr<include_cleaner::PragmaIncludes> PI;
|
||||
};
|
||||
index::IndexingOptions IndexOpts;
|
||||
IndexOpts.SystemSymbolFilter =
|
||||
index::IndexingOptions::SystemSymbolFilterKind::All;
|
||||
IndexOpts.IndexFunctionLocals = true;
|
||||
std::shared_ptr<include_cleaner::PragmaIncludes> PI =
|
||||
std::make_shared<include_cleaner::PragmaIncludes>();
|
||||
COpts.PragmaIncludes = PI.get();
|
||||
Collector = std::make_shared<SymbolCollector>(COpts);
|
||||
return std::make_unique<IndexAction>(Collector, std::move(IndexOpts),
|
||||
PragmaHandler);
|
||||
std::move(PI));
|
||||
}
|
||||
|
||||
std::shared_ptr<SymbolCollector> Collector;
|
||||
SymbolCollector::Options COpts;
|
||||
CommentHandler *PragmaHandler;
|
||||
};
|
||||
|
||||
class SymbolCollectorTest : public ::testing::Test {
|
||||
@@ -289,8 +291,7 @@ public:
|
||||
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
||||
new FileManager(FileSystemOptions(), InMemoryFileSystem));
|
||||
|
||||
auto Factory = std::make_unique<SymbolIndexActionFactory>(
|
||||
CollectorOpts, PragmaHandler.get());
|
||||
auto Factory = std::make_unique<SymbolIndexActionFactory>(CollectorOpts);
|
||||
|
||||
std::vector<std::string> Args = {"symbol_collector", "-fsyntax-only",
|
||||
"-xc++", "-include", TestHeaderName};
|
||||
@@ -324,7 +325,6 @@ protected:
|
||||
RefSlab Refs;
|
||||
RelationSlab Relations;
|
||||
SymbolCollector::Options CollectorOpts;
|
||||
std::unique_ptr<CommentHandler> PragmaHandler;
|
||||
};
|
||||
|
||||
TEST_F(SymbolCollectorTest, CollectSymbols) {
|
||||
@@ -1545,11 +1545,6 @@ TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
|
||||
|
||||
TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
|
||||
CollectorOpts.CollectIncludePath = true;
|
||||
CanonicalIncludes Includes;
|
||||
auto Language = LangOptions();
|
||||
Language.CPlusPlus = true;
|
||||
Includes.addSystemHeadersMapping(Language);
|
||||
CollectorOpts.Includes = &Includes;
|
||||
runSymbolCollector(
|
||||
R"cpp(
|
||||
namespace std {
|
||||
@@ -1573,9 +1568,6 @@ TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
|
||||
|
||||
TEST_F(SymbolCollectorTest, IWYUPragma) {
|
||||
CollectorOpts.CollectIncludePath = true;
|
||||
CanonicalIncludes Includes;
|
||||
PragmaHandler = collectIWYUHeaderMaps(&Includes);
|
||||
CollectorOpts.Includes = &Includes;
|
||||
const std::string Header = R"(
|
||||
// IWYU pragma: private, include the/good/header.h
|
||||
class Foo {};
|
||||
@@ -1588,9 +1580,6 @@ TEST_F(SymbolCollectorTest, IWYUPragma) {
|
||||
|
||||
TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
|
||||
CollectorOpts.CollectIncludePath = true;
|
||||
CanonicalIncludes Includes;
|
||||
PragmaHandler = collectIWYUHeaderMaps(&Includes);
|
||||
CollectorOpts.Includes = &Includes;
|
||||
const std::string Header = R"(
|
||||
// IWYU pragma: private, include "the/good/header.h"
|
||||
class Foo {};
|
||||
@@ -1601,29 +1590,25 @@ TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
|
||||
includeHeader("\"the/good/header.h\""))));
|
||||
}
|
||||
|
||||
TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
|
||||
auto IncFile = testPath("test.inc");
|
||||
auto IncURI = URI::create(IncFile).toString();
|
||||
InMemoryFileSystem->addFile(IncFile, 0,
|
||||
llvm::MemoryBuffer::getMemBuffer("class X {};"));
|
||||
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
||||
new FileManager(FileSystemOptions(), InMemoryFileSystem));
|
||||
std::string HeaderCode = "#include \"test.inc\"\nclass Y {};";
|
||||
InMemoryFileSystem->addFile(TestHeaderName, 0,
|
||||
llvm::MemoryBuffer::getMemBuffer(HeaderCode));
|
||||
auto File = Files->getFileRef(TestHeaderName);
|
||||
ASSERT_THAT_EXPECTED(File, llvm::Succeeded());
|
||||
CanonicalIncludes Includes;
|
||||
Includes.addMapping(*File, "<canonical>");
|
||||
TEST_F(SymbolCollectorTest, IWYUPragmaExport) {
|
||||
CollectorOpts.CollectIncludePath = true;
|
||||
CollectorOpts.Includes = &Includes;
|
||||
runSymbolCollector(HeaderCode, /*Main=*/"",
|
||||
const std::string Header = R"cpp(#pragma once
|
||||
#include "exporter.h"
|
||||
)cpp";
|
||||
auto ExporterFile = testPath("exporter.h");
|
||||
InMemoryFileSystem->addFile(
|
||||
ExporterFile, 0, llvm::MemoryBuffer::getMemBuffer(R"cpp(#pragma once
|
||||
#include "private.h" // IWYU pragma: export
|
||||
)cpp"));
|
||||
auto PrivateFile = testPath("private.h");
|
||||
InMemoryFileSystem->addFile(
|
||||
PrivateFile, 0, llvm::MemoryBuffer::getMemBuffer("class Foo {};"));
|
||||
runSymbolCollector(Header, /*Main=*/"",
|
||||
/*ExtraArgs=*/{"-I", testRoot()});
|
||||
EXPECT_THAT(Symbols,
|
||||
UnorderedElementsAre(AllOf(qName("X"), declURI(IncURI),
|
||||
includeHeader("<canonical>")),
|
||||
AllOf(qName("Y"), declURI(TestHeaderURI),
|
||||
includeHeader("<canonical>"))));
|
||||
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
|
||||
qName("Foo"),
|
||||
includeHeader(URI::create(ExporterFile).toString()),
|
||||
declURI(URI::create(PrivateFile).toString()))));
|
||||
}
|
||||
|
||||
TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "TUScheduler.h"
|
||||
#include "TestFS.h"
|
||||
#include "TestIndex.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "support/Cancellation.h"
|
||||
#include "support/Context.h"
|
||||
#include "support/Path.h"
|
||||
@@ -1134,9 +1135,9 @@ TEST_F(TUSchedulerTests, AsyncPreambleThread) {
|
||||
public:
|
||||
BlockPreambleThread(llvm::StringRef BlockVersion, Notification &N)
|
||||
: BlockVersion(BlockVersion), N(N) {}
|
||||
void
|
||||
onPreambleAST(PathRef Path, llvm::StringRef Version, CapturedASTCtx,
|
||||
const std::shared_ptr<const CanonicalIncludes>) override {
|
||||
void onPreambleAST(
|
||||
PathRef Path, llvm::StringRef Version, CapturedASTCtx,
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes>) override {
|
||||
if (Version == BlockVersion)
|
||||
N.wait();
|
||||
}
|
||||
@@ -1213,9 +1214,9 @@ TEST_F(TUSchedulerTests, PublishWithStalePreamble) {
|
||||
BlockPreambleThread(Notification &UnblockPreamble, DiagsCB CB)
|
||||
: UnblockPreamble(UnblockPreamble), CB(std::move(CB)) {}
|
||||
|
||||
void
|
||||
onPreambleAST(PathRef Path, llvm::StringRef Version, CapturedASTCtx,
|
||||
const std::shared_ptr<const CanonicalIncludes>) override {
|
||||
void onPreambleAST(
|
||||
PathRef Path, llvm::StringRef Version, CapturedASTCtx,
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes>) override {
|
||||
if (BuildBefore)
|
||||
ASSERT_TRUE(UnblockPreamble.wait(timeoutSeconds(5)))
|
||||
<< "Expected notification";
|
||||
@@ -1563,9 +1564,9 @@ TEST_F(TUSchedulerTests, PreambleThrottle) {
|
||||
std::vector<std::string> &Filenames;
|
||||
CaptureBuiltFilenames(std::vector<std::string> &Filenames)
|
||||
: Filenames(Filenames) {}
|
||||
void
|
||||
onPreambleAST(PathRef Path, llvm::StringRef Version, CapturedASTCtx,
|
||||
const std::shared_ptr<const CanonicalIncludes>) override {
|
||||
void onPreambleAST(
|
||||
PathRef Path, llvm::StringRef Version, CapturedASTCtx,
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes> PI) override {
|
||||
// Deliberately no synchronization.
|
||||
// The PreambleThrottler should serialize these calls, if not then tsan
|
||||
// will find a bug here.
|
||||
|
||||
@@ -162,9 +162,9 @@ ParsedAST TestTU::build() const {
|
||||
|
||||
SymbolSlab TestTU::headerSymbols() const {
|
||||
auto AST = build();
|
||||
return std::get<0>(indexHeaderSymbols(/*Version=*/"null", AST.getASTContext(),
|
||||
AST.getPreprocessor(),
|
||||
AST.getCanonicalIncludes()));
|
||||
return std::get<0>(indexHeaderSymbols(
|
||||
/*Version=*/"null", AST.getASTContext(), AST.getPreprocessor(),
|
||||
*AST.getPragmaIncludes()));
|
||||
}
|
||||
|
||||
RefSlab TestTU::headerRefs() const {
|
||||
@@ -177,7 +177,7 @@ std::unique_ptr<SymbolIndex> TestTU::index() const {
|
||||
auto Idx = std::make_unique<FileIndex>();
|
||||
Idx->updatePreamble(testPath(Filename), /*Version=*/"null",
|
||||
AST.getASTContext(), AST.getPreprocessor(),
|
||||
AST.getCanonicalIncludes());
|
||||
*AST.getPragmaIncludes());
|
||||
Idx->updateMain(testPath(Filename), AST);
|
||||
return std::move(Idx);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "TestWorkspace.h"
|
||||
#include "clang-include-cleaner/Record.h"
|
||||
#include "index/FileIndex.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace clang {
|
||||
@@ -21,14 +23,12 @@ std::unique_ptr<SymbolIndex> TestWorkspace::index() {
|
||||
continue;
|
||||
TU.Code = Input.second.Code;
|
||||
TU.Filename = Input.first().str();
|
||||
TU.preamble(
|
||||
[&](CapturedASTCtx ASTCtx,
|
||||
const std::shared_ptr<const CanonicalIncludes> CanonIncludes) {
|
||||
auto &Ctx = ASTCtx.getASTContext();
|
||||
auto &PP = ASTCtx.getPreprocessor();
|
||||
Index->updatePreamble(testPath(Input.first()), "null", Ctx, PP,
|
||||
*CanonIncludes);
|
||||
});
|
||||
TU.preamble([&](CapturedASTCtx ASTCtx,
|
||||
std::shared_ptr<const include_cleaner::PragmaIncludes> PI) {
|
||||
auto &Ctx = ASTCtx.getASTContext();
|
||||
auto &PP = ASTCtx.getPreprocessor();
|
||||
Index->updatePreamble(testPath(Input.first()), "null", Ctx, PP, *PI);
|
||||
});
|
||||
ParsedAST MainAST = TU.build();
|
||||
Index->updateMain(testPath(Input.first()), MainAST);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace include_cleaner {
|
||||
|
||||
/// We consider a macro to be a different symbol each time it is defined.
|
||||
struct Macro {
|
||||
IdentifierInfo *Name;
|
||||
const IdentifierInfo *Name;
|
||||
/// The location of the Name where the macro is defined.
|
||||
SourceLocation Definition;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user