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