18#include "clang-include-cleaner/Record.h"
24#include "clang/Basic/LangOptions.h"
25#include "clang/Frontend/CompilerInvocation.h"
26#include "clang/Frontend/FrontendActions.h"
27#include "clang/Lex/PreprocessorOptions.h"
28#include "clang/Tooling/Inclusions/StandardLibrary.h"
29#include "llvm/ADT/IntrusiveRefCntPtr.h"
30#include "llvm/ADT/StringRef.h"
31#include "llvm/Support/MemoryBuffer.h"
32#include "llvm/Support/Path.h"
40Lang langFromOpts(
const LangOptions &LO) {
return LO.CPlusPlus ? CXX :
C; }
41llvm::StringLiteral mandatoryHeader(Lang L) {
48 llvm_unreachable(
"unhandled Lang");
51LangStandard::Kind standardFromOpts(
const LangOptions &LO) {
54 return LangStandard::lang_cxx23;
56 return LangStandard::lang_cxx20;
58 return LangStandard::lang_cxx17;
60 return LangStandard::lang_cxx14;
62 return LangStandard::lang_cxx11;
63 return LangStandard::lang_cxx98;
66 return LangStandard::lang_c23;
69 return LangStandard::lang_c17;
70 return LangStandard::lang_c99;
73std::string buildUmbrella(llvm::StringLiteral Mandatory,
74 llvm::ArrayRef<tooling::stdlib::Header> Headers) {
76 llvm::raw_string_ostream OS(Result);
82 "#if !__has_include(<{0}>)\n"
83 "#error Mandatory header <{0}> not found in standard library!\n"
87 for (
auto Header : Headers) {
88 OS << llvm::formatv(
"#if __has_include({0})\n"
103 Lang L = langFromOpts(LO);
106 static std::string *UmbrellaCXX =
new std::string(buildUmbrella(
108 tooling::stdlib::Header::all(tooling::stdlib::Lang::CXX)));
111 static std::string *UmbrellaC =
new std::string(
112 buildUmbrella(mandatoryHeader(L),
113 tooling::stdlib::Header::all(tooling::stdlib::Lang::C)));
116 llvm_unreachable(
"invalid Lang in langFromOpts");
134SymbolSlab filter(SymbolSlab Slab,
const StdLibLocation &Loc) {
135 SymbolSlab::Builder Result;
137 static auto &StandardHeaders = *[] {
138 auto *Set =
new llvm::DenseSet<llvm::StringRef>();
139 for (
auto Header : tooling::stdlib::Header::all(tooling::stdlib::Lang::CXX))
140 Set->insert(Header.name());
141 for (
auto Header : tooling::stdlib::Header::all(tooling::stdlib::Lang::C))
142 Set->insert(Header.name());
148 llvm::SmallVector<std::string> StdLibURIPrefixes;
149 for (
const auto &
Path : Loc.Paths) {
151 if (StdLibURIPrefixes.back().back() !=
'/')
152 StdLibURIPrefixes.back().push_back(
'/');
157 llvm::DenseMap<const char *, bool> GoodHeader;
158 for (
const Symbol &S : Slab) {
159 if (!S.IncludeHeaders.empty() &&
160 StandardHeaders.contains(S.IncludeHeaders.front().IncludeHeader)) {
161 GoodHeader[S.CanonicalDeclaration.FileURI] =
true;
162 GoodHeader[S.Definition.FileURI] =
true;
165 for (
const char *
URI :
166 {S.CanonicalDeclaration.FileURI, S.Definition.FileURI}) {
167 auto R = GoodHeader.try_emplace(
URI,
false);
169 R.first->second = llvm::any_of(
171 [&, URIStr(llvm::StringRef(
URI))](
const std::string &Prefix) {
172 return URIStr.starts_with(Prefix);
178 for (
const auto &Good : GoodHeader)
179 if (Good.second && *Good.first)
180 dlog(
"Stdlib header: {0}", Good.first);
183 auto IsGoodHeader = [&](
const char *
C) {
return *
C && GoodHeader.lookup(C); };
185 for (
const Symbol &S : Slab) {
186 if (!(IsGoodHeader(S.CanonicalDeclaration.FileURI) ||
187 IsGoodHeader(S.Definition.FileURI))) {
188 dlog(
"Ignoring wrong-header symbol {0}{1} in {2}", S.Scope, S.Name,
189 S.CanonicalDeclaration.FileURI);
195 return std::move(Result).build();
201 std::unique_ptr<CompilerInvocation> CI,
204 if (CI->getFrontendOpts().Inputs.size() != 1 ||
205 !CI->getPreprocessorOpts().ImplicitPCHInclude.empty()) {
206 elog(
"Indexing standard library failed: bad CompilerInvocation");
207 assert(
false &&
"indexing stdlib with a dubious CompilerInvocation!");
210 const FrontendInputFile &Input = CI->getFrontendOpts().Inputs.front();
212 LangStandard::Kind LangStd = standardFromOpts(CI->getLangOpts());
213 log(
"Indexing {0} standard library in the context of {1}",
214 LangStandard::getLangStandardForKind(LangStd).getName(), Input.getFile());
219 CI->getPreprocessorOpts().clearRemappedFiles();
221 std::move(CI),
nullptr,
222 llvm::MemoryBuffer::getMemBuffer(HeaderSources, Input.getFile()),
223 TFS.
view(std::nullopt), IgnoreDiags);
225 elog(
"Standard Library Index: Couldn't build compiler instance");
231 if (!Action.BeginSourceFile(*Clang, Input)) {
232 elog(
"Standard Library Index: BeginSourceFile() failed");
236 if (llvm::Error Err = Action.Execute()) {
237 elog(
"Standard Library Index: Execute failed: {0}", std::move(Err));
243 include_cleaner::PragmaIncludes PI;
247 Symbols = std::move(std::get<0>(Slabs));
251 Action.EndSourceFile();
253 unsigned SymbolsBeforeFilter =
Symbols.size();
255 bool Errors = Clang->hasDiagnostics() &&
256 Clang->getDiagnostics().hasUncompilableErrorOccurred();
257 log(
"Indexed {0} standard library{3}: {1} symbols, {2} filtered",
258 LangStandard::getLangStandardForKind(LangStd).getName(),
Symbols.size(),
259 SymbolsBeforeFilter -
Symbols.size(),
260 Errors ?
" (incomplete due to errors)" :
"");
273 return standardFromOpts(LO) >=
274 Best[langFromOpts(LO)].load(std::memory_order_acquire);
278 const HeaderSearch &HS) {
279 Lang L = langFromOpts(LO);
280 int OldVersion = Best[L].load(std::memory_order_acquire);
281 int NewVersion = standardFromOpts(LO);
282 dlog(
"Index stdlib? {0}",
283 LangStandard::getLangStandardForKind(standardFromOpts(LO)).getName());
286 dlog(
"No: disabled in config");
290 if (NewVersion <= OldVersion) {
291 dlog(
"No: have {0}, {1}>={2}",
292 LangStandard::getLangStandardForKind(
293 static_cast<LangStandard::Kind
>(NewVersion))
295 OldVersion, NewVersion);
303 llvm::StringLiteral ProbeHeader = mandatoryHeader(L);
304 llvm::SmallString<256>
Path;
305 llvm::SmallVector<std::string> SearchPaths;
306 auto RecordHeaderPath = [&](llvm::StringRef HeaderPath) {
307 llvm::StringRef DirPath = llvm::sys::path::parent_path(HeaderPath);
308 if (!HS.getFileMgr().getVirtualFileSystem().getRealPath(DirPath,
Path))
309 SearchPaths.emplace_back(
Path);
311 for (
const auto &DL :
312 llvm::make_range(HS.search_dir_begin(), HS.search_dir_end())) {
313 switch (DL.getLookupType()) {
314 case DirectoryLookup::LT_NormalDir: {
315 Path = DL.getDirRef()->getName();
316 llvm::sys::path::append(
Path, ProbeHeader);
317 llvm::vfs::Status Stat;
318 if (!HS.getFileMgr().getNoncachedStatValue(
Path, Stat) &&
319 Stat.isRegularFile())
320 RecordHeaderPath(
Path);
323 case DirectoryLookup::LT_Framework:
326 case DirectoryLookup::LT_HeaderMap:
327 llvm::StringRef Target =
328 DL.getHeaderMap()->lookupFilename(ProbeHeader,
Path);
330 RecordHeaderPath(Target);
334 if (SearchPaths.empty())
337 dlog(
"Found standard library in {0}", llvm::join(SearchPaths,
", "));
339 while (!Best[L].compare_exchange_weak(OldVersion, NewVersion,
340 std::memory_order_acq_rel))
341 if (OldVersion >= NewVersion) {
342 dlog(
"No: lost the race");
347 dlog(
"Yes, index stdlib!");
#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.
A URI describes the location of a source file.
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.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
static const char * toString(OffsetEncoding OE)
SlabTuple indexHeaderSymbols(llvm::StringRef Version, ASTContext &AST, Preprocessor &PP, const include_cleaner::PragmaIncludes &PI, SymbolOrigin Origin)
Index declarations from AST and macros from PP that are declared in included headers.
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)
void log(const char *Fmt, Ts &&... Vals)
std::string Path
A typedef to represent a file path.
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.
The class presents a C++ symbol, e.g.