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