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