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