/src/llvm-project/clang/lib/Basic/DiagnosticIDs.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- DiagnosticIDs.cpp - Diagnostic IDs Handling ----------------------===// |
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 | | // This file implements the Diagnostic IDs-related interfaces. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "clang/Basic/DiagnosticIDs.h" |
14 | | #include "clang/Basic/AllDiagnostics.h" |
15 | | #include "clang/Basic/DiagnosticCategories.h" |
16 | | #include "clang/Basic/SourceManager.h" |
17 | | #include "llvm/ADT/STLExtras.h" |
18 | | #include "llvm/ADT/SmallVector.h" |
19 | | #include "llvm/Support/ErrorHandling.h" |
20 | | #include <map> |
21 | | #include <optional> |
22 | | using namespace clang; |
23 | | |
24 | | //===----------------------------------------------------------------------===// |
25 | | // Builtin Diagnostic information |
26 | | //===----------------------------------------------------------------------===// |
27 | | |
28 | | namespace { |
29 | | |
30 | | struct StaticDiagInfoRec; |
31 | | |
32 | | // Store the descriptions in a separate table to avoid pointers that need to |
33 | | // be relocated, and also decrease the amount of data needed on 64-bit |
34 | | // platforms. See "How To Write Shared Libraries" by Ulrich Drepper. |
35 | | struct StaticDiagInfoDescriptionStringTable { |
36 | | #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ |
37 | | SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ |
38 | | char ENUM##_desc[sizeof(DESC)]; |
39 | | // clang-format off |
40 | | #include "clang/Basic/DiagnosticCommonKinds.inc" |
41 | | #include "clang/Basic/DiagnosticDriverKinds.inc" |
42 | | #include "clang/Basic/DiagnosticFrontendKinds.inc" |
43 | | #include "clang/Basic/DiagnosticSerializationKinds.inc" |
44 | | #include "clang/Basic/DiagnosticLexKinds.inc" |
45 | | #include "clang/Basic/DiagnosticParseKinds.inc" |
46 | | #include "clang/Basic/DiagnosticASTKinds.inc" |
47 | | #include "clang/Basic/DiagnosticCommentKinds.inc" |
48 | | #include "clang/Basic/DiagnosticCrossTUKinds.inc" |
49 | | #include "clang/Basic/DiagnosticSemaKinds.inc" |
50 | | #include "clang/Basic/DiagnosticAnalysisKinds.inc" |
51 | | #include "clang/Basic/DiagnosticRefactoringKinds.inc" |
52 | | // clang-format on |
53 | | #undef DIAG |
54 | | }; |
55 | | |
56 | | const StaticDiagInfoDescriptionStringTable StaticDiagInfoDescriptions = { |
57 | | #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ |
58 | | SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ |
59 | | DESC, |
60 | | // clang-format off |
61 | | #include "clang/Basic/DiagnosticCommonKinds.inc" |
62 | | #include "clang/Basic/DiagnosticDriverKinds.inc" |
63 | | #include "clang/Basic/DiagnosticFrontendKinds.inc" |
64 | | #include "clang/Basic/DiagnosticSerializationKinds.inc" |
65 | | #include "clang/Basic/DiagnosticLexKinds.inc" |
66 | | #include "clang/Basic/DiagnosticParseKinds.inc" |
67 | | #include "clang/Basic/DiagnosticASTKinds.inc" |
68 | | #include "clang/Basic/DiagnosticCommentKinds.inc" |
69 | | #include "clang/Basic/DiagnosticCrossTUKinds.inc" |
70 | | #include "clang/Basic/DiagnosticSemaKinds.inc" |
71 | | #include "clang/Basic/DiagnosticAnalysisKinds.inc" |
72 | | #include "clang/Basic/DiagnosticRefactoringKinds.inc" |
73 | | // clang-format on |
74 | | #undef DIAG |
75 | | }; |
76 | | |
77 | | extern const StaticDiagInfoRec StaticDiagInfo[]; |
78 | | |
79 | | // Stored separately from StaticDiagInfoRec to pack better. Otherwise, |
80 | | // StaticDiagInfoRec would have extra padding on 64-bit platforms. |
81 | | const uint32_t StaticDiagInfoDescriptionOffsets[] = { |
82 | | #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ |
83 | | SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ |
84 | | offsetof(StaticDiagInfoDescriptionStringTable, ENUM##_desc), |
85 | | // clang-format off |
86 | | #include "clang/Basic/DiagnosticCommonKinds.inc" |
87 | | #include "clang/Basic/DiagnosticDriverKinds.inc" |
88 | | #include "clang/Basic/DiagnosticFrontendKinds.inc" |
89 | | #include "clang/Basic/DiagnosticSerializationKinds.inc" |
90 | | #include "clang/Basic/DiagnosticLexKinds.inc" |
91 | | #include "clang/Basic/DiagnosticParseKinds.inc" |
92 | | #include "clang/Basic/DiagnosticASTKinds.inc" |
93 | | #include "clang/Basic/DiagnosticCommentKinds.inc" |
94 | | #include "clang/Basic/DiagnosticCrossTUKinds.inc" |
95 | | #include "clang/Basic/DiagnosticSemaKinds.inc" |
96 | | #include "clang/Basic/DiagnosticAnalysisKinds.inc" |
97 | | #include "clang/Basic/DiagnosticRefactoringKinds.inc" |
98 | | // clang-format on |
99 | | #undef DIAG |
100 | | }; |
101 | | |
102 | | // Diagnostic classes. |
103 | | enum { |
104 | | CLASS_NOTE = 0x01, |
105 | | CLASS_REMARK = 0x02, |
106 | | CLASS_WARNING = 0x03, |
107 | | CLASS_EXTENSION = 0x04, |
108 | | CLASS_ERROR = 0x05 |
109 | | }; |
110 | | |
111 | | struct StaticDiagInfoRec { |
112 | | uint16_t DiagID; |
113 | | uint8_t DefaultSeverity : 3; |
114 | | uint8_t Class : 3; |
115 | | uint8_t SFINAE : 2; |
116 | | uint8_t Category : 6; |
117 | | uint8_t WarnNoWerror : 1; |
118 | | uint8_t WarnShowInSystemHeader : 1; |
119 | | uint8_t WarnShowInSystemMacro : 1; |
120 | | |
121 | | uint16_t OptionGroupIndex : 15; |
122 | | uint16_t Deferrable : 1; |
123 | | |
124 | | uint16_t DescriptionLen; |
125 | | |
126 | 0 | unsigned getOptionGroupIndex() const { |
127 | 0 | return OptionGroupIndex; |
128 | 0 | } |
129 | | |
130 | 0 | StringRef getDescription() const { |
131 | 0 | size_t MyIndex = this - &StaticDiagInfo[0]; |
132 | 0 | uint32_t StringOffset = StaticDiagInfoDescriptionOffsets[MyIndex]; |
133 | 0 | const char* Table = reinterpret_cast<const char*>(&StaticDiagInfoDescriptions); |
134 | 0 | return StringRef(&Table[StringOffset], DescriptionLen); |
135 | 0 | } |
136 | | |
137 | 0 | diag::Flavor getFlavor() const { |
138 | 0 | return Class == CLASS_REMARK ? diag::Flavor::Remark |
139 | 0 | : diag::Flavor::WarningOrError; |
140 | 0 | } |
141 | | |
142 | 0 | bool operator<(const StaticDiagInfoRec &RHS) const { |
143 | 0 | return DiagID < RHS.DiagID; |
144 | 0 | } |
145 | | }; |
146 | | |
147 | | #define STRINGIFY_NAME(NAME) #NAME |
148 | | #define VALIDATE_DIAG_SIZE(NAME) \ |
149 | | static_assert( \ |
150 | | static_cast<unsigned>(diag::NUM_BUILTIN_##NAME##_DIAGNOSTICS) < \ |
151 | | static_cast<unsigned>(diag::DIAG_START_##NAME) + \ |
152 | | static_cast<unsigned>(diag::DIAG_SIZE_##NAME), \ |
153 | | STRINGIFY_NAME( \ |
154 | | DIAG_SIZE_##NAME) " is insufficient to contain all " \ |
155 | | "diagnostics, it may need to be made larger in " \ |
156 | | "DiagnosticIDs.h."); |
157 | | VALIDATE_DIAG_SIZE(COMMON) |
158 | | VALIDATE_DIAG_SIZE(DRIVER) |
159 | | VALIDATE_DIAG_SIZE(FRONTEND) |
160 | | VALIDATE_DIAG_SIZE(SERIALIZATION) |
161 | | VALIDATE_DIAG_SIZE(LEX) |
162 | | VALIDATE_DIAG_SIZE(PARSE) |
163 | | VALIDATE_DIAG_SIZE(AST) |
164 | | VALIDATE_DIAG_SIZE(COMMENT) |
165 | | VALIDATE_DIAG_SIZE(CROSSTU) |
166 | | VALIDATE_DIAG_SIZE(SEMA) |
167 | | VALIDATE_DIAG_SIZE(ANALYSIS) |
168 | | VALIDATE_DIAG_SIZE(REFACTORING) |
169 | | #undef VALIDATE_DIAG_SIZE |
170 | | #undef STRINGIFY_NAME |
171 | | |
172 | | const StaticDiagInfoRec StaticDiagInfo[] = { |
173 | | // clang-format off |
174 | | #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ |
175 | | SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ |
176 | | { \ |
177 | | diag::ENUM, \ |
178 | | DEFAULT_SEVERITY, \ |
179 | | CLASS, \ |
180 | | DiagnosticIDs::SFINAE, \ |
181 | | CATEGORY, \ |
182 | | NOWERROR, \ |
183 | | SHOWINSYSHEADER, \ |
184 | | SHOWINSYSMACRO, \ |
185 | | GROUP, \ |
186 | | DEFERRABLE, \ |
187 | | STR_SIZE(DESC, uint16_t)}, |
188 | | #include "clang/Basic/DiagnosticCommonKinds.inc" |
189 | | #include "clang/Basic/DiagnosticDriverKinds.inc" |
190 | | #include "clang/Basic/DiagnosticFrontendKinds.inc" |
191 | | #include "clang/Basic/DiagnosticSerializationKinds.inc" |
192 | | #include "clang/Basic/DiagnosticLexKinds.inc" |
193 | | #include "clang/Basic/DiagnosticParseKinds.inc" |
194 | | #include "clang/Basic/DiagnosticASTKinds.inc" |
195 | | #include "clang/Basic/DiagnosticCommentKinds.inc" |
196 | | #include "clang/Basic/DiagnosticCrossTUKinds.inc" |
197 | | #include "clang/Basic/DiagnosticSemaKinds.inc" |
198 | | #include "clang/Basic/DiagnosticAnalysisKinds.inc" |
199 | | #include "clang/Basic/DiagnosticRefactoringKinds.inc" |
200 | | // clang-format on |
201 | | #undef DIAG |
202 | | }; |
203 | | |
204 | | } // namespace |
205 | | |
206 | | static const unsigned StaticDiagInfoSize = std::size(StaticDiagInfo); |
207 | | |
208 | | /// GetDiagInfo - Return the StaticDiagInfoRec entry for the specified DiagID, |
209 | | /// or null if the ID is invalid. |
210 | 120M | static const StaticDiagInfoRec *GetDiagInfo(unsigned DiagID) { |
211 | | // Out of bounds diag. Can't be in the table. |
212 | 120M | using namespace diag; |
213 | 120M | if (DiagID >= DIAG_UPPER_LIMIT || DiagID <= DIAG_START_COMMON) |
214 | 0 | return nullptr; |
215 | | |
216 | | // Compute the index of the requested diagnostic in the static table. |
217 | | // 1. Add the number of diagnostics in each category preceding the |
218 | | // diagnostic and of the category the diagnostic is in. This gives us |
219 | | // the offset of the category in the table. |
220 | | // 2. Subtract the number of IDs in each category from our ID. This gives us |
221 | | // the offset of the diagnostic in the category. |
222 | | // This is cheaper than a binary search on the table as it doesn't touch |
223 | | // memory at all. |
224 | 120M | unsigned Offset = 0; |
225 | 120M | unsigned ID = DiagID - DIAG_START_COMMON - 1; |
226 | 120M | #define CATEGORY(NAME, PREV) \ |
227 | 1.32G | if (DiagID > DIAG_START_##NAME) { \ |
228 | 482M | Offset += NUM_BUILTIN_##PREV##_DIAGNOSTICS - DIAG_START_##PREV - 1; \ |
229 | 482M | ID -= DIAG_START_##NAME - DIAG_START_##PREV; \ |
230 | 482M | } |
231 | 120M | CATEGORY(DRIVER, COMMON) |
232 | 120M | CATEGORY(FRONTEND, DRIVER) |
233 | 120M | CATEGORY(SERIALIZATION, FRONTEND) |
234 | 120M | CATEGORY(LEX, SERIALIZATION) |
235 | 120M | CATEGORY(PARSE, LEX) |
236 | 120M | CATEGORY(AST, PARSE) |
237 | 120M | CATEGORY(COMMENT, AST) |
238 | 120M | CATEGORY(CROSSTU, COMMENT) |
239 | 120M | CATEGORY(SEMA, CROSSTU) |
240 | 120M | CATEGORY(ANALYSIS, SEMA) |
241 | 120M | CATEGORY(REFACTORING, ANALYSIS) |
242 | 120M | #undef CATEGORY |
243 | | |
244 | | // Avoid out of bounds reads. |
245 | 120M | if (ID + Offset >= StaticDiagInfoSize) |
246 | 0 | return nullptr; |
247 | | |
248 | 120M | assert(ID < StaticDiagInfoSize && Offset < StaticDiagInfoSize); |
249 | | |
250 | 0 | const StaticDiagInfoRec *Found = &StaticDiagInfo[ID + Offset]; |
251 | | // If the diag id doesn't match we found a different diag, abort. This can |
252 | | // happen when this function is called with an ID that points into a hole in |
253 | | // the diagID space. |
254 | 120M | if (Found->DiagID != DiagID) |
255 | 0 | return nullptr; |
256 | 120M | return Found; |
257 | 120M | } |
258 | | |
259 | 8.86M | DiagnosticMapping DiagnosticIDs::getDefaultMapping(unsigned DiagID) { |
260 | 8.86M | DiagnosticMapping Info = DiagnosticMapping::Make( |
261 | 8.86M | diag::Severity::Fatal, /*IsUser=*/false, /*IsPragma=*/false); |
262 | | |
263 | 8.86M | if (const StaticDiagInfoRec *StaticInfo = GetDiagInfo(DiagID)) { |
264 | 8.86M | Info.setSeverity((diag::Severity)StaticInfo->DefaultSeverity); |
265 | | |
266 | 8.86M | if (StaticInfo->WarnNoWerror) { |
267 | 0 | assert(Info.getSeverity() == diag::Severity::Warning && |
268 | 0 | "Unexpected mapping with no-Werror bit!"); |
269 | 0 | Info.setNoWarningAsError(true); |
270 | 0 | } |
271 | 8.86M | } |
272 | | |
273 | 0 | return Info; |
274 | 8.86M | } |
275 | | |
276 | | /// getCategoryNumberForDiag - Return the category number that a specified |
277 | | /// DiagID belongs to, or 0 if no category. |
278 | 14.6M | unsigned DiagnosticIDs::getCategoryNumberForDiag(unsigned DiagID) { |
279 | 14.6M | if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) |
280 | 14.6M | return Info->Category; |
281 | 0 | return 0; |
282 | 14.6M | } |
283 | | |
284 | | namespace { |
285 | | // The diagnostic category names. |
286 | | struct StaticDiagCategoryRec { |
287 | | const char *NameStr; |
288 | | uint8_t NameLen; |
289 | | |
290 | 14.6M | StringRef getName() const { |
291 | 14.6M | return StringRef(NameStr, NameLen); |
292 | 14.6M | } |
293 | | }; |
294 | | } |
295 | | |
296 | | static const StaticDiagCategoryRec CategoryNameTable[] = { |
297 | | #define GET_CATEGORY_TABLE |
298 | | #define CATEGORY(X, ENUM) { X, STR_SIZE(X, uint8_t) }, |
299 | | #include "clang/Basic/DiagnosticGroups.inc" |
300 | | #undef GET_CATEGORY_TABLE |
301 | | { nullptr, 0 } |
302 | | }; |
303 | | |
304 | | /// getNumberOfCategories - Return the number of categories |
305 | 14.6M | unsigned DiagnosticIDs::getNumberOfCategories() { |
306 | 14.6M | return std::size(CategoryNameTable) - 1; |
307 | 14.6M | } |
308 | | |
309 | | /// getCategoryNameFromID - Given a category ID, return the name of the |
310 | | /// category, an empty string if CategoryID is zero, or null if CategoryID is |
311 | | /// invalid. |
312 | 14.6M | StringRef DiagnosticIDs::getCategoryNameFromID(unsigned CategoryID) { |
313 | 14.6M | if (CategoryID >= getNumberOfCategories()) |
314 | 0 | return StringRef(); |
315 | 14.6M | return CategoryNameTable[CategoryID].getName(); |
316 | 14.6M | } |
317 | | |
318 | | |
319 | | |
320 | | DiagnosticIDs::SFINAEResponse |
321 | 0 | DiagnosticIDs::getDiagnosticSFINAEResponse(unsigned DiagID) { |
322 | 0 | if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) |
323 | 0 | return static_cast<DiagnosticIDs::SFINAEResponse>(Info->SFINAE); |
324 | 0 | return SFINAE_Report; |
325 | 0 | } |
326 | | |
327 | 0 | bool DiagnosticIDs::isDeferrable(unsigned DiagID) { |
328 | 0 | if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) |
329 | 0 | return Info->Deferrable; |
330 | 0 | return false; |
331 | 0 | } |
332 | | |
333 | | /// getBuiltinDiagClass - Return the class field of the diagnostic. |
334 | | /// |
335 | 52.7M | static unsigned getBuiltinDiagClass(unsigned DiagID) { |
336 | 52.7M | if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) |
337 | 52.7M | return Info->Class; |
338 | 0 | return ~0U; |
339 | 52.7M | } |
340 | | |
341 | | //===----------------------------------------------------------------------===// |
342 | | // Custom Diagnostic information |
343 | | //===----------------------------------------------------------------------===// |
344 | | |
345 | | namespace clang { |
346 | | namespace diag { |
347 | | class CustomDiagInfo { |
348 | | typedef std::pair<DiagnosticIDs::Level, std::string> DiagDesc; |
349 | | std::vector<DiagDesc> DiagInfo; |
350 | | std::map<DiagDesc, unsigned> DiagIDs; |
351 | | public: |
352 | | |
353 | | /// getDescription - Return the description of the specified custom |
354 | | /// diagnostic. |
355 | 0 | StringRef getDescription(unsigned DiagID) const { |
356 | 0 | assert(DiagID - DIAG_UPPER_LIMIT < DiagInfo.size() && |
357 | 0 | "Invalid diagnostic ID"); |
358 | 0 | return DiagInfo[DiagID-DIAG_UPPER_LIMIT].second; |
359 | 0 | } |
360 | | |
361 | | /// getLevel - Return the level of the specified custom diagnostic. |
362 | 0 | DiagnosticIDs::Level getLevel(unsigned DiagID) const { |
363 | 0 | assert(DiagID - DIAG_UPPER_LIMIT < DiagInfo.size() && |
364 | 0 | "Invalid diagnostic ID"); |
365 | 0 | return DiagInfo[DiagID-DIAG_UPPER_LIMIT].first; |
366 | 0 | } |
367 | | |
368 | | unsigned getOrCreateDiagID(DiagnosticIDs::Level L, StringRef Message, |
369 | 0 | DiagnosticIDs &Diags) { |
370 | 0 | DiagDesc D(L, std::string(Message)); |
371 | | // Check to see if it already exists. |
372 | 0 | std::map<DiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(D); |
373 | 0 | if (I != DiagIDs.end() && I->first == D) |
374 | 0 | return I->second; |
375 | | |
376 | | // If not, assign a new ID. |
377 | 0 | unsigned ID = DiagInfo.size()+DIAG_UPPER_LIMIT; |
378 | 0 | DiagIDs.insert(std::make_pair(D, ID)); |
379 | 0 | DiagInfo.push_back(D); |
380 | 0 | return ID; |
381 | 0 | } |
382 | | }; |
383 | | |
384 | | } // end diag namespace |
385 | | } // end clang namespace |
386 | | |
387 | | |
388 | | //===----------------------------------------------------------------------===// |
389 | | // Common Diagnostic implementation |
390 | | //===----------------------------------------------------------------------===// |
391 | | |
392 | 2.03k | DiagnosticIDs::DiagnosticIDs() {} |
393 | | |
394 | 2.03k | DiagnosticIDs::~DiagnosticIDs() {} |
395 | | |
396 | | /// getCustomDiagID - Return an ID for a diagnostic with the specified message |
397 | | /// and level. If this is the first request for this diagnostic, it is |
398 | | /// registered and created, otherwise the existing ID is returned. |
399 | | /// |
400 | | /// \param FormatString A fixed diagnostic format string that will be hashed and |
401 | | /// mapped to a unique DiagID. |
402 | 0 | unsigned DiagnosticIDs::getCustomDiagID(Level L, StringRef FormatString) { |
403 | 0 | if (!CustomDiagInfo) |
404 | 0 | CustomDiagInfo.reset(new diag::CustomDiagInfo()); |
405 | 0 | return CustomDiagInfo->getOrCreateDiagID(L, FormatString, *this); |
406 | 0 | } |
407 | | |
408 | | |
409 | | /// isBuiltinWarningOrExtension - Return true if the unmapped diagnostic |
410 | | /// level of the specified diagnostic ID is a Warning or Extension. |
411 | | /// This only works on builtin diagnostics, not custom ones, and is not legal to |
412 | | /// call on NOTEs. |
413 | 0 | bool DiagnosticIDs::isBuiltinWarningOrExtension(unsigned DiagID) { |
414 | 0 | return DiagID < diag::DIAG_UPPER_LIMIT && |
415 | 0 | getBuiltinDiagClass(DiagID) != CLASS_ERROR; |
416 | 0 | } |
417 | | |
418 | | /// Determine whether the given built-in diagnostic ID is a |
419 | | /// Note. |
420 | 12.3k | bool DiagnosticIDs::isBuiltinNote(unsigned DiagID) { |
421 | 12.3k | return DiagID < diag::DIAG_UPPER_LIMIT && |
422 | 12.3k | getBuiltinDiagClass(DiagID) == CLASS_NOTE; |
423 | 12.3k | } |
424 | | |
425 | | /// isBuiltinExtensionDiag - Determine whether the given built-in diagnostic |
426 | | /// ID is for an extension of some sort. This also returns EnabledByDefault, |
427 | | /// which is set to indicate whether the diagnostic is ignored by default (in |
428 | | /// which case -pedantic enables it) or treated as a warning/error by default. |
429 | | /// |
430 | | bool DiagnosticIDs::isBuiltinExtensionDiag(unsigned DiagID, |
431 | 12.8M | bool &EnabledByDefault) { |
432 | 12.8M | if (DiagID >= diag::DIAG_UPPER_LIMIT || |
433 | 12.8M | getBuiltinDiagClass(DiagID) != CLASS_EXTENSION) |
434 | 11.2M | return false; |
435 | | |
436 | 1.53M | EnabledByDefault = |
437 | 1.53M | getDefaultMapping(DiagID).getSeverity() != diag::Severity::Ignored; |
438 | 1.53M | return true; |
439 | 12.8M | } |
440 | | |
441 | 7.32M | bool DiagnosticIDs::isDefaultMappingAsError(unsigned DiagID) { |
442 | 7.32M | if (DiagID >= diag::DIAG_UPPER_LIMIT) |
443 | 0 | return false; |
444 | | |
445 | 7.32M | return getDefaultMapping(DiagID).getSeverity() >= diag::Severity::Error; |
446 | 7.32M | } |
447 | | |
448 | | /// getDescription - Given a diagnostic ID, return a description of the |
449 | | /// issue. |
450 | 0 | StringRef DiagnosticIDs::getDescription(unsigned DiagID) const { |
451 | 0 | if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) |
452 | 0 | return Info->getDescription(); |
453 | 0 | assert(CustomDiagInfo && "Invalid CustomDiagInfo"); |
454 | 0 | return CustomDiagInfo->getDescription(DiagID); |
455 | 0 | } |
456 | | |
457 | 12.5M | static DiagnosticIDs::Level toLevel(diag::Severity SV) { |
458 | 12.5M | switch (SV) { |
459 | 1.47M | case diag::Severity::Ignored: |
460 | 1.47M | return DiagnosticIDs::Ignored; |
461 | 0 | case diag::Severity::Remark: |
462 | 0 | return DiagnosticIDs::Remark; |
463 | 3.70M | case diag::Severity::Warning: |
464 | 3.70M | return DiagnosticIDs::Warning; |
465 | 7.32M | case diag::Severity::Error: |
466 | 7.32M | return DiagnosticIDs::Error; |
467 | 0 | case diag::Severity::Fatal: |
468 | 0 | return DiagnosticIDs::Fatal; |
469 | 12.5M | } |
470 | 0 | llvm_unreachable("unexpected severity"); |
471 | 0 | } |
472 | | |
473 | | /// getDiagnosticLevel - Based on the way the client configured the |
474 | | /// DiagnosticsEngine object, classify the specified diagnostic ID into a Level, |
475 | | /// by consumable the DiagnosticClient. |
476 | | DiagnosticIDs::Level |
477 | | DiagnosticIDs::getDiagnosticLevel(unsigned DiagID, SourceLocation Loc, |
478 | 12.5M | const DiagnosticsEngine &Diag) const { |
479 | | // Handle custom diagnostics, which cannot be mapped. |
480 | 12.5M | if (DiagID >= diag::DIAG_UPPER_LIMIT) { |
481 | 0 | assert(CustomDiagInfo && "Invalid CustomDiagInfo"); |
482 | 0 | return CustomDiagInfo->getLevel(DiagID); |
483 | 0 | } |
484 | | |
485 | 12.5M | unsigned DiagClass = getBuiltinDiagClass(DiagID); |
486 | 12.5M | if (DiagClass == CLASS_NOTE) return DiagnosticIDs::Note; |
487 | 12.5M | return toLevel(getDiagnosticSeverity(DiagID, Loc, Diag)); |
488 | 12.5M | } |
489 | | |
490 | | /// Based on the way the client configured the Diagnostic |
491 | | /// object, classify the specified diagnostic ID into a Level, consumable by |
492 | | /// the DiagnosticClient. |
493 | | /// |
494 | | /// \param Loc The source location we are interested in finding out the |
495 | | /// diagnostic state. Can be null in order to query the latest state. |
496 | | diag::Severity |
497 | | DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc, |
498 | 12.8M | const DiagnosticsEngine &Diag) const { |
499 | 12.8M | assert(getBuiltinDiagClass(DiagID) != CLASS_NOTE); |
500 | | |
501 | | // Specific non-error diagnostics may be mapped to various levels from ignored |
502 | | // to error. Errors can only be mapped to fatal. |
503 | 0 | diag::Severity Result = diag::Severity::Fatal; |
504 | | |
505 | | // Get the mapping information, or compute it lazily. |
506 | 12.8M | DiagnosticsEngine::DiagState *State = Diag.GetDiagStateForLoc(Loc); |
507 | 12.8M | DiagnosticMapping &Mapping = State->getOrAddMapping((diag::kind)DiagID); |
508 | | |
509 | | // TODO: Can a null severity really get here? |
510 | 12.8M | if (Mapping.getSeverity() != diag::Severity()) |
511 | 12.8M | Result = Mapping.getSeverity(); |
512 | | |
513 | | // Upgrade ignored diagnostics if -Weverything is enabled. |
514 | 12.8M | if (State->EnableAllWarnings && Result == diag::Severity::Ignored && |
515 | 12.8M | !Mapping.isUser() && getBuiltinDiagClass(DiagID) != CLASS_REMARK) |
516 | 0 | Result = diag::Severity::Warning; |
517 | | |
518 | | // Ignore -pedantic diagnostics inside __extension__ blocks. |
519 | | // (The diagnostics controlled by -pedantic are the extension diagnostics |
520 | | // that are not enabled by default.) |
521 | 12.8M | bool EnabledByDefault = false; |
522 | 12.8M | bool IsExtensionDiag = isBuiltinExtensionDiag(DiagID, EnabledByDefault); |
523 | 12.8M | if (Diag.AllExtensionsSilenced && IsExtensionDiag && !EnabledByDefault) |
524 | 0 | return diag::Severity::Ignored; |
525 | | |
526 | | // For extension diagnostics that haven't been explicitly mapped, check if we |
527 | | // should upgrade the diagnostic. |
528 | 12.8M | if (IsExtensionDiag && !Mapping.isUser()) |
529 | 1.53M | Result = std::max(Result, State->ExtBehavior); |
530 | | |
531 | | // At this point, ignored errors can no longer be upgraded. |
532 | 12.8M | if (Result == diag::Severity::Ignored) |
533 | 1.79M | return Result; |
534 | | |
535 | | // Honor -w: this disables all messages which are not Error/Fatal by |
536 | | // default (disregarding attempts to upgrade severity from Warning to Error), |
537 | | // as well as disabling all messages which are currently mapped to Warning |
538 | | // (whether by default or downgraded from Error via e.g. -Wno-error or #pragma |
539 | | // diagnostic.) |
540 | 11.0M | if (State->IgnoreAllWarnings) { |
541 | 0 | if (Result == diag::Severity::Warning || |
542 | 0 | (Result >= diag::Severity::Error && |
543 | 0 | !isDefaultMappingAsError((diag::kind)DiagID))) |
544 | 0 | return diag::Severity::Ignored; |
545 | 0 | } |
546 | | |
547 | | // If -Werror is enabled, map warnings to errors unless explicitly disabled. |
548 | 11.0M | if (Result == diag::Severity::Warning) { |
549 | 3.70M | if (State->WarningsAsErrors && !Mapping.hasNoWarningAsError()) |
550 | 0 | Result = diag::Severity::Error; |
551 | 3.70M | } |
552 | | |
553 | | // If -Wfatal-errors is enabled, map errors to fatal unless explicitly |
554 | | // disabled. |
555 | 11.0M | if (Result == diag::Severity::Error) { |
556 | 7.32M | if (State->ErrorsAsFatal && !Mapping.hasNoErrorAsFatal()) |
557 | 0 | Result = diag::Severity::Fatal; |
558 | 7.32M | } |
559 | | |
560 | | // If explicitly requested, map fatal errors to errors. |
561 | 11.0M | if (Result == diag::Severity::Fatal && |
562 | 11.0M | Diag.CurDiagID != diag::fatal_too_many_errors && Diag.FatalsAsError) |
563 | 0 | Result = diag::Severity::Error; |
564 | | |
565 | | // Custom diagnostics always are emitted in system headers. |
566 | 11.0M | bool ShowInSystemHeader = |
567 | 11.0M | !GetDiagInfo(DiagID) || GetDiagInfo(DiagID)->WarnShowInSystemHeader; |
568 | | |
569 | | // If we are in a system header, we ignore it. We look at the diagnostic class |
570 | | // because we also want to ignore extensions and warnings in -Werror and |
571 | | // -pedantic-errors modes, which *map* warnings/extensions to errors. |
572 | 11.0M | if (State->SuppressSystemWarnings && !ShowInSystemHeader && Loc.isValid() && |
573 | 11.0M | Diag.getSourceManager().isInSystemHeader( |
574 | 3.70M | Diag.getSourceManager().getExpansionLoc(Loc))) |
575 | 0 | return diag::Severity::Ignored; |
576 | | |
577 | | // We also ignore warnings due to system macros |
578 | 11.0M | bool ShowInSystemMacro = |
579 | 11.0M | !GetDiagInfo(DiagID) || GetDiagInfo(DiagID)->WarnShowInSystemMacro; |
580 | 11.0M | if (State->SuppressSystemWarnings && !ShowInSystemMacro && Loc.isValid() && |
581 | 11.0M | Diag.getSourceManager().isInSystemMacro(Loc)) |
582 | 0 | return diag::Severity::Ignored; |
583 | | |
584 | 11.0M | return Result; |
585 | 11.0M | } |
586 | | |
587 | | #define GET_DIAG_ARRAYS |
588 | | #include "clang/Basic/DiagnosticGroups.inc" |
589 | | #undef GET_DIAG_ARRAYS |
590 | | |
591 | | namespace { |
592 | | struct WarningOption { |
593 | | uint16_t NameOffset; |
594 | | uint16_t Members; |
595 | | uint16_t SubGroups; |
596 | | StringRef Documentation; |
597 | | |
598 | | // String is stored with a pascal-style length byte. |
599 | 0 | StringRef getName() const { |
600 | 0 | return StringRef(DiagGroupNames + NameOffset + 1, |
601 | 0 | DiagGroupNames[NameOffset]); |
602 | 0 | } |
603 | | }; |
604 | | } |
605 | | |
606 | | // Second the table of options, sorted by name for fast binary lookup. |
607 | | static const WarningOption OptionTable[] = { |
608 | | #define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs) \ |
609 | | {FlagNameOffset, Members, SubGroups, Docs}, |
610 | | #include "clang/Basic/DiagnosticGroups.inc" |
611 | | #undef DIAG_ENTRY |
612 | | }; |
613 | | |
614 | | /// Given a diagnostic group ID, return its documentation. |
615 | 0 | StringRef DiagnosticIDs::getWarningOptionDocumentation(diag::Group Group) { |
616 | 0 | return OptionTable[static_cast<int>(Group)].Documentation; |
617 | 0 | } |
618 | | |
619 | 0 | StringRef DiagnosticIDs::getWarningOptionForGroup(diag::Group Group) { |
620 | 0 | return OptionTable[static_cast<int>(Group)].getName(); |
621 | 0 | } |
622 | | |
623 | | std::optional<diag::Group> |
624 | 0 | DiagnosticIDs::getGroupForWarningOption(StringRef Name) { |
625 | 0 | const auto *Found = llvm::partition_point( |
626 | 0 | OptionTable, [=](const WarningOption &O) { return O.getName() < Name; }); |
627 | 0 | if (Found == std::end(OptionTable) || Found->getName() != Name) |
628 | 0 | return std::nullopt; |
629 | 0 | return static_cast<diag::Group>(Found - OptionTable); |
630 | 0 | } |
631 | | |
632 | 0 | std::optional<diag::Group> DiagnosticIDs::getGroupForDiag(unsigned DiagID) { |
633 | 0 | if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) |
634 | 0 | return static_cast<diag::Group>(Info->getOptionGroupIndex()); |
635 | 0 | return std::nullopt; |
636 | 0 | } |
637 | | |
638 | | /// getWarningOptionForDiag - Return the lowest-level warning option that |
639 | | /// enables the specified diagnostic. If there is no -Wfoo flag that controls |
640 | | /// the diagnostic, this returns null. |
641 | 0 | StringRef DiagnosticIDs::getWarningOptionForDiag(unsigned DiagID) { |
642 | 0 | if (auto G = getGroupForDiag(DiagID)) |
643 | 0 | return getWarningOptionForGroup(*G); |
644 | 0 | return StringRef(); |
645 | 0 | } |
646 | | |
647 | 0 | std::vector<std::string> DiagnosticIDs::getDiagnosticFlags() { |
648 | 0 | std::vector<std::string> Res{"-W", "-Wno-"}; |
649 | 0 | for (size_t I = 1; DiagGroupNames[I] != '\0';) { |
650 | 0 | std::string Diag(DiagGroupNames + I + 1, DiagGroupNames[I]); |
651 | 0 | I += DiagGroupNames[I] + 1; |
652 | 0 | Res.push_back("-W" + Diag); |
653 | 0 | Res.push_back("-Wno-" + Diag); |
654 | 0 | } |
655 | |
|
656 | 0 | return Res; |
657 | 0 | } |
658 | | |
659 | | /// Return \c true if any diagnostics were found in this group, even if they |
660 | | /// were filtered out due to having the wrong flavor. |
661 | | static bool getDiagnosticsInGroup(diag::Flavor Flavor, |
662 | | const WarningOption *Group, |
663 | 0 | SmallVectorImpl<diag::kind> &Diags) { |
664 | | // An empty group is considered to be a warning group: we have empty groups |
665 | | // for GCC compatibility, and GCC does not have remarks. |
666 | 0 | if (!Group->Members && !Group->SubGroups) |
667 | 0 | return Flavor == diag::Flavor::Remark; |
668 | | |
669 | 0 | bool NotFound = true; |
670 | | |
671 | | // Add the members of the option diagnostic set. |
672 | 0 | const int16_t *Member = DiagArrays + Group->Members; |
673 | 0 | for (; *Member != -1; ++Member) { |
674 | 0 | if (GetDiagInfo(*Member)->getFlavor() == Flavor) { |
675 | 0 | NotFound = false; |
676 | 0 | Diags.push_back(*Member); |
677 | 0 | } |
678 | 0 | } |
679 | | |
680 | | // Add the members of the subgroups. |
681 | 0 | const int16_t *SubGroups = DiagSubGroups + Group->SubGroups; |
682 | 0 | for (; *SubGroups != (int16_t)-1; ++SubGroups) |
683 | 0 | NotFound &= getDiagnosticsInGroup(Flavor, &OptionTable[(short)*SubGroups], |
684 | 0 | Diags); |
685 | |
|
686 | 0 | return NotFound; |
687 | 0 | } |
688 | | |
689 | | bool |
690 | | DiagnosticIDs::getDiagnosticsInGroup(diag::Flavor Flavor, StringRef Group, |
691 | 0 | SmallVectorImpl<diag::kind> &Diags) const { |
692 | 0 | if (std::optional<diag::Group> G = getGroupForWarningOption(Group)) |
693 | 0 | return ::getDiagnosticsInGroup( |
694 | 0 | Flavor, &OptionTable[static_cast<unsigned>(*G)], Diags); |
695 | 0 | return true; |
696 | 0 | } |
697 | | |
698 | | void DiagnosticIDs::getAllDiagnostics(diag::Flavor Flavor, |
699 | 0 | std::vector<diag::kind> &Diags) { |
700 | 0 | for (unsigned i = 0; i != StaticDiagInfoSize; ++i) |
701 | 0 | if (StaticDiagInfo[i].getFlavor() == Flavor) |
702 | 0 | Diags.push_back(StaticDiagInfo[i].DiagID); |
703 | 0 | } |
704 | | |
705 | | StringRef DiagnosticIDs::getNearestOption(diag::Flavor Flavor, |
706 | 0 | StringRef Group) { |
707 | 0 | StringRef Best; |
708 | 0 | unsigned BestDistance = Group.size() + 1; // Maximum threshold. |
709 | 0 | for (const WarningOption &O : OptionTable) { |
710 | | // Don't suggest ignored warning flags. |
711 | 0 | if (!O.Members && !O.SubGroups) |
712 | 0 | continue; |
713 | | |
714 | 0 | unsigned Distance = O.getName().edit_distance(Group, true, BestDistance); |
715 | 0 | if (Distance > BestDistance) |
716 | 0 | continue; |
717 | | |
718 | | // Don't suggest groups that are not of this kind. |
719 | 0 | llvm::SmallVector<diag::kind, 8> Diags; |
720 | 0 | if (::getDiagnosticsInGroup(Flavor, &O, Diags) || Diags.empty()) |
721 | 0 | continue; |
722 | | |
723 | 0 | if (Distance == BestDistance) { |
724 | | // Two matches with the same distance, don't prefer one over the other. |
725 | 0 | Best = ""; |
726 | 0 | } else if (Distance < BestDistance) { |
727 | | // This is a better match. |
728 | 0 | Best = O.getName(); |
729 | 0 | BestDistance = Distance; |
730 | 0 | } |
731 | 0 | } |
732 | |
|
733 | 0 | return Best; |
734 | 0 | } |
735 | | |
736 | | /// ProcessDiag - This is the method used to report a diagnostic that is |
737 | | /// finally fully formed. |
738 | 12.4M | bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const { |
739 | 12.4M | Diagnostic Info(&Diag); |
740 | | |
741 | 12.4M | assert(Diag.getClient() && "DiagnosticClient not set!"); |
742 | | |
743 | | // Figure out the diagnostic level of this message. |
744 | 0 | unsigned DiagID = Info.getID(); |
745 | 12.4M | DiagnosticIDs::Level DiagLevel |
746 | 12.4M | = getDiagnosticLevel(DiagID, Info.getLocation(), Diag); |
747 | | |
748 | | // Update counts for DiagnosticErrorTrap even if a fatal error occurred |
749 | | // or diagnostics are suppressed. |
750 | 12.4M | if (DiagLevel >= DiagnosticIDs::Error) { |
751 | 7.31M | ++Diag.TrapNumErrorsOccurred; |
752 | 7.31M | if (isUnrecoverable(DiagID)) |
753 | 7.31M | ++Diag.TrapNumUnrecoverableErrorsOccurred; |
754 | 7.31M | } |
755 | | |
756 | 12.4M | if (Diag.SuppressAllDiagnostics) |
757 | 0 | return false; |
758 | | |
759 | 12.4M | if (DiagLevel != DiagnosticIDs::Note) { |
760 | | // Record that a fatal error occurred only when we see a second |
761 | | // non-note diagnostic. This allows notes to be attached to the |
762 | | // fatal error, but suppresses any diagnostics that follow those |
763 | | // notes. |
764 | 12.4M | if (Diag.LastDiagLevel == DiagnosticIDs::Fatal) |
765 | 0 | Diag.FatalErrorOccurred = true; |
766 | | |
767 | 12.4M | Diag.LastDiagLevel = DiagLevel; |
768 | 12.4M | } |
769 | | |
770 | | // If a fatal error has already been emitted, silence all subsequent |
771 | | // diagnostics. |
772 | 12.4M | if (Diag.FatalErrorOccurred) { |
773 | 0 | if (DiagLevel >= DiagnosticIDs::Error && |
774 | 0 | Diag.Client->IncludeInDiagnosticCounts()) { |
775 | 0 | ++Diag.NumErrors; |
776 | 0 | } |
777 | |
|
778 | 0 | return false; |
779 | 0 | } |
780 | | |
781 | | // If the client doesn't care about this message, don't issue it. If this is |
782 | | // a note and the last real diagnostic was ignored, ignore it too. |
783 | 12.4M | if (DiagLevel == DiagnosticIDs::Ignored || |
784 | 12.4M | (DiagLevel == DiagnosticIDs::Note && |
785 | 11.0M | Diag.LastDiagLevel == DiagnosticIDs::Ignored)) |
786 | 1.47M | return false; |
787 | | |
788 | 11.0M | if (DiagLevel >= DiagnosticIDs::Error) { |
789 | 7.31M | if (isUnrecoverable(DiagID)) |
790 | 7.31M | Diag.UnrecoverableErrorOccurred = true; |
791 | | |
792 | | // Warnings which have been upgraded to errors do not prevent compilation. |
793 | 7.31M | if (isDefaultMappingAsError(DiagID)) |
794 | 7.31M | Diag.UncompilableErrorOccurred = true; |
795 | | |
796 | 7.31M | Diag.ErrorOccurred = true; |
797 | 7.31M | if (Diag.Client->IncludeInDiagnosticCounts()) { |
798 | 7.31M | ++Diag.NumErrors; |
799 | 7.31M | } |
800 | | |
801 | | // If we've emitted a lot of errors, emit a fatal error instead of it to |
802 | | // stop a flood of bogus errors. |
803 | 7.31M | if (Diag.ErrorLimit && Diag.NumErrors > Diag.ErrorLimit && |
804 | 7.31M | DiagLevel == DiagnosticIDs::Error) { |
805 | 0 | Diag.SetDelayedDiagnostic(diag::fatal_too_many_errors); |
806 | 0 | return false; |
807 | 0 | } |
808 | 7.31M | } |
809 | | |
810 | | // Make sure we set FatalErrorOccurred to ensure that the notes from the |
811 | | // diagnostic that caused `fatal_too_many_errors` won't be emitted. |
812 | 11.0M | if (Diag.CurDiagID == diag::fatal_too_many_errors) |
813 | 0 | Diag.FatalErrorOccurred = true; |
814 | | // Finally, report it. |
815 | 11.0M | EmitDiag(Diag, DiagLevel); |
816 | 11.0M | return true; |
817 | 11.0M | } |
818 | | |
819 | 11.0M | void DiagnosticIDs::EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const { |
820 | 11.0M | Diagnostic Info(&Diag); |
821 | 11.0M | assert(DiagLevel != DiagnosticIDs::Ignored && "Cannot emit ignored diagnostics!"); |
822 | | |
823 | 0 | Diag.Client->HandleDiagnostic((DiagnosticsEngine::Level)DiagLevel, Info); |
824 | 11.0M | if (Diag.Client->IncludeInDiagnosticCounts()) { |
825 | 11.0M | if (DiagLevel == DiagnosticIDs::Warning) |
826 | 3.70M | ++Diag.NumWarnings; |
827 | 11.0M | } |
828 | | |
829 | 11.0M | Diag.CurDiagID = ~0U; |
830 | 11.0M | } |
831 | | |
832 | 14.6M | bool DiagnosticIDs::isUnrecoverable(unsigned DiagID) const { |
833 | 14.6M | if (DiagID >= diag::DIAG_UPPER_LIMIT) { |
834 | 0 | assert(CustomDiagInfo && "Invalid CustomDiagInfo"); |
835 | | // Custom diagnostics. |
836 | 0 | return CustomDiagInfo->getLevel(DiagID) >= DiagnosticIDs::Error; |
837 | 0 | } |
838 | | |
839 | | // Only errors may be unrecoverable. |
840 | 14.6M | if (getBuiltinDiagClass(DiagID) < CLASS_ERROR) |
841 | 6.55k | return false; |
842 | | |
843 | 14.6M | if (DiagID == diag::err_unavailable || |
844 | 14.6M | DiagID == diag::err_unavailable_message) |
845 | 0 | return false; |
846 | | |
847 | | // Currently we consider all ARC errors as recoverable. |
848 | 14.6M | if (isARCDiagnostic(DiagID)) |
849 | 0 | return false; |
850 | | |
851 | 14.6M | return true; |
852 | 14.6M | } |
853 | | |
854 | 14.6M | bool DiagnosticIDs::isARCDiagnostic(unsigned DiagID) { |
855 | 14.6M | unsigned cat = getCategoryNumberForDiag(DiagID); |
856 | 14.6M | return DiagnosticIDs::getCategoryNameFromID(cat).starts_with("ARC "); |
857 | 14.6M | } |