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>
25 using namespace clang;
26
27 //===----------------------------------------------------------------------===//
28 // Builtin Diagnostic information
29 //===----------------------------------------------------------------------===//
30
31 namespace {
32
33 struct 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.
38 struct 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
46 const 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
54 extern const StaticDiagInfoRec StaticDiagInfo[];
55
56 // Stored separately from StaticDiagInfoRec to pack better. Otherwise,
57 // StaticDiagInfoRec would have extra padding on 64-bit platforms.
58 const 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
66 enum 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
74 struct 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
getOptionGroupIndex__anon8a6357700111::StaticDiagInfoRec98 unsigned getOptionGroupIndex() const {
99 return OptionGroupIndex;
100 }
101
getDescription__anon8a6357700111::StaticDiagInfoRec102 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
getFlavor__anon8a6357700111::StaticDiagInfoRec109 diag::Flavor getFlavor() const {
110 return Class == CLASS_REMARK ? diag::Flavor::Remark
111 : diag::Flavor::WarningOrError;
112 }
113
operator <__anon8a6357700111::StaticDiagInfoRec114 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.");
129 VALIDATE_DIAG_SIZE(COMMON)
130 VALIDATE_DIAG_SIZE(DRIVER)
131 VALIDATE_DIAG_SIZE(FRONTEND)
132 VALIDATE_DIAG_SIZE(SERIALIZATION)
133 VALIDATE_DIAG_SIZE(LEX)
134 VALIDATE_DIAG_SIZE(PARSE)
135 VALIDATE_DIAG_SIZE(AST)
136 VALIDATE_DIAG_SIZE(COMMENT)
137 VALIDATE_DIAG_SIZE(CROSSTU)
138 VALIDATE_DIAG_SIZE(SEMA)
139 VALIDATE_DIAG_SIZE(ANALYSIS)
140 VALIDATE_DIAG_SIZE(REFACTORING)
141 VALIDATE_DIAG_SIZE(INSTALLAPI)
142 #undef VALIDATE_DIAG_SIZE
143 #undef STRINGIFY_NAME
144
145 const 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
180 static 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.
GetDiagInfo(unsigned DiagID)184 static 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 }
205 CATEGORY(DRIVER, COMMON)
206 CATEGORY(FRONTEND, DRIVER)
207 CATEGORY(SERIALIZATION, FRONTEND)
208 CATEGORY(LEX, SERIALIZATION)
209 CATEGORY(PARSE, LEX)
210 CATEGORY(AST, PARSE)
211 CATEGORY(COMMENT, AST)
212 CATEGORY(CROSSTU, COMMENT)
213 CATEGORY(SEMA, CROSSTU)
214 CATEGORY(ANALYSIS, SEMA)
215 CATEGORY(REFACTORING, ANALYSIS)
216 CATEGORY(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
238 namespace clang {
239 namespace diag {
240 using CustomDiagDesc = DiagnosticIDs::CustomDiagDesc;
241 class CustomDiagInfo {
242 std::vector<CustomDiagDesc> DiagInfo;
243 std::map<CustomDiagDesc, unsigned> DiagIDs;
244 std::map<diag::Group, std::vector<unsigned>> GroupToDiags;
245
246 public:
247 /// getDescription - Return the description of the specified custom
248 /// diagnostic.
getDescription(unsigned DiagID) const249 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
getOrCreateDiagID(DiagnosticIDs::CustomDiagDesc D)255 unsigned getOrCreateDiagID(DiagnosticIDs::CustomDiagDesc D) {
256 // Check to see if it already exists.
257 std::map<CustomDiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(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(std::make_pair(D, ID));
264 DiagInfo.push_back(D);
265 if (auto Group = D.GetGroup())
266 GroupToDiags[*Group].emplace_back(ID);
267 return ID;
268 }
269
getDiagsInGroup(diag::Group G) const270 ArrayRef<unsigned> getDiagsInGroup(diag::Group G) const {
271 if (auto Diags = GroupToDiags.find(G); Diags != GroupToDiags.end())
272 return Diags->second;
273 return {};
274 }
275 };
276
277 } // namespace diag
278 } // namespace clang
279
getDefaultMapping(unsigned DiagID) const280 DiagnosticMapping DiagnosticIDs::getDefaultMapping(unsigned DiagID) const {
281 DiagnosticMapping Info = DiagnosticMapping::Make(
282 diag::Severity::Fatal, /*IsUser=*/false, /*IsPragma=*/false);
283
284 if (IsCustomDiag(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
initCustomDiagMapping(DiagnosticMapping & Mapping,unsigned DiagID)300 void 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.
getCategoryNumberForDiag(unsigned DiagID)318 unsigned DiagnosticIDs::getCategoryNumberForDiag(unsigned DiagID) {
319 if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
320 return Info->Category;
321 return 0;
322 }
323
324 namespace {
325 // The diagnostic category names.
326 struct StaticDiagCategoryRec {
327 const char *NameStr;
328 uint8_t NameLen;
329
getName__anon8a6357700211::StaticDiagCategoryRec330 StringRef getName() const {
331 return StringRef(NameStr, NameLen);
332 }
333 };
334 }
335
336 static 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 { nullptr, 0 }
342 };
343
344 /// getNumberOfCategories - Return the number of categories
getNumberOfCategories()345 unsigned 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.
getCategoryNameFromID(unsigned CategoryID)352 StringRef DiagnosticIDs::getCategoryNameFromID(unsigned CategoryID) {
353 if (CategoryID >= getNumberOfCategories())
354 return StringRef();
355 return CategoryNameTable[CategoryID].getName();
356 }
357
358
359
360 DiagnosticIDs::SFINAEResponse
getDiagnosticSFINAEResponse(unsigned DiagID)361 DiagnosticIDs::getDiagnosticSFINAEResponse(unsigned DiagID) {
362 if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
363 return static_cast<DiagnosticIDs::SFINAEResponse>(Info->SFINAE);
364 return SFINAE_Report;
365 }
366
isDeferrable(unsigned DiagID)367 bool 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
DiagnosticIDs()377 DiagnosticIDs::DiagnosticIDs() {}
378
~DiagnosticIDs()379 DiagnosticIDs::~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.
getCustomDiagID(CustomDiagDesc Diag)387 unsigned DiagnosticIDs::getCustomDiagID(CustomDiagDesc Diag) {
388 if (!CustomDiagInfo)
389 CustomDiagInfo.reset(new diag::CustomDiagInfo());
390 return CustomDiagInfo->getOrCreateDiagID(Diag);
391 }
392
isWarningOrExtension(unsigned DiagID) const393 bool 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.
isNote(unsigned DiagID) const401 bool 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 ///
isExtensionDiag(unsigned DiagID,bool & EnabledByDefault) const410 bool DiagnosticIDs::isExtensionDiag(unsigned DiagID,
411 bool &EnabledByDefault) const {
412 if (IsCustomDiag(DiagID) || getDiagClass(DiagID) != CLASS_EXTENSION)
413 return false;
414
415 EnabledByDefault =
416 getDefaultMapping(DiagID).getSeverity() != diag::Severity::Ignored;
417 return true;
418 }
419
isDefaultMappingAsError(unsigned DiagID) const420 bool 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.
getDescription(unsigned DiagID) const426 StringRef 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
toLevel(diag::Severity SV)433 static 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.
452 DiagnosticIDs::Level
getDiagnosticLevel(unsigned DiagID,SourceLocation Loc,const DiagnosticsEngine & Diag) const453 DiagnosticIDs::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(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.
466 diag::Severity
getDiagnosticSeverity(unsigned DiagID,SourceLocation Loc,const DiagnosticsEngine & Diag) const467 DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,
468 const DiagnosticsEngine &Diag) const {
469 bool IsCustomDiag = DiagnosticIDs::IsCustomDiag(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::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(Result, 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(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)) {
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, Loc))
573 return diag::Severity::Ignored;
574
575 return Result;
576 }
577
getDiagClass(unsigned DiagID) const578 DiagnosticIDs::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
591 namespace {
592 struct WarningOption {
593 uint16_t NameOffset;
594 uint16_t Members;
595 uint16_t SubGroups;
596 StringRef Documentation;
597
getName__anon8a6357700311::WarningOption598 StringRef getName() const { return DiagGroupNames[NameOffset]; }
599 };
600 }
601
602 // Second the table of options, sorted by name for fast binary lookup.
603 static 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.
getWarningOptionDocumentation(diag::Group Group)611 StringRef DiagnosticIDs::getWarningOptionDocumentation(diag::Group Group) {
612 return OptionTable[static_cast<int>(Group)].Documentation;
613 }
614
getWarningOptionForGroup(diag::Group Group)615 StringRef DiagnosticIDs::getWarningOptionForGroup(diag::Group Group) {
616 return OptionTable[static_cast<int>(Group)].getName();
617 }
618
619 std::optional<diag::Group>
getGroupForWarningOption(StringRef Name)620 DiagnosticIDs::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
628 std::optional<diag::Group>
getGroupForDiag(unsigned DiagID) const629 DiagnosticIDs::getGroupForDiag(unsigned DiagID) const {
630 if (IsCustomDiag(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.
getWarningOptionForDiag(unsigned DiagID)642 StringRef DiagnosticIDs::getWarningOptionForDiag(unsigned DiagID) {
643 if (auto G = getGroupForDiag(DiagID))
644 return getWarningOptionForGroup(*G);
645 return StringRef();
646 }
647
getDiagnosticFlags()648 std::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.
getDiagnosticsInGroup(diag::Flavor Flavor,const WarningOption * Group,SmallVectorImpl<diag::kind> & Diags,diag::CustomDiagInfo * CustomDiagInfo)663 static 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(*Member)->getFlavor() == Flavor) {
678 NotFound = false;
679 Diags.push_back(*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 CustomDiagInfo->getDiagsInGroup(static_cast<diag::Group>(*SubGroups)),
689 std::back_inserter(Diags));
690 NotFound &= getDiagnosticsInGroup(Flavor, &OptionTable[(short)*SubGroups],
691 Diags, CustomDiagInfo);
692 }
693
694 return NotFound;
695 }
696
697 bool
getDiagnosticsInGroup(diag::Flavor Flavor,StringRef Group,SmallVectorImpl<diag::kind> & Diags) const698 DiagnosticIDs::getDiagnosticsInGroup(diag::Flavor Flavor, StringRef Group,
699 SmallVectorImpl<diag::kind> &Diags) const {
700 if (std::optional<diag::Group> G = getGroupForWarningOption(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
711 template <class Func>
forEachSubGroupImpl(const WarningOption * Group,Func func)712 static 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
720 template <class Func>
forEachSubGroup(diag::Group Group,Func func)721 static 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
setGroupSeverity(StringRef Group,diag::Severity Sev)727 void DiagnosticIDs::setGroupSeverity(StringRef Group, diag::Severity Sev) {
728 if (std::optional<diag::Group> G = getGroupForWarningOption(Group)) {
729 ::forEachSubGroup(*G, [&](size_t SubGroup) {
730 GroupInfos[SubGroup].Severity = static_cast<unsigned>(Sev);
731 });
732 }
733 }
734
setGroupNoWarningsAsError(StringRef Group,bool Val)735 void DiagnosticIDs::setGroupNoWarningsAsError(StringRef Group, bool Val) {
736 if (std::optional<diag::Group> G = getGroupForWarningOption(Group)) {
737 ::forEachSubGroup(*G, [&](size_t SubGroup) {
738 GroupInfos[static_cast<size_t>(*G)].HasNoWarningAsError = Val;
739 });
740 }
741 }
742
getAllDiagnostics(diag::Flavor Flavor,std::vector<diag::kind> & Diags)743 void 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(StaticDiagInfo[i].DiagID);
748 }
749
getNearestOption(diag::Flavor Flavor,StringRef Group)750 StringRef 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(Group, true, 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, &O, Diags, 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
getCXXCompatDiagId(const LangOptions & LangOpts,unsigned CompatDiagId)781 unsigned 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
isUnrecoverable(unsigned DiagID) const826 bool 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 // All ARC errors are currently considered recoverable, with the exception of
836 // err_arc_may_not_respond. This specific error is treated as unrecoverable
837 // because sending a message with an unknown selector could lead to crashes
838 // within CodeGen if the resulting expression is used to initialize a C++
839 // auto variable, where type deduction is required.
840 if (isARCDiagnostic(DiagID) && DiagID != diag::err_arc_may_not_respond)
841 return false;
842
843 if (isCodegenABICheckDiagnostic(DiagID))
844 return false;
845
846 return true;
847 }
848
isARCDiagnostic(unsigned DiagID)849 bool DiagnosticIDs::isARCDiagnostic(unsigned DiagID) {
850 unsigned cat = getCategoryNumberForDiag(DiagID);
851 return DiagnosticIDs::getCategoryNameFromID(cat).starts_with("ARC ");
852 }
853
isCodegenABICheckDiagnostic(unsigned DiagID)854 bool DiagnosticIDs::isCodegenABICheckDiagnostic(unsigned DiagID) {
855 unsigned cat = getCategoryNumberForDiag(DiagID);
856 return DiagnosticIDs::getCategoryNameFromID(cat) == "Codegen ABI Check";
857 }
858