clang 23.0.0git
DiagnosticIDs.cpp
Go to the documentation of this file.
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
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)];
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,
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),
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,
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,
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(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)
181VALIDATE_DIAG_SIZE(CROSSTU)
183VALIDATE_DIAG_SIZE(ANALYSIS)
184VALIDATE_DIAG_SIZE(REFACTORING)
185VALIDATE_DIAG_SIZE(INSTALLAPI)
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 {
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
304 // Check to see if it already exists.
305 std::map<CustomDiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(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(std::make_pair(D, ID));
312 DiagInfo.push_back(D);
313 if (auto Group = D.GetGroup())
314 GroupToDiags[*Group].emplace_back(ID);
315 return ID;
316 }
317
319 if (auto Diags = GroupToDiags.find(G); Diags != GroupToDiags.end())
320 return Diags->second;
321 return {};
322 }
323};
324
325} // namespace diag
326} // namespace clang
327
330 diag::Severity::Fatal, /*IsUser=*/false, /*IsPragma=*/false);
331
332 if (IsCustomDiag(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
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));
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.
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 { nullptr, 0 }
390};
391
392/// getNumberOfCategories - Return the number of categories
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
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
426
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.
436 if (!CustomDiagInfo)
437 CustomDiagInfo.reset(new diag::CustomDiagInfo());
438 return CustomDiagInfo->getOrCreateDiagID(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///
459 bool &EnabledByDefault) const {
460 if (IsCustomDiag(DiagID) || getDiagClass(DiagID) != CLASS_EXTENSION)
461 return false;
462
463 EnabledByDefault =
465 return true;
466}
467
468bool DiagnosticIDs::isDefaultMappingAsError(unsigned DiagID) const {
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(DiagID);
490}
491
492/// getLegacyStableIDs - Given a diagnostic ID, return the previous stable IDs
493/// of the diagnostic.
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
506 switch (SV) {
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.
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(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.
539DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,
540 const DiagnosticsEngine &Diag) const {
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.
547
548 // Get the mapping information, or compute it lazily.
549 DiagnosticsEngine::DiagState *State = Diag.GetDiagStateForLoc(Loc);
550 DiagnosticMapping Mapping = State->getOrAddMapping((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))
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)
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(Result,
577 std::min(State->ExtBehavior, diag::Severity::Warning));
578 else
579 Result = std::max(Result, State->ExtBehavior);
580 }
581
582 // At this point, ignored errors can no longer be upgraded.
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()) &&
598 }
599
600 // If -Werror is enabled, map warnings to errors unless explicitly disabled.
602 if (State->WarningsAsErrors && !Mapping.hasNoWarningAsError())
604 }
605
606 // If -Wfatal-errors is enabled, map errors to fatal unless explicitly
607 // disabled.
609 if (State->ErrorsAsFatal && !Mapping.hasNoErrorAsFatal())
611 }
612
613 // If explicitly requested, map fatal errors to errors.
615 DiagID != diag::fatal_too_many_errors && Diag.FatalsAsError)
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(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)
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))
656 }
657 // Clang-diagnostics pragmas always take precedence over suppression mapping.
658 if (!Mapping.isPragma() && Diag.isSuppressedViaMapping(DiagID, Loc))
660
661 return Result;
662}
663
664DiagnosticIDs::Class DiagnosticIDs::getDiagClass(unsigned DiagID) const {
665 if (IsCustomDiag(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.
698 return OptionTable[static_cast<int>(Group)].Documentation;
699}
700
702 return OptionTable[static_cast<int>(Group)].getName();
703}
704
705std::optional<diag::Group>
707 const auto *Found = llvm::partition_point(
708 OptionTable, [=](const WarningOption &O) { return O.getName() < Name; });
709 if (Found == std::end(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(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(*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((Twine("-W") + Name).str());
741 Res.push_back((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.
750 const WarningOption *Group,
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(*Member)->getFlavor() == Flavor) {
764 NotFound = false;
765 Diags.push_back(*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 CustomDiagInfo->getDiagsInGroup(static_cast<diag::Group>(*SubGroups)),
775 std::back_inserter(Diags));
776 NotFound &= getDiagnosticsInGroup(Flavor, &OptionTable[(short)*SubGroups],
777 Diags, CustomDiagInfo);
778 }
779
780 return NotFound;
781}
782
783bool
785 SmallVectorImpl<diag::kind> &Diags) const {
786 if (std::optional<diag::Group> G = getGroupForWarningOption(Group)) {
787 if (CustomDiagInfo)
788 llvm::copy(CustomDiagInfo->getDiagsInGroup(*G),
789 std::back_inserter(Diags));
790 return ::getDiagnosticsInGroup(Flavor,
791 &OptionTable[static_cast<unsigned>(*G)],
792 Diags, 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
814 if (std::optional<diag::Group> G = getGroupForWarningOption(Group)) {
815 ::forEachSubGroup(*G, [&](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(Group)) {
823 ::forEachSubGroup(*G, [&](size_t SubGroup) {
824 GroupInfos[static_cast<size_t>(*G)].HasNoWarningAsError = Val;
825 });
826 }
827}
828
830 std::vector<diag::kind> &Diags) {
831 for (unsigned i = 0; i != StaticDiagInfoSize; ++i)
832 if (StaticDiagInfo[i].getFlavor() == Flavor)
833 Diags.push_back(StaticDiagInfo[i].DiagID);
834}
835
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(Group, true, BestDistance);
846 if (Distance > BestDistance)
847 continue;
848
849 // Don't suggest groups that are not of this kind.
851 if (::getDiagnosticsInGroup(Flavor, &O, Diags, 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
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(cat).starts_with("ARC ");
938}
939
941 unsigned cat = getCategoryNumberForDiag(DiagID);
942 return DiagnosticIDs::getCategoryNameFromID(cat) == "Codegen ABI Check";
943}
Defines the Diagnostic IDs in ID sorted order.
Includes all the separate Diagnostic headers & some related helpers.
#define COMMENT(CLASS, PARENT)
Definition Comment.h:55
static const StaticDiagInfoRec * GetDiagInfo(unsigned DiagID)
GetDiagInfo - Return the StaticDiagInfoRec entry for the specified DiagID, or null if the ID is inval...
static bool getDiagnosticsInGroup(diag::Flavor Flavor, const WarningOption *Group, SmallVectorImpl< diag::kind > &Diags, diag::CustomDiagInfo *CustomDiagInfo)
Return true if any diagnostics were found in this group, even if they were filtered out due to having...
static DiagnosticIDs::Level toLevel(diag::Severity SV)
static void forEachSubGroupImpl(const WarningOption *Group, Func func)
#define VALIDATE_DIAG_SIZE(NAME)
#define CATEGORY(NAME, PREV)
static const unsigned StaticDiagInfoSize
static const WarningOption OptionTable[]
static const StaticDiagCategoryRec CategoryNameTable[]
static void forEachSubGroup(diag::Group Group, Func func)
Defines the Diagnostic IDs-related interfaces.
Defines the clang::LangOptions interface.
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
#define SM(sm)
Defines the SourceManager interface.
std::optional< diag::Group > GetGroup() const
void initCustomDiagMapping(DiagnosticMapping &, unsigned DiagID)
static StringRef getCategoryNameFromID(unsigned CategoryID)
Given a category ID, return the name of the category.
static unsigned getNumberOfCategories()
Return the number of diagnostic categories.
static StringRef getNearestOption(diag::Flavor Flavor, StringRef Group)
Get the diagnostic option with the closest edit distance to the given group name.
bool getDiagnosticsInGroup(diag::Flavor Flavor, StringRef Group, SmallVectorImpl< diag::kind > &Diags) const
Get the set of all diagnostic IDs in the group with the given name.
static std::vector< std::string > getDiagnosticFlags()
Get the string of all diagnostic flags.
bool isWarningOrExtension(unsigned DiagID) const
Return true if the unmapped diagnostic levelof the specified diagnostic ID is a Warning or Extension.
void setGroupSeverity(StringRef Group, diag::Severity)
bool isExtensionDiag(unsigned DiagID) const
Determine whether the given diagnostic ID is for an extension of some sort.
static unsigned getCXXCompatDiagId(const LangOptions &LangOpts, unsigned CompatDiagId)
Get the appropriate diagnostic Id to use for issuing a compatibility diagnostic.
DiagnosticMapping getDefaultMapping(unsigned DiagID) const
Get the default mapping for this diagnostic.
std::string getStableID(unsigned DiagID) const
Given a diagnostic ID, return the stable ID of the diagnostic.
static SFINAEResponse getDiagnosticSFINAEResponse(unsigned DiagID)
Determines whether the given built-in diagnostic ID is for an error that is suppressed if it occurs d...
bool isDefaultMappingAsError(unsigned DiagID) const
Return true if the specified diagnostic is mapped to errors by default.
void setGroupNoWarningsAsError(StringRef Group, bool)
static bool isCodegenABICheckDiagnostic(unsigned DiagID)
Return true if a given diagnostic is a codegen-time ABI check.
StringRef getDescription(unsigned DiagID) const
Given a diagnostic ID, return a description of the issue.
SFINAEResponse
Enumeration describing how the emission of a diagnostic should be treated when it occurs during C++ t...
@ SFINAE_Report
The diagnostic should be reported.
bool isNote(unsigned DiagID) const
Determine whether the given diagnostic ID is a Note.
StringRef getWarningOptionForDiag(unsigned DiagID)
Return the lowest-level warning option that enables the specified diagnostic.
static StringRef getWarningOptionDocumentation(diag::Group GroupID)
Given a diagnostic group ID, return its documentation.
static std::optional< diag::Group > getGroupForWarningOption(StringRef)
Given a group ID, returns the flag that toggles the group.
static bool IsCustomDiag(diag::kind Diag)
unsigned getCustomDiagID(CustomDiagDesc Diag)
Return an ID for a diagnostic with the specified format string and level.
Level
The level of the diagnostic, after it has been through mapping.
static unsigned getCategoryNumberForDiag(unsigned DiagID)
Return the category number that a specified DiagID belongs to, or 0 if no category.
static StringRef getWarningOptionForGroup(diag::Group)
Given a group ID, returns the flag that toggles the group.
static bool isARCDiagnostic(unsigned DiagID)
Return true if a given diagnostic falls into an ARC diagnostic category.
static void getAllDiagnostics(diag::Flavor Flavor, std::vector< diag::kind > &Diags)
Get the set of all diagnostic IDs.
std::optional< diag::Group > getGroupForDiag(unsigned DiagID) const
Return the lowest-level group that contains the specified diagnostic.
static bool isDeferrable(unsigned DiagID)
Whether the diagnostic message can be deferred.
llvm::SmallVector< StringRef, 4 > getLegacyStableIDs(unsigned DiagID) const
Given a diagnostic ID, return the previous stable IDs of the diagnostic.
void setNoWarningAsError(bool Value)
void setSeverity(diag::Severity Value)
diag::Severity getSeverity() const
static DiagnosticMapping Make(diag::Severity Severity, bool IsUser, bool IsPragma)
void setNoErrorAsFatal(bool Value)
bool hasNoWarningAsError() const
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:232
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
unsigned getOrCreateDiagID(DiagnosticIDs::CustomDiagDesc D)
const CustomDiagDesc & getDescription(unsigned DiagID) const
getDescription - Return the description of the specified custom diagnostic.
ArrayRef< unsigned > getDiagsInGroup(diag::Group G) const
DiagnosticIDs::CustomDiagDesc CustomDiagDesc
Flavor
Flavors of diagnostics we can emit.
@ Remark
A diagnostic that indicates normal progress through compilation.
unsigned kind
All of the diagnostics that can be emitted by the frontend.
Severity
Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs to either Ignore (nothing),...
@ Warning
Present this diagnostic as a warning.
@ Fatal
Present this diagnostic as a fatal error.
@ Error
Present this diagnostic as an error.
@ Remark
Present this diagnostic as a remark.
@ Ignored
Do not present this diagnostic, ignore it.
The JSON file list parser is used to communicate input to InstallAPI.
bool operator<(DeclarationName LHS, DeclarationName RHS)
Ordering on two declaration names.
@ Result
The result type of a method or function.
Definition TypeBase.h:905
unsigned int uint32_t