/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- StandardLibrary.cpp ------------------------------------*- C++ -*-===// |
2 | | // |
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 |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | |
9 | | #include "clang/Tooling/Inclusions/StandardLibrary.h" |
10 | | #include "clang/AST/Decl.h" |
11 | | #include "clang/Basic/LangOptions.h" |
12 | | #include "llvm/ADT/ArrayRef.h" |
13 | | #include "llvm/ADT/DenseSet.h" |
14 | | #include "llvm/ADT/STLExtras.h" |
15 | | #include "llvm/ADT/StringRef.h" |
16 | | #include "llvm/Support/Casting.h" |
17 | | #include <optional> |
18 | | |
19 | | namespace clang { |
20 | | namespace tooling { |
21 | | namespace stdlib { |
22 | | |
23 | | namespace { |
24 | | // Symbol name -> Symbol::ID, within a namespace. |
25 | | using NSSymbolMap = llvm::DenseMap<llvm::StringRef, unsigned>; |
26 | | |
27 | | // A Mapping per language. |
28 | | struct SymbolHeaderMapping { |
29 | | llvm::StringRef *HeaderNames = nullptr; |
30 | | // Header name => Header::ID |
31 | | llvm::DenseMap<llvm::StringRef, unsigned> *HeaderIDs; |
32 | | |
33 | | unsigned SymbolCount = 0; |
34 | | // Symbol::ID => symbol qualified_name/name/scope |
35 | | struct SymbolName { |
36 | | const char *Data; // std::vector |
37 | | unsigned ScopeLen; // ~~~~~ |
38 | | unsigned NameLen; // ~~~~~~ |
39 | 2 | StringRef scope() const { return StringRef(Data, ScopeLen); } |
40 | 2 | StringRef name() const { return StringRef(Data + ScopeLen, NameLen); } |
41 | 4.19M | StringRef qualifiedName() const { |
42 | 4.19M | return StringRef(Data, ScopeLen + NameLen); |
43 | 4.19M | } |
44 | | } *SymbolNames = nullptr; |
45 | | // Symbol name -> Symbol::ID, within a namespace. |
46 | | llvm::DenseMap<llvm::StringRef, NSSymbolMap *> *NamespaceSymbols = nullptr; |
47 | | // Symbol::ID => Header::ID |
48 | | llvm::SmallVector<unsigned> *SymbolHeaderIDs = nullptr; |
49 | | }; |
50 | | } // namespace |
51 | | static SymbolHeaderMapping |
52 | | *LanguageMappings[static_cast<unsigned>(Lang::LastValue) + 1]; |
53 | 70 | static const SymbolHeaderMapping *getMappingPerLang(Lang L) { |
54 | 70 | return LanguageMappings[static_cast<unsigned>(L)]; |
55 | 70 | } |
56 | | |
57 | 8 | static int countSymbols(Lang Language) { |
58 | 8 | llvm::DenseSet<llvm::StringRef> Set; |
59 | 20.6k | #define SYMBOL(Name, NS, Header) Set.insert(#NS #Name); |
60 | 8 | switch (Language) { |
61 | 4 | case Lang::C: |
62 | 4 | #include "CSymbolMap.inc" |
63 | 4 | break; |
64 | 4 | case Lang::CXX: |
65 | 4 | #include "StdSpecialSymbolMap.inc" |
66 | 4 | #include "StdSymbolMap.inc" |
67 | 4 | #include "StdTsSymbolMap.inc" |
68 | 4 | break; |
69 | 8 | } |
70 | 8 | #undef SYMBOL |
71 | 8 | return Set.size(); |
72 | 8 | } |
73 | | |
74 | 8 | static int initialize(Lang Language) { |
75 | 8 | SymbolHeaderMapping *Mapping = new SymbolHeaderMapping(); |
76 | 8 | LanguageMappings[static_cast<unsigned>(Language)] = Mapping; |
77 | | |
78 | 8 | unsigned SymCount = countSymbols(Language); |
79 | 8 | Mapping->SymbolCount = SymCount; |
80 | 8 | Mapping->SymbolNames = |
81 | 8 | new std::remove_reference_t<decltype(*Mapping->SymbolNames)>[SymCount]; |
82 | 8 | Mapping->SymbolHeaderIDs = new std::remove_reference_t< |
83 | 8 | decltype(*Mapping->SymbolHeaderIDs)>[SymCount]; |
84 | 8 | Mapping->NamespaceSymbols = |
85 | 8 | new std::remove_reference_t<decltype(*Mapping->NamespaceSymbols)>; |
86 | 8 | Mapping->HeaderIDs = |
87 | 8 | new std::remove_reference_t<decltype(*Mapping->HeaderIDs)>; |
88 | 20.6k | auto AddNS = [&](llvm::StringRef NS) -> NSSymbolMap & { |
89 | 20.6k | auto R = Mapping->NamespaceSymbols->try_emplace(NS, nullptr); |
90 | 20.6k | if (R.second) |
91 | 52 | R.first->second = new NSSymbolMap(); |
92 | 20.6k | return *R.first->second; |
93 | 20.6k | }; |
94 | | |
95 | 20.6k | auto AddHeader = [&](llvm::StringRef Header) -> unsigned { |
96 | 20.6k | return Mapping->HeaderIDs->try_emplace(Header, Mapping->HeaderIDs->size()) |
97 | 20.6k | .first->second; |
98 | 20.6k | }; |
99 | | |
100 | 8 | auto Add = [&, SymIndex(-1)](llvm::StringRef QName, unsigned NSLen, |
101 | 20.6k | llvm::StringRef HeaderName) mutable { |
102 | | // Correct "Nonefoo" => foo. |
103 | | // FIXME: get rid of "None" from the generated mapping files. |
104 | 20.6k | if (QName.take_front(NSLen) == "None") { |
105 | 9.95k | QName = QName.drop_front(NSLen); |
106 | 9.95k | NSLen = 0; |
107 | 9.95k | } |
108 | | |
109 | 20.6k | if (SymIndex >= 0 && |
110 | 20.6k | Mapping->SymbolNames[SymIndex].qualifiedName() == QName20.6k ) { |
111 | | // Not a new symbol, use the same index. |
112 | 4.22k | assert(llvm::none_of(llvm::ArrayRef(Mapping->SymbolNames, SymIndex), |
113 | 4.22k | [&QName](const SymbolHeaderMapping::SymbolName &S) { |
114 | 4.22k | return S.qualifiedName() == QName; |
115 | 4.22k | }) && |
116 | 4.22k | "The symbol has been added before, make sure entries in the .inc " |
117 | 4.22k | "file are grouped by symbol name!"); |
118 | 16.3k | } else { |
119 | | // First symbol or new symbol, increment next available index. |
120 | 16.3k | ++SymIndex; |
121 | 16.3k | } |
122 | 20.6k | Mapping->SymbolNames[SymIndex] = { |
123 | 20.6k | QName.data(), NSLen, static_cast<unsigned int>(QName.size() - NSLen)}; |
124 | 20.6k | if (!HeaderName.empty()) |
125 | 20.6k | Mapping->SymbolHeaderIDs[SymIndex].push_back(AddHeader(HeaderName)); |
126 | | |
127 | 20.6k | NSSymbolMap &NSSymbols = AddNS(QName.take_front(NSLen)); |
128 | 20.6k | NSSymbols.try_emplace(QName.drop_front(NSLen), SymIndex); |
129 | 20.6k | }; |
130 | 20.6k | #define SYMBOL(Name, NS, Header) Add(#NS #Name, strlen(#NS), #Header); |
131 | 8 | switch (Language) { |
132 | 4 | case Lang::C: |
133 | 4 | #include "CSymbolMap.inc" |
134 | 4 | break; |
135 | 4 | case Lang::CXX: |
136 | 4 | #include "StdSpecialSymbolMap.inc" |
137 | 4 | #include "StdSymbolMap.inc" |
138 | 4 | #include "StdTsSymbolMap.inc" |
139 | 4 | break; |
140 | 8 | } |
141 | 8 | #undef SYMBOL |
142 | | |
143 | 8 | Mapping->HeaderNames = new llvm::StringRef[Mapping->HeaderIDs->size()]; |
144 | 8 | for (const auto &E : *Mapping->HeaderIDs) |
145 | 596 | Mapping->HeaderNames[E.second] = E.first; |
146 | | |
147 | 8 | return 0; |
148 | 8 | } |
149 | | |
150 | 48 | static void ensureInitialized() { |
151 | 48 | static int Dummy = []() { |
152 | 12 | for (unsigned L = 0; L <= static_cast<unsigned>(Lang::LastValue); ++L8 ) |
153 | 8 | initialize(static_cast<Lang>(L)); |
154 | 4 | return 0; |
155 | 4 | }(); |
156 | 48 | (void)Dummy; |
157 | 48 | } |
158 | | |
159 | 1 | std::vector<Header> Header::all(Lang L) { |
160 | 1 | ensureInitialized(); |
161 | 1 | std::vector<Header> Result; |
162 | 1 | const auto *Mapping = getMappingPerLang(L); |
163 | 1 | Result.reserve(Mapping->HeaderIDs->size()); |
164 | 123 | for (unsigned I = 0, E = Mapping->HeaderIDs->size(); I < E; ++I122 ) |
165 | 122 | Result.push_back(Header(I, L)); |
166 | 1 | return Result; |
167 | 1 | } |
168 | 25 | std::optional<Header> Header::named(llvm::StringRef Name, Lang L) { |
169 | 25 | ensureInitialized(); |
170 | 25 | const auto *Mapping = getMappingPerLang(L); |
171 | 25 | auto It = Mapping->HeaderIDs->find(Name); |
172 | 25 | if (It == Mapping->HeaderIDs->end()) |
173 | 4 | return std::nullopt; |
174 | 21 | return Header(It->second, L); |
175 | 25 | } |
176 | 2 | llvm::StringRef Header::name() const { |
177 | 2 | return getMappingPerLang(Language)->HeaderNames[ID]; |
178 | 2 | } |
179 | | |
180 | 1 | std::vector<Symbol> Symbol::all(Lang L) { |
181 | 1 | ensureInitialized(); |
182 | 1 | std::vector<Symbol> Result; |
183 | 1 | const auto *Mapping = getMappingPerLang(L); |
184 | 1 | Result.reserve(Mapping->SymbolCount); |
185 | 3.16k | for (unsigned I = 0, E = Mapping->SymbolCount; I < E; ++I3.16k ) |
186 | 3.16k | Result.push_back(Symbol(I, L)); |
187 | 1 | return Result; |
188 | 1 | } |
189 | 2 | llvm::StringRef Symbol::scope() const { |
190 | 2 | return getMappingPerLang(Language)->SymbolNames[ID].scope(); |
191 | 2 | } |
192 | 2 | llvm::StringRef Symbol::name() const { |
193 | 2 | return getMappingPerLang(Language)->SymbolNames[ID].name(); |
194 | 2 | } |
195 | 3 | llvm::StringRef Symbol::qualifiedName() const { |
196 | 3 | return getMappingPerLang(Language)->SymbolNames[ID].qualifiedName(); |
197 | 3 | } |
198 | | std::optional<Symbol> Symbol::named(llvm::StringRef Scope, llvm::StringRef Name, |
199 | 20 | Lang L) { |
200 | 20 | ensureInitialized(); |
201 | | |
202 | 20 | if (NSSymbolMap *NSSymbols = |
203 | 20 | getMappingPerLang(L)->NamespaceSymbols->lookup(Scope)) { |
204 | 17 | auto It = NSSymbols->find(Name); |
205 | 17 | if (It != NSSymbols->end()) |
206 | 16 | return Symbol(It->second, L); |
207 | 17 | } |
208 | 4 | return std::nullopt; |
209 | 20 | } |
210 | 4 | std::optional<Header> Symbol::header() const { |
211 | 4 | const auto& Headers = getMappingPerLang(Language)->SymbolHeaderIDs[ID]; |
212 | 4 | if (Headers.empty()) |
213 | 1 | return std::nullopt; |
214 | 3 | return Header(Headers.front(), Language); |
215 | 4 | } |
216 | 6 | llvm::SmallVector<Header> Symbol::headers() const { |
217 | 6 | llvm::SmallVector<Header> Results; |
218 | 6 | for (auto HeaderID : getMappingPerLang(Language)->SymbolHeaderIDs[ID]) |
219 | 15 | Results.emplace_back(Header(HeaderID, Language)); |
220 | 6 | return Results; |
221 | 6 | } |
222 | | |
223 | 1 | Recognizer::Recognizer() { ensureInitialized(); } |
224 | | |
225 | 7 | NSSymbolMap *Recognizer::namespaceSymbols(const DeclContext *DC, Lang L) { |
226 | 7 | if (DC->isTranslationUnit()) // global scope. |
227 | 2 | return getMappingPerLang(L)->NamespaceSymbols->lookup(""); |
228 | | |
229 | 5 | auto It = NamespaceCache.find(DC); |
230 | 5 | if (It != NamespaceCache.end()) |
231 | 3 | return It->second; |
232 | 2 | const NamespaceDecl *D = llvm::cast<NamespaceDecl>(DC); |
233 | 2 | NSSymbolMap *Result = [&]() -> NSSymbolMap * { |
234 | 2 | if (D->isAnonymousNamespace()) |
235 | 0 | return nullptr; |
236 | | // Print the namespace and its parents ommitting inline scopes. |
237 | 2 | std::string Scope; |
238 | 8 | for (const auto *ND = D; ND; |
239 | 6 | ND = llvm::dyn_cast_or_null<NamespaceDecl>(ND->getParent())) |
240 | 6 | if (!ND->isInlineNamespace() && !ND->isAnonymousNamespace()3 ) |
241 | 3 | Scope = ND->getName().str() + "::" + Scope; |
242 | 2 | return getMappingPerLang(L)->NamespaceSymbols->lookup(Scope); |
243 | 2 | }(); |
244 | 2 | NamespaceCache.try_emplace(D, Result); |
245 | 2 | return Result; |
246 | 5 | } |
247 | | |
248 | 7 | std::optional<Symbol> Recognizer::operator()(const Decl *D) { |
249 | 7 | Lang L; |
250 | 7 | if (D->getLangOpts().CPlusPlus) { |
251 | 7 | L = Lang::CXX; |
252 | 7 | } else if (0 D->getLangOpts().C110 ) { |
253 | 0 | L = Lang::C; |
254 | 0 | } else { |
255 | 0 | return std::nullopt; // not a supported language. |
256 | 0 | } |
257 | | |
258 | | // If D is std::vector::iterator, `vector` is the outer symbol to look up. |
259 | | // We keep all the candidate DCs as some may turn out to be anon enums. |
260 | | // Do this resolution lazily as we may turn out not to have a std namespace. |
261 | 7 | llvm::SmallVector<const DeclContext *> IntermediateDecl; |
262 | 7 | const DeclContext *DC = D->getDeclContext(); |
263 | 7 | if (!DC) // The passed D is a TranslationUnitDecl! |
264 | 0 | return std::nullopt; |
265 | 8 | while (7 !DC->isNamespace() && !DC->isTranslationUnit()3 ) { |
266 | 1 | if (NamedDecl::classofKind(DC->getDeclKind())) |
267 | 1 | IntermediateDecl.push_back(DC); |
268 | 1 | DC = DC->getParent(); |
269 | 1 | } |
270 | 7 | NSSymbolMap *Symbols = namespaceSymbols(DC, L); |
271 | 7 | if (!Symbols) |
272 | 0 | return std::nullopt; |
273 | | |
274 | 7 | llvm::StringRef Name = [&]() -> llvm::StringRef { |
275 | 7 | for (const auto *SymDC : llvm::reverse(IntermediateDecl)) { |
276 | 1 | DeclarationName N = cast<NamedDecl>(SymDC)->getDeclName(); |
277 | 1 | if (const auto *II = N.getAsIdentifierInfo()) |
278 | 1 | return II->getName(); |
279 | 0 | if (!N.isEmpty()) |
280 | 0 | return ""; // e.g. operator<: give up |
281 | 0 | } |
282 | 6 | if (const auto *ND = llvm::dyn_cast<NamedDecl>(D)) |
283 | 6 | if (const auto *II = ND->getIdentifier()) |
284 | 6 | return II->getName(); |
285 | 0 | return ""; |
286 | 6 | }(); |
287 | 7 | if (Name.empty()) |
288 | 0 | return std::nullopt; |
289 | | |
290 | 7 | auto It = Symbols->find(Name); |
291 | 7 | if (It == Symbols->end()) |
292 | 2 | return std::nullopt; |
293 | 5 | return Symbol(It->second, L); |
294 | 7 | } |
295 | | |
296 | | } // namespace stdlib |
297 | | } // namespace tooling |
298 | | } // namespace clang |