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"
101 Lang L = langFromOpts(LO);
104 static std::string *UmbrellaCXX =
new std::string(buildUmbrella(
106 tooling::stdlib::Header::all(tooling::stdlib::Lang::CXX)));
109 static std::string *UmbrellaC =
new std::string(
110 buildUmbrella(mandatoryHeader(L),
111 tooling::stdlib::Header::all(tooling::stdlib::Lang::C)));
114 llvm_unreachable(
"invalid Lang in langFromOpts");
132SymbolSlab filter(SymbolSlab Slab,
const StdLibLocation &
Loc) {
133 SymbolSlab::Builder Result;
135 static auto &StandardHeaders = *[] {
136 auto *Set =
new llvm::DenseSet<llvm::StringRef>();
137 for (
auto Header : tooling::stdlib::Header::all(tooling::stdlib::Lang::CXX))
138 Set->insert(Header.name());
139 for (
auto Header : tooling::stdlib::Header::all(tooling::stdlib::Lang::C))
140 Set->insert(Header.name());
146 llvm::SmallVector<std::string> StdLibURIPrefixes;
147 for (
const auto &
Path :
Loc.Paths) {
149 if (StdLibURIPrefixes.back().back() !=
'/')
150 StdLibURIPrefixes.back().push_back(
'/');
155 llvm::DenseMap<const char *, bool> GoodHeader;
156 for (
const Symbol &S : Slab) {
157 if (!S.IncludeHeaders.empty() &&
158 StandardHeaders.contains(S.IncludeHeaders.front().IncludeHeader)) {
159 GoodHeader[S.CanonicalDeclaration.FileURI] =
true;
160 GoodHeader[S.Definition.FileURI] =
true;
163 for (
const char *URI :
164 {S.CanonicalDeclaration.FileURI, S.Definition.FileURI}) {
165 auto R = GoodHeader.try_emplace(URI,
false);
167 R.first->second = llvm::any_of(
169 [&, URIStr(llvm::StringRef(URI))](
const std::string &Prefix) {
170 return URIStr.startswith(Prefix);
176 for (
const auto &Good : GoodHeader)
177 if (Good.second && *Good.first)
178 dlog(
"Stdlib header: {0}", Good.first);
181 auto IsGoodHeader = [&](
const char *
C) {
return *
C && GoodHeader.lookup(
C); };
183 for (
const Symbol &S : Slab) {
184 if (!(IsGoodHeader(S.CanonicalDeclaration.FileURI) ||
185 IsGoodHeader(S.Definition.FileURI))) {
186 dlog(
"Ignoring wrong-header symbol {0}{1} in {2}", S.Scope, S.Name,
187 S.CanonicalDeclaration.FileURI);
193 return std::move(Result).build();
199 std::unique_ptr<CompilerInvocation>
CI,
202 if (
CI->getFrontendOpts().Inputs.size() != 1 ||
203 !
CI->getPreprocessorOpts().ImplicitPCHInclude.empty()) {
204 elog(
"Indexing standard library failed: bad CompilerInvocation");
205 assert(
false &&
"indexing stdlib with a dubious CompilerInvocation!");
208 const FrontendInputFile &Input =
CI->getFrontendOpts().Inputs.front();
210 LangStandard::Kind LangStd = standardFromOpts(
CI->getLangOpts());
211 log(
"Indexing {0} standard library in the context of {1}",
212 LangStandard::getLangStandardForKind(LangStd).getName(), Input.getFile());
217 CI->getPreprocessorOpts().clearRemappedFiles();
219 std::move(
CI),
nullptr,
220 llvm::MemoryBuffer::getMemBuffer(HeaderSources, Input.getFile()),
223 elog(
"Standard Library Index: Couldn't build compiler instance");
244 elog(
"Standard Library Index: BeginSourceFile() failed");
248 if (llvm::Error Err =
Action->Execute()) {
249 elog(
"Standard Library Index: Execute failed: {0}", std::move(Err));
255 unsigned SymbolsBeforeFilter =
Symbols.size();
257 bool Errors =
Clang->hasDiagnostics() &&
258 Clang->getDiagnostics().hasUncompilableErrorOccurred();
259 log(
"Indexed {0} standard library{3}: {1} symbols, {2} filtered",
260 LangStandard::getLangStandardForKind(LangStd).getName(),
Symbols.size(),
261 SymbolsBeforeFilter -
Symbols.size(),
262 Errors ?
" (incomplete due to errors)" :
"");
275 return standardFromOpts(LO) >=
276 Best[langFromOpts(LO)].load(std::memory_order_acquire);
280 const HeaderSearch &HS) {
281 Lang L = langFromOpts(LO);
282 int OldVersion = Best[L].load(std::memory_order_acquire);
283 int NewVersion = standardFromOpts(LO);
284 dlog(
"Index stdlib? {0}",
285 LangStandard::getLangStandardForKind(standardFromOpts(LO)).getName());
288 dlog(
"No: disabled in config");
292 if (NewVersion <= OldVersion) {
293 dlog(
"No: have {0}, {1}>={2}",
294 LangStandard::getLangStandardForKind(
295 static_cast<LangStandard::Kind
>(NewVersion))
297 OldVersion, NewVersion);
305 llvm::StringLiteral ProbeHeader = mandatoryHeader(L);
306 llvm::SmallString<256>
Path;
307 llvm::SmallVector<std::string> SearchPaths;
308 auto RecordHeaderPath = [&](llvm::StringRef HeaderPath) {
309 llvm::StringRef DirPath = llvm::sys::path::parent_path(HeaderPath);
310 if (!HS.getFileMgr().getVirtualFileSystem().getRealPath(DirPath,
Path))
311 SearchPaths.emplace_back(
Path);
313 for (
const auto &DL :
314 llvm::make_range(HS.search_dir_begin(), HS.search_dir_end())) {
315 switch (DL.getLookupType()) {
316 case DirectoryLookup::LT_NormalDir: {
317 Path = DL.getDirRef()->getName();
318 llvm::sys::path::append(
Path, ProbeHeader);
319 llvm::vfs::Status Stat;
320 if (!HS.getFileMgr().getNoncachedStatValue(
Path, Stat) &&
321 Stat.isRegularFile())
322 RecordHeaderPath(
Path);
325 case DirectoryLookup::LT_Framework:
328 case DirectoryLookup::LT_HeaderMap:
329 llvm::StringRef Target =
330 DL.getHeaderMap()->lookupFilename(ProbeHeader,
Path);
332 RecordHeaderPath(Target);
336 if (SearchPaths.empty())
339 dlog(
"Found standard library in {0}", llvm::join(SearchPaths,
", "));
341 while (!Best[L].compare_exchange_weak(OldVersion, NewVersion,
342 std::memory_order_acq_rel))
343 if (OldVersion >= NewVersion) {
344 dlog(
"No: lost the race");
349 dlog(
"Yes, index stdlib!");
std::unique_ptr< CompilerInvocation > CI
llvm::raw_string_ostream OS
#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.