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