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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang/lib/Basic/DiagnosticIDs.cpp