1 //===-- StdLib.cpp ----------------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
17 #include "SymbolCollector.h"
18 #include "index/IndexAction.h"
19 #include "support/Logger.h"
20 #include "support/ThreadsafeFS.h"
21 #include "support/Trace.h"
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"
37 Lang
langFromOpts(const LangOptions
&LO
) { return LO
.CPlusPlus
? CXX
: C
; }
38 llvm::StringLiteral
mandatoryHeader(Lang L
) {
45 llvm_unreachable("unhandled Lang");
48 LangStandard::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
;
64 // C17 has no new features, so treat {C11,C17} as C17.
66 return LangStandard::lang_c17
;
67 return LangStandard::lang_c99
;
70 std::string
buildUmbrella(llvm::StringLiteral Mandatory
,
71 llvm::ArrayRef
<tooling::stdlib::Header
> Headers
) {
73 llvm::raw_string_ostream
OS(Result
);
75 // We __has_include guard all our #includes to avoid errors when using older
76 // stdlib version that don't have headers for the newest language standards.
77 // But make sure we get *some* error if things are totally broken.
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"
96 llvm::StringRef
getStdlibUmbrellaHeader(const LangOptions
&LO
) {
97 // The umbrella header is the same for all versions of each language.
98 // Headers that are unsupported in old lang versions are usually guarded by
99 // #if. Some headers may be not present in old stdlib versions, the umbrella
100 // header guards with __has_include for this purpose.
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");
119 // Including the standard library leaks unwanted transitively included symbols.
121 // We want to drop these, they're a bit tricky to identify:
122 // - we don't want to limit to symbols on our list, as our list has only
123 // top-level symbols (and there may be legitimate stdlib extensions).
124 // - we can't limit to only symbols defined in known stdlib headers, as stdlib
125 // internal structure is murky
126 // - we can't strictly require symbols to come from a particular path, e.g.
127 // libstdc++ is mostly under /usr/include/c++/10/...
128 // but std::ctype_base is under /usr/include/<platform>/c++/10/...
129 // We require the symbol to come from a header that is *either* from
130 // the standard library path (as identified by the location of <vector>), or
131 // another header that defines a symbol from our stdlib list.
132 SymbolSlab
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());
144 // Form prefixes like file:///usr/include/c++/10/
145 // These can be trivially prefix-compared with URIs in the indexed symbols.
146 llvm::SmallVector
<std::string
> StdLibURIPrefixes
;
147 for (const auto &Path
: Loc
.Paths
) {
148 StdLibURIPrefixes
.push_back(URI::create(Path
).toString());
149 if (StdLibURIPrefixes
.back().back() != '/')
150 StdLibURIPrefixes
.back().push_back('/');
152 // For each header URI, is it *either* prefixed by StdLibURIPrefixes *or*
153 // owner of a symbol whose insertable header is in StandardHeaders?
154 // Pointer key because strings in a SymbolSlab are interned.
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
);
180 // Empty URIs aren't considered good. (Definition can be blank).
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();
198 SymbolSlab
indexStandardLibrary(llvm::StringRef HeaderSources
,
199 std::unique_ptr
<CompilerInvocation
> CI
,
200 const StdLibLocation
&Loc
,
201 const ThreadsafeFS
&TFS
) {
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();
209 trace::Span
Tracer("StandardLibraryIndex");
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());
215 IgnoreDiagnostics IgnoreDiags
;
216 // CompilerInvocation is taken from elsewhere, and may map a dirty buffer.
217 CI
->getPreprocessorOpts().clearRemappedFiles();
218 auto Clang
= prepareCompilerInstance(
219 std::move(CI
), /*Preamble=*/nullptr,
220 llvm::MemoryBuffer::getMemBuffer(HeaderSources
, Input
.getFile()),
221 TFS
.view(/*CWD=*/std::nullopt
), IgnoreDiags
);
223 elog("Standard Library Index: Couldn't build compiler instance");
227 SymbolCollector::Options IndexOpts
;
228 IndexOpts
.Origin
= SymbolOrigin::StdLib
;
229 IndexOpts
.CollectMainFileSymbols
= false;
230 IndexOpts
.CollectMainFileRefs
= false;
231 IndexOpts
.CollectMacro
= true;
232 IndexOpts
.StoreAllDocumentation
= true;
233 // Sadly we can't use IndexOpts.FileFilter to restrict indexing scope.
234 // Files from outside the StdLibLocation may define true std symbols anyway.
235 // We end up "blessing" such headers, and can only do that by indexing
238 // Refs, relations, include graph in the stdlib mostly aren't useful.
239 auto Action
= createStaticIndexingAction(
240 IndexOpts
, [&](SymbolSlab S
) { Symbols
= std::move(S
); }, nullptr,
243 if (!Action
->BeginSourceFile(*Clang
, Input
)) {
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
));
253 Action
->EndSourceFile();
255 unsigned SymbolsBeforeFilter
= Symbols
.size();
256 Symbols
= filter(std::move(Symbols
), Loc
);
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)" : "");
263 SPAN_ATTACH(Tracer
, "symbols", int(Symbols
.size()));
267 SymbolSlab
indexStandardLibrary(std::unique_ptr
<CompilerInvocation
> Invocation
,
268 const StdLibLocation
&Loc
,
269 const ThreadsafeFS
&TFS
) {
270 llvm::StringRef Header
= getStdlibUmbrellaHeader(Invocation
->getLangOpts());
271 return indexStandardLibrary(Header
, std::move(Invocation
), Loc
, TFS
);
274 bool StdLibSet::isBest(const LangOptions
&LO
) const {
275 return standardFromOpts(LO
) >=
276 Best
[langFromOpts(LO
)].load(std::memory_order_acquire
);
279 std::optional
<StdLibLocation
> StdLibSet::add(const LangOptions
&LO
,
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());
287 if (!Config::current().Index
.StandardLibrary
) {
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
);
301 // We'd like to index a standard library here if there is one.
302 // Check for the existence of <vector> on the search path.
303 // We could cache this, but we only get here repeatedly when there's no
304 // stdlib, and even then only once per preamble build.
305 llvm::StringLiteral ProbeHeader
= mandatoryHeader(L
);
306 llvm::SmallString
<256> Path
; // Scratch space.
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
:
326 // stdlib can't be a framework (framework includes must have a slash)
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");
345 return std::nullopt
; // Another thread won the race while we were
349 dlog("Yes, index stdlib!");
350 return StdLibLocation
{std::move(SearchPaths
)};
353 } // namespace clangd