22#include "clang/Basic/LangOptions.h"
23#include "clang/Frontend/CompilerInvocation.h"
24#include "clang/Lex/PreprocessorOptions.h"
25#include "clang/Tooling/Inclusions/StandardLibrary.h"
26#include "llvm/ADT/IntrusiveRefCntPtr.h"
27#include "llvm/ADT/StringRef.h"
28#include "llvm/Support/MemoryBuffer.h"
29#include "llvm/Support/Path.h"
37Lang langFromOpts(
const LangOptions &LO) {
return LO.CPlusPlus ? CXX :
C; }
38llvm::StringLiteral mandatoryHeader(
Lang L) {
45 llvm_unreachable(
"unhandled Lang");
48LangStandard::Kind standardFromOpts(
const LangOptions &LO) {
51 return LangStandard::lang_cxx23;
53 return LangStandard::lang_cxx20;
55 return LangStandard::lang_cxx17;
57 return LangStandard::lang_cxx14;
59 return LangStandard::lang_cxx11;
60 return LangStandard::lang_cxx98;
63 return LangStandard::lang_c23;
66 return LangStandard::lang_c17;
67 return LangStandard::lang_c99;
70std::string buildUmbrella(llvm::StringLiteral Mandatory,
71 llvm::ArrayRef<tooling::stdlib::Header> Headers) {
73 llvm::raw_string_ostream
OS(Result);
79 "#if !__has_include(<{0}>)\n"
80 "#error Mandatory header <{0}> not found in standard library!\n"
84 for (
auto Header : Headers) {
85 OS << llvm::formatv(
"#if __has_include({0})\n"
100 Lang L = langFromOpts(LO);
103 static std::string *UmbrellaCXX =
new std::string(buildUmbrella(
105 tooling::stdlib::Header::all(tooling::stdlib::Lang::CXX)));
108 static std::string *UmbrellaC =
new std::string(
109 buildUmbrella(mandatoryHeader(L),
110 tooling::stdlib::Header::all(tooling::stdlib::Lang::C)));
113 llvm_unreachable(
"invalid Lang in langFromOpts");
131SymbolSlab filter(SymbolSlab Slab,
const StdLibLocation &
Loc) {
132 SymbolSlab::Builder Result;
134 static auto &StandardHeaders = *[] {
135 auto *Set =
new llvm::DenseSet<llvm::StringRef>();
136 for (
auto Header : tooling::stdlib::Header::all(tooling::stdlib::Lang::CXX))
137 Set->insert(Header.name());
138 for (
auto Header : tooling::stdlib::Header::all(tooling::stdlib::Lang::C))
139 Set->insert(Header.name());
145 llvm::SmallVector<std::string> StdLibURIPrefixes;
146 for (
const auto &
Path :
Loc.Paths) {
148 if (StdLibURIPrefixes.back().back() !=
'/')
149 StdLibURIPrefixes.back().push_back(
'/');
154 llvm::DenseMap<const char *, bool> GoodHeader;
155 for (
const Symbol &S : Slab) {
156 if (!S.IncludeHeaders.empty() &&
157 StandardHeaders.contains(S.IncludeHeaders.front().IncludeHeader)) {
158 GoodHeader[S.CanonicalDeclaration.FileURI] =
true;
159 GoodHeader[S.Definition.FileURI] =
true;
162 for (
const char *URI :
163 {S.CanonicalDeclaration.FileURI, S.Definition.FileURI}) {
164 auto R = GoodHeader.try_emplace(URI,
false);
166 R.first->second = llvm::any_of(
168 [&, URIStr(llvm::StringRef(URI))](
const std::string &Prefix) {
169 return URIStr.starts_with(Prefix);
175 for (
const auto &Good : GoodHeader)
176 if (Good.second && *Good.first)
177 dlog(
"Stdlib header: {0}", Good.first);
180 auto IsGoodHeader = [&](
const char *
C) {
return *
C && GoodHeader.lookup(
C); };
182 for (
const Symbol &S : Slab) {
183 if (!(IsGoodHeader(S.CanonicalDeclaration.FileURI) ||
184 IsGoodHeader(S.Definition.FileURI))) {
185 dlog(
"Ignoring wrong-header symbol {0}{1} in {2}", S.Scope, S.Name,
186 S.CanonicalDeclaration.FileURI);
192 return std::move(Result).build();
198 std::unique_ptr<CompilerInvocation>
CI,
201 if (
CI->getFrontendOpts().Inputs.size() != 1 ||
202 !
CI->getPreprocessorOpts().ImplicitPCHInclude.empty()) {
203 elog(
"Indexing standard library failed: bad CompilerInvocation");
204 assert(
false &&
"indexing stdlib with a dubious CompilerInvocation!");
207 const FrontendInputFile &Input =
CI->getFrontendOpts().Inputs.front();
209 LangStandard::Kind LangStd = standardFromOpts(
CI->getLangOpts());
210 log(
"Indexing {0} standard library in the context of {1}",
211 LangStandard::getLangStandardForKind(LangStd).getName(), Input.getFile());
216 CI->getPreprocessorOpts().clearRemappedFiles();
218 std::move(
CI),
nullptr,
219 llvm::MemoryBuffer::getMemBuffer(HeaderSources, Input.getFile()),
222 elog(
"Standard Library Index: Couldn't build compiler instance");
243 elog(
"Standard Library Index: BeginSourceFile() failed");
247 if (llvm::Error Err =
Action->Execute()) {
248 elog(
"Standard Library Index: Execute failed: {0}", std::move(Err));
254 unsigned SymbolsBeforeFilter =
Symbols.size();
257 Clang->getDiagnostics().hasUncompilableErrorOccurred();
258 log(
"Indexed {0} standard library{3}: {1} symbols, {2} filtered",
259 LangStandard::getLangStandardForKind(LangStd).getName(),
Symbols.size(),
260 SymbolsBeforeFilter -
Symbols.size(),
261 Errors ?
" (incomplete due to errors)" :
"");
274 return standardFromOpts(LO) >=
275 Best[langFromOpts(LO)].load(std::memory_order_acquire);
279 const HeaderSearch &HS) {
280 Lang L = langFromOpts(LO);
281 int OldVersion = Best[L].load(std::memory_order_acquire);
282 int NewVersion = standardFromOpts(LO);
283 dlog(
"Index stdlib? {0}",
284 LangStandard::getLangStandardForKind(standardFromOpts(LO)).getName());
287 dlog(
"No: disabled in config");
291 if (NewVersion <= OldVersion) {
292 dlog(
"No: have {0}, {1}>={2}",
293 LangStandard::getLangStandardForKind(
294 static_cast<LangStandard::Kind
>(NewVersion))
296 OldVersion, NewVersion);
304 llvm::StringLiteral ProbeHeader = mandatoryHeader(L);
305 llvm::SmallString<256>
Path;
306 llvm::SmallVector<std::string> SearchPaths;
307 auto RecordHeaderPath = [&](llvm::StringRef HeaderPath) {
308 llvm::StringRef DirPath = llvm::sys::path::parent_path(HeaderPath);
309 if (!HS.getFileMgr().getVirtualFileSystem().getRealPath(DirPath,
Path))
310 SearchPaths.emplace_back(
Path);
312 for (
const auto &DL :
313 llvm::make_range(HS.search_dir_begin(), HS.search_dir_end())) {
314 switch (DL.getLookupType()) {
315 case DirectoryLookup::LT_NormalDir: {
316 Path = DL.getDirRef()->getName();
317 llvm::sys::path::append(
Path, ProbeHeader);
318 llvm::vfs::Status Stat;
319 if (!HS.getFileMgr().getNoncachedStatValue(
Path, Stat) &&
320 Stat.isRegularFile())
321 RecordHeaderPath(
Path);
324 case DirectoryLookup::LT_Framework:
327 case DirectoryLookup::LT_HeaderMap:
328 llvm::StringRef Target =
329 DL.getHeaderMap()->lookupFilename(ProbeHeader,
Path);
331 RecordHeaderPath(Target);
335 if (SearchPaths.empty())
338 dlog(
"Found standard library in {0}", llvm::join(SearchPaths,
", "));
340 while (!Best[L].compare_exchange_weak(OldVersion, NewVersion,
341 std::memory_order_acq_rel))
342 if (OldVersion >= NewVersion) {
343 dlog(
"No: lost the race");
348 dlog(
"Yes, index stdlib!");
std::unique_ptr< CompilerInvocation > CI
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
std::optional< StdLibLocation > add(const LangOptions &, const HeaderSearch &)
bool isBest(const LangOptions &) const
An immutable symbol container that stores a set of symbols.
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > view(std::nullopt_t CWD) const
Obtain a vfs::FileSystem with an arbitrary initial working directory.
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Records an event whose duration is the lifetime of the Span object.
std::string Path
A typedef to represent a file path.
std::unique_ptr< CompilerInstance > prepareCompilerInstance(std::unique_ptr< clang::CompilerInvocation > CI, const PrecompiledPreamble *Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, DiagnosticConsumer &DiagsClient)
llvm::StringRef getStdlibUmbrellaHeader(const LangOptions &LO)
std::unique_ptr< FrontendAction > createStaticIndexingAction(SymbolCollector::Options Opts, std::function< void(SymbolSlab)> SymbolsCallback, std::function< void(RefSlab)> RefsCallback, std::function< void(RelationSlab)> RelationsCallback, std::function< void(IncludeGraph)> IncludeGraphCallback)
void log(const char *Fmt, Ts &&... Vals)
void elog(const char *Fmt, Ts &&... Vals)
SymbolSlab indexStandardLibrary(llvm::StringRef HeaderSources, std::unique_ptr< CompilerInvocation > CI, const StdLibLocation &Loc, const ThreadsafeFS &TFS)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static const Config & current()
Returns the Config of the current Context, or an empty configuration.
bool CollectMacro
Collect macros.
bool CollectMainFileSymbols
Collect symbols local to main-files, such as static functions, symbols inside an anonymous namespace,...
bool StoreAllDocumentation
If set to true, SymbolCollector will collect doc for all symbols.
bool CollectMainFileRefs
Collect references to main-file symbols.