clang 24.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(CODEGEN)
177VALIDATE_DIAG_SIZE(SERIALIZATION)
182VALIDATE_DIAG_SIZE(CROSSTU)
184VALIDATE_DIAG_SIZE(ANALYSIS)
185VALIDATE_DIAG_SIZE(REFACTORING)
186VALIDATE_DIAG_SIZE(INSTALLAPI)
188#undef VALIDATE_DIAG_SIZE
189#undef STRINGIFY_NAME
190
191const StaticDiagInfoRec StaticDiagInfo[] = {
192// clang-format off
193#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \
194 SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, STABLE_ID, \
195 LEGACY_STABLE_IDS) \
196 { \
197 diag::ENUM, \
198 DEFAULT_SEVERITY, \
199 CLASS, \
200 DiagnosticIDs::SFINAE, \
201 CATEGORY, \
202 NOWERROR, \
203 SHOWINSYSHEADER, \
204 SHOWINSYSMACRO, \
205 GROUP, \
206 DEFERRABLE, \
207 STR_SIZE(DESC, uint16_t)},
208#include "clang/Basic/DiagnosticCommonKinds.inc"
209#include "clang/Basic/DiagnosticDriverKinds.inc"
210#include "clang/Basic/DiagnosticFrontendKinds.inc"
211#include "clang/Basic/DiagnosticCodeGenKinds.inc"
212#include "clang/Basic/DiagnosticSerializationKinds.inc"
213#include "clang/Basic/DiagnosticLexKinds.inc"
214#include "clang/Basic/DiagnosticParseKinds.inc"
215#include "clang/Basic/DiagnosticASTKinds.inc"
216#include "clang/Basic/DiagnosticCommentKinds.inc"
217#include "clang/Basic/DiagnosticCrossTUKinds.inc"
218#include "clang/Basic/DiagnosticSemaKinds.inc"
219#include "clang/Basic/DiagnosticAnalysisKinds.inc"
220#include "clang/Basic/DiagnosticRefactoringKinds.inc"
221#include "clang/Basic/DiagnosticInstallAPIKinds.inc"
222#include "clang/Basic/DiagnosticTrapKinds.inc"
223// clang-format on
224#undef DIAG
225};
226
227} // namespace
228
229static const unsigned StaticDiagInfoSize = std::size(StaticDiagInfo);
230
231/// GetDiagInfo - Return the StaticDiagInfoRec entry for the specified DiagID,
232/// or null if the ID is invalid.
233static const StaticDiagInfoRec *GetDiagInfo(unsigned DiagID) {
234 // Out of bounds diag. Can't be in the table.
235 using namespace diag;
236 if (DiagID >= DIAG_UPPER_LIMIT || DiagID <= DIAG_START_COMMON)
237 return nullptr;
238
239 // Compute the index of the requested diagnostic in the static table.
240 // 1. Add the number of diagnostics in each category preceding the
241 // diagnostic and of the category the diagnostic is in. This gives us
242 // the offset of the category in the table.
243 // 2. Subtract the number of IDs in each category from our ID. This gives us
244 // the offset of the diagnostic in the category.
245 // This is cheaper than a binary search on the table as it doesn't touch
246 // memory at all.
247 unsigned Offset = 0;
248 unsigned ID = DiagID - DIAG_START_COMMON - 1;
249#define CATEGORY(NAME, PREV) \
250 if (DiagID > DIAG_START_##NAME) { \
251 Offset += NUM_BUILTIN_##PREV##_DIAGNOSTICS - DIAG_START_##PREV - 1; \
252 ID -= DIAG_START_##NAME - DIAG_START_##PREV; \
253 }
254CATEGORY(DRIVER, COMMON)
255CATEGORY(FRONTEND, DRIVER)
256CATEGORY(CODEGEN, FRONTEND)
257CATEGORY(SERIALIZATION, CODEGEN)
258CATEGORY(LEX, SERIALIZATION)
259CATEGORY(PARSE, LEX)
260CATEGORY(AST, PARSE)
261CATEGORY(COMMENT, AST)
262CATEGORY(CROSSTU, COMMENT)
263CATEGORY(SEMA, CROSSTU)
264CATEGORY(ANALYSIS, SEMA)
265CATEGORY(REFACTORING, ANALYSIS)
266CATEGORY(INSTALLAPI, REFACTORING)
267CATEGORY(TRAP, INSTALLAPI)
268#undef CATEGORY
269
270 // Avoid out of bounds reads.
271 if (ID + Offset >= StaticDiagInfoSize)
272 return nullptr;
273
274 assert(ID < StaticDiagInfoSize && Offset < StaticDiagInfoSize);
275
276 const StaticDiagInfoRec *Found = &StaticDiagInfo[ID + Offset];
277 // If the diag id doesn't match we found a different diag, abort. This can
278 // happen when this function is called with an ID that points into a hole in
279 // the diagID space.
280 if (Found->DiagID != DiagID)
281 return nullptr;
282 return Found;
283}
284
285//===----------------------------------------------------------------------===//
286// Custom Diagnostic information
287//===----------------------------------------------------------------------===//
288
289namespace clang {
290namespace diag {
293 std::vector<CustomDiagDesc> DiagInfo;
294 std::map<CustomDiagDesc, unsigned> DiagIDs;
295 std::map<diag::Group, std::vector<unsigned>> GroupToDiags;
296
297public:
298 /// getDescription - Return the description of the specified custom
299 /// diagnostic.
300 const CustomDiagDesc &getDescription(unsigned DiagID) const {
301 assert(DiagID - DIAG_UPPER_LIMIT < DiagInfo.size() &&
302 "Invalid diagnostic ID");
303 return DiagInfo[DiagID - DIAG_UPPER_LIMIT];
304 }
305
307 // Check to see if it already exists.
308 std::map<CustomDiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(D);
309 if (I != DiagIDs.end() && I->first == D)
310 return I->second;
311
312 // If not, assign a new ID.
313 unsigned ID = DiagInfo.size() + DIAG_UPPER_LIMIT;
314 DiagIDs.insert(std::make_pair(D, ID));
315 DiagInfo.push_back(D);
316 if (auto Group = D.GetGroup())
317 GroupToDiags[*Group].emplace_back(ID);
318 return ID;
319 }
320
322 if (auto Diags = GroupToDiags.find(G); Diags != GroupToDiags.end())
323 return Diags->second;
324 return {};
325 }
326};
327
328} // namespace diag
329} // namespace clang
330
333 diag::Severity::Fatal, /*IsUser=*/false, /*IsPragma=*/false);
334
335 if (IsCustomDiag(DiagID)) {
336 Info.setSeverity(
337 CustomDiagInfo->getDescription(DiagID).GetDefaultSeverity());
338 } else if (const StaticDiagInfoRec *StaticInfo = GetDiagInfo(DiagID)) {
339 Info.setSeverity((diag::Severity)StaticInfo->DefaultSeverity);
340
341 if (StaticInfo->WarnNoWerror) {
342 assert(Info.getSeverity() == diag::Severity::Warning &&
343 "Unexpected mapping with no-Werror bit!");
344 Info.setNoWarningAsError(true);
345 }
346 }
347
348 return Info;
349}
350
352 unsigned DiagID) {
353 assert(IsCustomDiag(DiagID));
354 const auto &Diag = CustomDiagInfo->getDescription(DiagID);
355 if (auto Group = Diag.GetGroup()) {
356 GroupInfo GroupInfo = GroupInfos[static_cast<size_t>(*Group)];
357 if (static_cast<diag::Severity>(GroupInfo.Severity) != diag::Severity())
358 Mapping.setSeverity(static_cast<diag::Severity>(GroupInfo.Severity));
360 } else {
361 Mapping.setSeverity(Diag.GetDefaultSeverity());
362 Mapping.setNoWarningAsError(true);
363 Mapping.setNoErrorAsFatal(true);
364 }
365}
366
367/// getCategoryNumberForDiag - Return the category number that a specified
368/// DiagID belongs to, or 0 if no category.
370 if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
371 return Info->Category;
372 return 0;
373}
374
375namespace {
376 // The diagnostic category names.
377 struct StaticDiagCategoryRec {
378 const char *NameStr;
379 uint8_t NameLen;
380
381 StringRef getName() const {
382 return StringRef(NameStr, NameLen);
383 }
384 };
385}
386
387static const StaticDiagCategoryRec CategoryNameTable[] = {
388#define GET_CATEGORY_TABLE
389#define CATEGORY(X, ENUM) { X, STR_SIZE(X, uint8_t) },
390#include "clang/Basic/DiagnosticGroups.inc"
391#undef GET_CATEGORY_TABLE
392 { nullptr, 0 }
393};
394
395/// getNumberOfCategories - Return the number of categories
397 return std::size(CategoryNameTable) - 1;
398}
399
400/// getCategoryNameFromID - Given a category ID, return the name of the
401/// category, an empty string if CategoryID is zero, or null if CategoryID is
402/// invalid.
403StringRef DiagnosticIDs::getCategoryNameFromID(unsigned CategoryID) {
404 if (CategoryID >= getNumberOfCategories())
405 return StringRef();
406 return CategoryNameTable[CategoryID].getName();
407}
408
409
410
413 if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
414 return static_cast<DiagnosticIDs::SFINAEResponse>(Info->SFINAE);
415 return SFINAE_Report;
416}
417
418bool DiagnosticIDs::isDeferrable(unsigned DiagID) {
419 if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
420 return Info->Deferrable;
421 return false;
422}
423
424//===----------------------------------------------------------------------===//
425// Common Diagnostic implementation
426//===----------------------------------------------------------------------===//
427
429
431
432/// getCustomDiagID - Return an ID for a diagnostic with the specified message
433/// and level. If this is the first request for this diagnostic, it is
434/// registered and created, otherwise the existing ID is returned.
435///
436/// \param FormatString A fixed diagnostic format string that will be hashed and
437/// mapped to a unique DiagID.
439 if (!CustomDiagInfo)
440 CustomDiagInfo.reset(new diag::CustomDiagInfo());
441 return CustomDiagInfo->getOrCreateDiagID(Diag);
442}
443
444bool DiagnosticIDs::isWarningOrExtension(unsigned DiagID) const {
445 return DiagID < diag::DIAG_UPPER_LIMIT
446 ? getDiagClass(DiagID) != CLASS_ERROR
447 : CustomDiagInfo->getDescription(DiagID).GetClass() != CLASS_ERROR;
448}
449
450/// Determine whether the given built-in diagnostic ID is a
451/// Note.
452bool DiagnosticIDs::isNote(unsigned DiagID) const {
453 return DiagID < diag::DIAG_UPPER_LIMIT && getDiagClass(DiagID) == CLASS_NOTE;
454}
455
456/// isExtensionDiag - Determine whether the given built-in diagnostic
457/// ID is for an extension of some sort. This also returns EnabledByDefault,
458/// which is set to indicate whether the diagnostic is ignored by default (in
459/// which case -pedantic enables it) or treated as a warning/error by default.
460///
462 bool &EnabledByDefault) const {
463 if (IsCustomDiag(DiagID) || getDiagClass(DiagID) != CLASS_EXTENSION)
464 return false;
465
466 EnabledByDefault =
468 return true;
469}
470
471bool DiagnosticIDs::isDefaultMappingAsError(unsigned DiagID) const {
473}
474
475/// getDescription - Given a diagnostic ID, return a description of the
476/// issue.
477StringRef DiagnosticIDs::getDescription(unsigned DiagID) const {
478 if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
479 return Info->getDescription();
480 assert(CustomDiagInfo && "Invalid CustomDiagInfo");
481 return CustomDiagInfo->getDescription(DiagID).GetDescription();
482}
483
484/// getStableID - Given a diagnostic ID, return the stable ID of the diagnostic.
485std::string DiagnosticIDs::getStableID(unsigned DiagID) const {
486 if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
487 return Info->getStableID().str();
488 assert(CustomDiagInfo && "Invalid CustomDiagInfo");
489 // TODO: Stable IDs for custom diagnostics?
490 // If we have to go through every custom diagnostic and add a stable ID, we
491 // should instead just go replace them all with declared diagnostics.
492 return std::to_string(DiagID);
493}
494
495/// getLegacyStableIDs - Given a diagnostic ID, return the previous stable IDs
496/// of the diagnostic.
498DiagnosticIDs::getLegacyStableIDs(unsigned DiagID) const {
499 if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
500 return Info->getLegacyStableIDs();
501 assert(CustomDiagInfo && "Invalid CustomDiagInfo");
502 // TODO: Stable IDs for custom diagnostics?
503 // If we have to go through every custom diagnostic and add a stable ID, we
504 // should instead just go replace them all with declared diagnostics.
505 return {};
506}
507
509 switch (SV) {
520 }
521 llvm_unreachable("unexpected severity");
522}
523
524/// getDiagnosticLevel - Based on the way the client configured the
525/// DiagnosticsEngine object, classify the specified diagnostic ID into a Level,
526/// by consumable the DiagnosticClient.
528DiagnosticIDs::getDiagnosticLevel(unsigned DiagID, SourceLocation Loc,
529 const DiagnosticsEngine &Diag) const {
530 unsigned DiagClass = getDiagClass(DiagID);
531 if (DiagClass == CLASS_NOTE) return DiagnosticIDs::Note;
532 return toLevel(getDiagnosticSeverity(DiagID, Loc, Diag));
533}
534
535/// Based on the way the client configured the Diagnostic
536/// object, classify the specified diagnostic ID into a Level, consumable by
537/// the DiagnosticClient.
538///
539/// \param Loc The source location we are interested in finding out the
540/// diagnostic state. Can be null in order to query the latest state.
542DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,
543 const DiagnosticsEngine &Diag) const {
545 assert(getDiagClass(DiagID) != CLASS_NOTE);
546
547 // Specific non-error diagnostics may be mapped to various levels from ignored
548 // to error. Errors can only be mapped to fatal.
550
551 // Get the mapping information, or compute it lazily.
552 DiagnosticsEngine::DiagState *State = Diag.GetDiagStateForLoc(Loc);
553 DiagnosticMapping Mapping = State->getOrAddMapping((diag::kind)DiagID);
554
555 // TODO: Can a null severity really get here?
556 if (Mapping.getSeverity() != diag::Severity())
557 Result = Mapping.getSeverity();
558
559 // Upgrade ignored diagnostics if -Weverything is enabled.
560 if (State->EnableAllWarnings && Result == diag::Severity::Ignored &&
561 !Mapping.isUser() &&
562 (IsCustomDiag || getDiagClass(DiagID) != CLASS_REMARK))
564
565 // Ignore -pedantic diagnostics inside __extension__ blocks.
566 // (The diagnostics controlled by -pedantic are the extension diagnostics
567 // that are not enabled by default.)
568 bool EnabledByDefault = false;
569 bool IsExtensionDiag = isExtensionDiag(DiagID, EnabledByDefault);
570 if (Diag.AllExtensionsSilenced && IsExtensionDiag && !EnabledByDefault)
572
573 // For extension diagnostics that haven't been explicitly mapped, check if we
574 // should upgrade the diagnostic. Skip if the user explicitly suppressed it
575 // (e.g. -Wno-foo).
576 if (IsExtensionDiag &&
577 !(Mapping.isUser() && Result == diag::Severity::Ignored)) {
578 if (Mapping.hasNoWarningAsError())
579 Result = std::max(Result,
580 std::min(State->ExtBehavior, diag::Severity::Warning));
581 else
582 Result = std::max(Result, State->ExtBehavior);
583 }
584
585 // At this point, ignored errors can no longer be upgraded.
587 return Result;
588
589 // Honor -w: this disables all messages which are not Error/Fatal by
590 // default (disregarding attempts to upgrade severity from Warning to Error),
591 // as well as disabling all messages which are currently mapped to Warning
592 // (whether by default or downgraded from Error via e.g. -Wno-error or #pragma
593 // diagnostic.)
594 // FIXME: Should -w be ignored for custom warnings without a group?
595 if (State->IgnoreAllWarnings) {
596 if ((!IsCustomDiag || CustomDiagInfo->getDescription(DiagID).GetGroup()) &&
601 }
602
603 // If -Werror is enabled, map warnings to errors unless explicitly disabled.
605 if (State->WarningsAsErrors && !Mapping.hasNoWarningAsError())
607 }
608
609 // If -Wfatal-errors is enabled, map errors to fatal unless explicitly
610 // disabled.
612 if (State->ErrorsAsFatal && !Mapping.hasNoErrorAsFatal())
614 }
615
616 // If explicitly requested, map fatal errors to errors.
618 DiagID != diag::fatal_too_many_errors && Diag.FatalsAsError)
620
621 // Rest of the mappings are only applicable for diagnostics associated with a
622 // SourceLocation, bail out early for others.
623 if (!Diag.hasSourceManager())
624 return Result;
625
626 // We check both the location-specific state and the ForceSystemWarnings
627 // override. In some cases (like template instantiations from system modules),
628 // the location-specific state might have suppression enabled, but the
629 // engine might have an override (e.g. AllowWarningInSystemHeaders) to show
630 // the warning.
631 if (State->SuppressSystemWarnings && !Diag.getForceSystemWarnings() &&
632 shouldSuppressAsSystemWarning(DiagID, Loc, Diag)) {
634 }
635
636 // Clang-diagnostics pragmas always take precedence over suppression mapping.
637 if (!Mapping.isPragma() && Diag.isSuppressedViaMapping(DiagID, Loc))
639
640 return Result;
641}
642
644 unsigned DiagID, SourceLocation Loc, const DiagnosticsEngine &Diag) const {
645 if (!Loc.isValid())
646 return false;
647
649 const auto &SM = Diag.getSourceManager();
650
651 // If we are in a system header, we ignore it.
652 if (SM.isInSystemHeader(SM.getExpansionLoc(Loc))) {
653 bool ShowInSystemHeader = true;
654 if (IsCustomDiag)
655 ShowInSystemHeader =
656 CustomDiagInfo->getDescription(DiagID).ShouldShowInSystemHeader();
657 else if (const StaticDiagInfoRec *Rec = GetDiagInfo(DiagID))
658 ShowInSystemHeader = Rec->WarnShowInSystemHeader;
659
660 if (!ShowInSystemHeader)
661 return true;
662 }
663 // We also ignore warnings due to system macros.
664 if (Loc.isValid()) {
665 bool ShowInSystemMacro = true;
666
667 // FIXME: Respect the "show in system macro" information in the
668 // CustomDiagInfo (which is currently ignored).
669
670 if (const StaticDiagInfoRec *Rec = GetDiagInfo(DiagID))
671 ShowInSystemMacro = Rec->WarnShowInSystemMacro;
672
673 if (!ShowInSystemMacro && SM.isInSystemMacro(Loc))
674 return true;
675 }
676 return false;
677}
678
679DiagnosticIDs::Class DiagnosticIDs::getDiagClass(unsigned DiagID) const {
680 if (IsCustomDiag(DiagID))
681 return Class(CustomDiagInfo->getDescription(DiagID).GetClass());
682
683 if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
684 return Class(Info->Class);
685 return CLASS_INVALID;
686}
687
688#define GET_DIAG_ARRAYS
689#include "clang/Basic/DiagnosticGroups.inc"
690#undef GET_DIAG_ARRAYS
691
692namespace {
693 struct WarningOption {
694 uint16_t NameOffset;
695 uint16_t Members;
696 uint16_t SubGroups;
697 StringRef Documentation;
698
699 StringRef getName() const { return DiagGroupNames[NameOffset]; }
700 };
701}
702
703// Second the table of options, sorted by name for fast binary lookup.
704static const WarningOption OptionTable[] = {
705#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs) \
706 {FlagNameOffset, Members, SubGroups, Docs},
707#include "clang/Basic/DiagnosticGroups.inc"
708#undef DIAG_ENTRY
709};
710
711/// Given a diagnostic group ID, return its documentation.
713 return OptionTable[static_cast<int>(Group)].Documentation;
714}
715
717 return OptionTable[static_cast<int>(Group)].getName();
718}
719
720std::optional<diag::Group>
722 const auto *Found = llvm::partition_point(
723 OptionTable, [=](const WarningOption &O) { return O.getName() < Name; });
724 if (Found == std::end(OptionTable) || Found->getName() != Name)
725 return std::nullopt;
726 return static_cast<diag::Group>(Found - OptionTable);
727}
728
729std::optional<diag::Group>
730DiagnosticIDs::getGroupForDiag(unsigned DiagID) const {
731 if (IsCustomDiag(DiagID)) {
732 assert(CustomDiagInfo);
733 return CustomDiagInfo->getDescription(DiagID).GetGroup();
734 }
735 if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
736 return static_cast<diag::Group>(Info->getOptionGroupIndex());
737 return std::nullopt;
738}
739
740/// getWarningOptionForDiag - Return the lowest-level warning option that
741/// enables the specified diagnostic. If there is no -Wfoo flag that controls
742/// the diagnostic, this returns null.
743StringRef DiagnosticIDs::getWarningOptionForDiag(unsigned DiagID) {
744 if (auto G = getGroupForDiag(DiagID))
745 return getWarningOptionForGroup(*G);
746 return StringRef();
747}
748
749std::vector<std::string> DiagnosticIDs::getDiagnosticFlags() {
750 std::vector<std::string> Res{"-W", "-Wno-"};
751 for (StringRef Name : DiagGroupNames) {
752 if (Name.empty())
753 continue;
754
755 Res.push_back((Twine("-W") + Name).str());
756 Res.push_back((Twine("-Wno-") + Name).str());
757 }
758
759 return Res;
760}
761
762/// Return \c true if any diagnostics were found in this group, even if they
763/// were filtered out due to having the wrong flavor.
765 const WarningOption *Group,
767 diag::CustomDiagInfo *CustomDiagInfo) {
768 // An empty group is considered to be a warning group: we have empty groups
769 // for GCC compatibility, and GCC does not have remarks.
770 if (!Group->Members && !Group->SubGroups)
771 return Flavor == diag::Flavor::Remark;
772
773 bool NotFound = true;
774
775 // Add the members of the option diagnostic set.
776 const int16_t *Member = DiagArrays + Group->Members;
777 for (; *Member != -1; ++Member) {
778 if (GetDiagInfo(*Member)->getFlavor() == Flavor) {
779 NotFound = false;
780 Diags.push_back(*Member);
781 }
782 }
783
784 // Add the members of the subgroups.
785 const int16_t *SubGroups = DiagSubGroups + Group->SubGroups;
786 for (; *SubGroups != (int16_t)-1; ++SubGroups) {
787 if (CustomDiagInfo)
788 llvm::copy(
789 CustomDiagInfo->getDiagsInGroup(static_cast<diag::Group>(*SubGroups)),
790 std::back_inserter(Diags));
791 NotFound &= getDiagnosticsInGroup(Flavor, &OptionTable[(short)*SubGroups],
792 Diags, CustomDiagInfo);
793 }
794
795 return NotFound;
796}
797
798bool
800 SmallVectorImpl<diag::kind> &Diags) const {
801 if (std::optional<diag::Group> G = getGroupForWarningOption(Group)) {
802 if (CustomDiagInfo)
803 llvm::copy(CustomDiagInfo->getDiagsInGroup(*G),
804 std::back_inserter(Diags));
805 return ::getDiagnosticsInGroup(Flavor,
806 &OptionTable[static_cast<unsigned>(*G)],
807 Diags, CustomDiagInfo.get());
808 }
809 return true;
810}
811
812template <class Func>
813static void forEachSubGroupImpl(const WarningOption *Group, Func func) {
814 for (const int16_t *SubGroups = DiagSubGroups + Group->SubGroups;
815 *SubGroups != -1; ++SubGroups) {
816 func(static_cast<size_t>(*SubGroups));
817 forEachSubGroupImpl(&OptionTable[*SubGroups], func);
818 }
819}
820
821template <class Func>
822static void forEachSubGroup(diag::Group Group, Func func) {
823 const WarningOption *WarningOpt = &OptionTable[static_cast<size_t>(Group)];
824 func(static_cast<size_t>(Group));
825 ::forEachSubGroupImpl(WarningOpt, std::move(func));
826}
827
829 if (std::optional<diag::Group> G = getGroupForWarningOption(Group)) {
830 ::forEachSubGroup(*G, [&](size_t SubGroup) {
831 GroupInfos[SubGroup].Severity = static_cast<unsigned>(Sev);
832 });
833 }
834}
835
836void DiagnosticIDs::setGroupNoWarningsAsError(StringRef Group, bool Val) {
837 if (std::optional<diag::Group> G = getGroupForWarningOption(Group)) {
838 ::forEachSubGroup(*G, [&](size_t SubGroup) {
839 GroupInfos[static_cast<size_t>(*G)].HasNoWarningAsError = Val;
840 });
841 }
842}
843
845 std::vector<diag::kind> &Diags) {
846 for (unsigned i = 0; i != StaticDiagInfoSize; ++i)
847 if (StaticDiagInfo[i].getFlavor() == Flavor)
848 Diags.push_back(StaticDiagInfo[i].DiagID);
849}
850
852 StringRef Group) {
853 StringRef Best;
854 unsigned BestDistance = Group.size() + 1; // Maximum threshold.
855 for (const WarningOption &O : OptionTable) {
856 // Don't suggest ignored warning flags.
857 if (!O.Members && !O.SubGroups)
858 continue;
859
860 unsigned Distance = O.getName().edit_distance(Group, true, BestDistance);
861 if (Distance > BestDistance)
862 continue;
863
864 // Don't suggest groups that are not of this kind.
866 if (::getDiagnosticsInGroup(Flavor, &O, Diags, nullptr) || Diags.empty())
867 continue;
868
869 if (Distance == BestDistance) {
870 // Two matches with the same distance, don't prefer one over the other.
871 Best = "";
872 } else if (Distance < BestDistance) {
873 // This is a better match.
874 Best = O.getName();
875 BestDistance = Distance;
876 }
877 }
878
879 return Best;
880}
881
883 unsigned CompatDiagId) {
884 struct CompatDiag {
885 unsigned StdVer;
886 unsigned DiagId;
887 unsigned PreDiagId;
888 };
889
890 // We encode the standard version such that C++98 < C++11 < C++14 etc. The
891 // actual numbers don't really matter for this, but the definitions of the
892 // compat diags in the Tablegen file use the standard version number (i.e.
893 // 98, 11, 14, etc.), so we base the encoding here on that.
894 //
895 // Likewise, for C, we have C99 < C11 < C17 < C23 < C29.
896 //
897 // We do end up with some overlap between C and C++ here, e.g. 2011 is used
898 // for both C11 and C++11, but this doesn't matter since we're never in e.g.
899 // C11 and C++11 mode at the same time (additionally, we should only ever
900 // be issuing C compatibility diagnostics in C mode and likewise for C++).
901#define DIAG_COMPAT_IDS_BEGIN()
902#define DIAG_COMPAT_IDS_END()
903#define DIAG_COMPAT_ID(Value, Name, Std, Diag, DiagPre) \
904 {Std >= 98 ? 1900 + Std : 2000 + Std, diag::Diag, diag::DiagPre},
905 static constexpr CompatDiag Diags[]{
906#include "clang/Basic/DiagnosticAllCompatIDs.inc"
907 };
908#undef DIAG_COMPAT_ID
909#undef DIAG_COMPAT_IDS_BEGIN
910#undef DIAG_COMPAT_IDS_END
911
912 assert(CompatDiagId < std::size(Diags) && "Invalid compat diag id");
913
914 unsigned StdVer = [&] {
915 if (!LangOpts.CPlusPlus) {
916 if (LangOpts.C2y)
917 return 2029;
918 if (LangOpts.C23)
919 return 2023;
920 if (LangOpts.C17)
921 return 2017;
922 if (LangOpts.C11)
923 return 2011;
924 if (LangOpts.C99)
925 return 1999;
926 return 1989;
927 }
928
929 if (LangOpts.CPlusPlus29)
930 return 2029;
931 if (LangOpts.CPlusPlus26)
932 return 2026;
933 if (LangOpts.CPlusPlus23)
934 return 2023;
935 if (LangOpts.CPlusPlus20)
936 return 2020;
937 if (LangOpts.CPlusPlus17)
938 return 2017;
939 if (LangOpts.CPlusPlus14)
940 return 2014;
941 if (LangOpts.CPlusPlus11)
942 return 2011;
943 return 1998;
944 }();
945
946 const CompatDiag &D = Diags[CompatDiagId];
947 return StdVer >= D.StdVer ? D.DiagId : D.PreDiagId;
948}
949
950bool DiagnosticIDs::isUnrecoverable(unsigned DiagID) const {
951 // Only errors may be unrecoverable.
952 if (getDiagClass(DiagID) < CLASS_ERROR)
953 return false;
954
955 if (DiagID == diag::err_unavailable ||
956 DiagID == diag::err_unavailable_message)
957 return false;
958
959 // All ARC errors are currently considered recoverable, with the exception of
960 // err_arc_may_not_respond. This specific error is treated as unrecoverable
961 // because sending a message with an unknown selector could lead to crashes
962 // within CodeGen if the resulting expression is used to initialize a C++
963 // auto variable, where type deduction is required.
964 if (isARCDiagnostic(DiagID) && DiagID != diag::err_arc_may_not_respond)
965 return false;
966
967 if (isCodegenABICheckDiagnostic(DiagID))
968 return false;
969
970 return true;
971}
972
973bool DiagnosticIDs::isARCDiagnostic(unsigned DiagID) {
974 unsigned cat = getCategoryNumberForDiag(DiagID);
975 return DiagnosticIDs::getCategoryNameFromID(cat).starts_with("ARC ");
976}
977
979 unsigned cat = getCategoryNumberForDiag(DiagID);
980 return DiagnosticIDs::getCategoryNameFromID(cat) == "Codegen ABI Check";
981}
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.
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.
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 shouldSuppressAsSystemWarning(unsigned DiagID, SourceLocation Loc, const DiagnosticsEngine &Diag) const
Return true if either of the following two conditions hold:
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.
friend class DiagnosticsEngine
static bool IsCustomDiag(diag::kind Diag)
static unsigned getCompatDiagId(const LangOptions &LangOpts, unsigned CompatDiagId)
Get the appropriate diagnostic Id to use for issuing a compatibility diagnostic.
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:234
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.
Top level wrappers for InstallAPI frontend operations.
bool operator<(DeclarationName LHS, DeclarationName RHS)
Ordering on two declaration names.
@ Result
The result type of a method or function.
Definition TypeBase.h:906
__packed_splat4 int16_t
__packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 __packed_splat2 __packed_splat4 uint16_t
__packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 __packed_splat2 __packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 uint32_t