xref: /llvm-project/clang/include/clang/Basic/DiagnosticIDs.h (revision 0865ecc5150b9a55ba1f9e30b6d463a66ac362a6)
1 //===--- DiagnosticIDs.h - Diagnostic IDs Handling --------------*- C++ -*-===//
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 /// \file
10 /// Defines the Diagnostic IDs-related interfaces.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_CLANG_BASIC_DIAGNOSTICIDS_H
15 #define LLVM_CLANG_BASIC_DIAGNOSTICIDS_H
16 
17 #include "clang/Basic/DiagnosticCategories.h"
18 #include "clang/Basic/LLVM.h"
19 #include "llvm/ADT/IntrusiveRefCntPtr.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/Support/ErrorHandling.h"
22 #include <optional>
23 #include <vector>
24 
25 namespace clang {
26   class DiagnosticsEngine;
27   class DiagnosticBuilder;
28   class SourceLocation;
29 
30   // Import the diagnostic enums themselves.
31   namespace diag {
32     enum class Group;
33 
34     // Size of each of the diagnostic categories.
35     enum {
36       DIAG_SIZE_COMMON        =  300,
37       DIAG_SIZE_DRIVER        =  400,
38       DIAG_SIZE_FRONTEND      =  200,
39       DIAG_SIZE_SERIALIZATION =  120,
40       DIAG_SIZE_LEX           =  400,
41       DIAG_SIZE_PARSE         =  700,
42       DIAG_SIZE_AST           =  300,
43       DIAG_SIZE_COMMENT       =  100,
44       DIAG_SIZE_CROSSTU       =  100,
45       DIAG_SIZE_SEMA          = 5000,
46       DIAG_SIZE_ANALYSIS      =  100,
47       DIAG_SIZE_REFACTORING   = 1000,
48       DIAG_SIZE_INSTALLAPI    =  100,
49     };
50     // Start position for diagnostics.
51     enum {
52       DIAG_START_COMMON        =                          0,
53       DIAG_START_DRIVER        = DIAG_START_COMMON        + static_cast<int>(DIAG_SIZE_COMMON),
54       DIAG_START_FRONTEND      = DIAG_START_DRIVER        + static_cast<int>(DIAG_SIZE_DRIVER),
55       DIAG_START_SERIALIZATION = DIAG_START_FRONTEND      + static_cast<int>(DIAG_SIZE_FRONTEND),
56       DIAG_START_LEX           = DIAG_START_SERIALIZATION + static_cast<int>(DIAG_SIZE_SERIALIZATION),
57       DIAG_START_PARSE         = DIAG_START_LEX           + static_cast<int>(DIAG_SIZE_LEX),
58       DIAG_START_AST           = DIAG_START_PARSE         + static_cast<int>(DIAG_SIZE_PARSE),
59       DIAG_START_COMMENT       = DIAG_START_AST           + static_cast<int>(DIAG_SIZE_AST),
60       DIAG_START_CROSSTU       = DIAG_START_COMMENT       + static_cast<int>(DIAG_SIZE_COMMENT),
61       DIAG_START_SEMA          = DIAG_START_CROSSTU       + static_cast<int>(DIAG_SIZE_CROSSTU),
62       DIAG_START_ANALYSIS      = DIAG_START_SEMA          + static_cast<int>(DIAG_SIZE_SEMA),
63       DIAG_START_REFACTORING   = DIAG_START_ANALYSIS      + static_cast<int>(DIAG_SIZE_ANALYSIS),
64       DIAG_START_INSTALLAPI    = DIAG_START_REFACTORING   + static_cast<int>(DIAG_SIZE_REFACTORING),
65       DIAG_UPPER_LIMIT         = DIAG_START_INSTALLAPI    + static_cast<int>(DIAG_SIZE_INSTALLAPI)
66     };
67 
68     class CustomDiagInfo;
69 
70     /// All of the diagnostics that can be emitted by the frontend.
71     typedef unsigned kind;
72 
73     // Get typedefs for common diagnostics.
74     enum {
75 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, CATEGORY,      \
76              NOWERROR, SHOWINSYSHEADER, SHOWINSYSMACRO, DEFFERABLE)            \
77   ENUM,
78 #define COMMONSTART
79 #include "clang/Basic/DiagnosticCommonKinds.inc"
80       NUM_BUILTIN_COMMON_DIAGNOSTICS
81 #undef DIAG
82     };
83 
84     /// Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs
85     /// to either Ignore (nothing), Remark (emit a remark), Warning
86     /// (emit a warning) or Error (emit as an error).  It allows clients to
87     /// map ERRORs to Error or Fatal (stop emitting diagnostics after this one).
88     enum class Severity : uint8_t {
89       // NOTE: 0 means "uncomputed".
90       Ignored = 1, ///< Do not present this diagnostic, ignore it.
91       Remark = 2,  ///< Present this diagnostic as a remark.
92       Warning = 3, ///< Present this diagnostic as a warning.
93       Error = 4,   ///< Present this diagnostic as an error.
94       Fatal = 5    ///< Present this diagnostic as a fatal error.
95     };
96 
97     /// Flavors of diagnostics we can emit. Used to filter for a particular
98     /// kind of diagnostic (for instance, for -W/-R flags).
99     enum class Flavor {
100       WarningOrError, ///< A diagnostic that indicates a problem or potential
101                       ///< problem. Can be made fatal by -Werror.
102       Remark          ///< A diagnostic that indicates normal progress through
103                       ///< compilation.
104     };
105   }
106 
107 class DiagnosticMapping {
108   LLVM_PREFERRED_TYPE(diag::Severity)
109   unsigned Severity : 3;
110   LLVM_PREFERRED_TYPE(bool)
111   unsigned IsUser : 1;
112   LLVM_PREFERRED_TYPE(bool)
113   unsigned IsPragma : 1;
114   LLVM_PREFERRED_TYPE(bool)
115   unsigned HasNoWarningAsError : 1;
116   LLVM_PREFERRED_TYPE(bool)
117   unsigned HasNoErrorAsFatal : 1;
118   LLVM_PREFERRED_TYPE(bool)
119   unsigned WasUpgradedFromWarning : 1;
120 
121 public:
122   static DiagnosticMapping Make(diag::Severity Severity, bool IsUser,
123                                 bool IsPragma) {
124     DiagnosticMapping Result;
125     Result.Severity = (unsigned)Severity;
126     Result.IsUser = IsUser;
127     Result.IsPragma = IsPragma;
128     Result.HasNoWarningAsError = 0;
129     Result.HasNoErrorAsFatal = 0;
130     Result.WasUpgradedFromWarning = 0;
131     return Result;
132   }
133 
134   diag::Severity getSeverity() const { return (diag::Severity)Severity; }
135   void setSeverity(diag::Severity Value) { Severity = (unsigned)Value; }
136 
137   bool isUser() const { return IsUser; }
138   bool isPragma() const { return IsPragma; }
139 
140   bool isErrorOrFatal() const {
141     return getSeverity() == diag::Severity::Error ||
142            getSeverity() == diag::Severity::Fatal;
143   }
144 
145   bool hasNoWarningAsError() const { return HasNoWarningAsError; }
146   void setNoWarningAsError(bool Value) { HasNoWarningAsError = Value; }
147 
148   bool hasNoErrorAsFatal() const { return HasNoErrorAsFatal; }
149   void setNoErrorAsFatal(bool Value) { HasNoErrorAsFatal = Value; }
150 
151   /// Whether this mapping attempted to map the diagnostic to a warning, but
152   /// was overruled because the diagnostic was already mapped to an error or
153   /// fatal error.
154   bool wasUpgradedFromWarning() const { return WasUpgradedFromWarning; }
155   void setUpgradedFromWarning(bool Value) { WasUpgradedFromWarning = Value; }
156 
157   /// Serialize this mapping as a raw integer.
158   unsigned serialize() const {
159     return (IsUser << 7) | (IsPragma << 6) | (HasNoWarningAsError << 5) |
160            (HasNoErrorAsFatal << 4) | (WasUpgradedFromWarning << 3) | Severity;
161   }
162   /// Deserialize a mapping.
163   static DiagnosticMapping deserialize(unsigned Bits) {
164     DiagnosticMapping Result;
165     Result.IsUser = (Bits >> 7) & 1;
166     Result.IsPragma = (Bits >> 6) & 1;
167     Result.HasNoWarningAsError = (Bits >> 5) & 1;
168     Result.HasNoErrorAsFatal = (Bits >> 4) & 1;
169     Result.WasUpgradedFromWarning = (Bits >> 3) & 1;
170     Result.Severity = Bits & 0x7;
171     return Result;
172   }
173 
174   bool operator==(DiagnosticMapping Other) const {
175     return serialize() == Other.serialize();
176   }
177 };
178 
179 /// Used for handling and querying diagnostic IDs.
180 ///
181 /// Can be used and shared by multiple Diagnostics for multiple translation units.
182 class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
183 public:
184   /// The level of the diagnostic, after it has been through mapping.
185   enum Level : uint8_t { Ignored, Note, Remark, Warning, Error, Fatal };
186 
187   // Diagnostic classes.
188   enum Class {
189     CLASS_INVALID = 0x00,
190     CLASS_NOTE = 0x01,
191     CLASS_REMARK = 0x02,
192     CLASS_WARNING = 0x03,
193     CLASS_EXTENSION = 0x04,
194     CLASS_ERROR = 0x05
195   };
196 
197   static bool IsCustomDiag(diag::kind Diag) {
198     return Diag >= diag::DIAG_UPPER_LIMIT;
199   }
200 
201   class CustomDiagDesc {
202     LLVM_PREFERRED_TYPE(diag::Severity)
203     unsigned DefaultSeverity : 3;
204     LLVM_PREFERRED_TYPE(Class)
205     unsigned DiagClass : 3;
206     LLVM_PREFERRED_TYPE(bool)
207     unsigned ShowInSystemHeader : 1;
208     LLVM_PREFERRED_TYPE(bool)
209     unsigned ShowInSystemMacro : 1;
210     LLVM_PREFERRED_TYPE(bool)
211     unsigned HasGroup : 1;
212     diag::Group Group;
213     std::string Description;
214 
215     auto get_as_tuple() const {
216       return std::tuple(DefaultSeverity, DiagClass, ShowInSystemHeader,
217                         ShowInSystemMacro, HasGroup, Group,
218                         std::string_view{Description});
219     }
220 
221   public:
222     CustomDiagDesc(diag::Severity DefaultSeverity, std::string Description,
223                    unsigned Class = CLASS_WARNING,
224                    bool ShowInSystemHeader = false,
225                    bool ShowInSystemMacro = false,
226                    std::optional<diag::Group> Group = std::nullopt)
227         : DefaultSeverity(static_cast<unsigned>(DefaultSeverity)),
228           DiagClass(Class), ShowInSystemHeader(ShowInSystemHeader),
229           ShowInSystemMacro(ShowInSystemMacro), HasGroup(Group != std::nullopt),
230           Group(Group.value_or(diag::Group{})),
231           Description(std::move(Description)) {}
232 
233     std::optional<diag::Group> GetGroup() const {
234       if (HasGroup)
235         return Group;
236       return std::nullopt;
237     }
238 
239     diag::Severity GetDefaultSeverity() const {
240       return static_cast<diag::Severity>(DefaultSeverity);
241     }
242 
243     Class GetClass() const { return static_cast<Class>(DiagClass); }
244     std::string_view GetDescription() const { return Description; }
245     bool ShouldShowInSystemHeader() const { return ShowInSystemHeader; }
246 
247     friend bool operator==(const CustomDiagDesc &lhs,
248                            const CustomDiagDesc &rhs) {
249       return lhs.get_as_tuple() == rhs.get_as_tuple();
250     }
251 
252     friend bool operator<(const CustomDiagDesc &lhs,
253                           const CustomDiagDesc &rhs) {
254       return lhs.get_as_tuple() < rhs.get_as_tuple();
255     }
256   };
257 
258   struct GroupInfo {
259     LLVM_PREFERRED_TYPE(diag::Severity)
260     unsigned Severity : 3;
261     LLVM_PREFERRED_TYPE(bool)
262     unsigned HasNoWarningAsError : 1;
263   };
264 
265 private:
266   /// Information for uniquing and looking up custom diags.
267   std::unique_ptr<diag::CustomDiagInfo> CustomDiagInfo;
268   std::unique_ptr<GroupInfo[]> GroupInfos = []() {
269     auto GIs = std::make_unique<GroupInfo[]>(
270         static_cast<size_t>(diag::Group::NUM_GROUPS));
271     for (size_t i = 0; i != static_cast<size_t>(diag::Group::NUM_GROUPS); ++i)
272       GIs[i] = {{}, false};
273     return GIs;
274   }();
275 
276 public:
277   DiagnosticIDs();
278   ~DiagnosticIDs();
279 
280   /// Return an ID for a diagnostic with the specified format string and
281   /// level.
282   ///
283   /// If this is the first request for this diagnostic, it is registered and
284   /// created, otherwise the existing ID is returned.
285 
286   // FIXME: Replace this function with a create-only facilty like
287   // createCustomDiagIDFromFormatString() to enforce safe usage. At the time of
288   // writing, nearly all callers of this function were invalid.
289   unsigned getCustomDiagID(CustomDiagDesc Diag);
290 
291   // TODO: Deprecate this once all uses are removed from LLVM
292   // [[deprecated("Use a CustomDiagDesc instead of a Level")]]
293   unsigned getCustomDiagID(Level Level, StringRef Message) {
294     return getCustomDiagID([&]() -> CustomDiagDesc {
295       switch (Level) {
296       case DiagnosticIDs::Level::Ignored:
297         return {diag::Severity::Ignored, std::string(Message), CLASS_WARNING,
298                 /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true};
299       case DiagnosticIDs::Level::Note:
300         return {diag::Severity::Fatal, std::string(Message), CLASS_NOTE,
301                 /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true};
302       case DiagnosticIDs::Level::Remark:
303         return {diag::Severity::Remark, std::string(Message), CLASS_REMARK,
304                 /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true};
305       case DiagnosticIDs::Level::Warning:
306         return {diag::Severity::Warning, std::string(Message), CLASS_WARNING,
307                 /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true};
308       case DiagnosticIDs::Level::Error:
309         return {diag::Severity::Error, std::string(Message), CLASS_ERROR,
310                 /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true};
311       case DiagnosticIDs::Level::Fatal:
312         return {diag::Severity::Fatal, std::string(Message), CLASS_ERROR,
313                 /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true};
314       }
315       llvm_unreachable("Fully covered switch above!");
316     }());
317   }
318 
319   //===--------------------------------------------------------------------===//
320   // Diagnostic classification and reporting interfaces.
321   //
322 
323   /// Given a diagnostic ID, return a description of the issue.
324   StringRef getDescription(unsigned DiagID) const;
325 
326   /// Return true if the unmapped diagnostic levelof the specified
327   /// diagnostic ID is a Warning or Extension.
328   ///
329   /// This is not legal to call on NOTEs.
330   bool isWarningOrExtension(unsigned DiagID) const;
331 
332   /// Return true if the specified diagnostic is mapped to errors by
333   /// default.
334   bool isDefaultMappingAsError(unsigned DiagID) const;
335 
336   /// Get the default mapping for this diagnostic.
337   DiagnosticMapping getDefaultMapping(unsigned DiagID) const;
338 
339   void initCustomDiagMapping(DiagnosticMapping &, unsigned DiagID);
340 
341   /// Determine whether the given diagnostic ID is a Note.
342   bool isNote(unsigned DiagID) const;
343 
344   /// Determine whether the given diagnostic ID is for an
345   /// extension of some sort.
346   bool isExtensionDiag(unsigned DiagID) const {
347     bool ignored;
348     return isExtensionDiag(DiagID, ignored);
349   }
350 
351   /// Determine whether the given diagnostic ID is for an
352   /// extension of some sort, and whether it is enabled by default.
353   ///
354   /// This also returns EnabledByDefault, which is set to indicate whether the
355   /// diagnostic is ignored by default (in which case -pedantic enables it) or
356   /// treated as a warning/error by default.
357   ///
358   bool isExtensionDiag(unsigned DiagID, bool &EnabledByDefault) const;
359 
360   /// Given a group ID, returns the flag that toggles the group.
361   /// For example, for Group::DeprecatedDeclarations, returns
362   /// "deprecated-declarations".
363   static StringRef getWarningOptionForGroup(diag::Group);
364 
365   /// Given a diagnostic group ID, return its documentation.
366   static StringRef getWarningOptionDocumentation(diag::Group GroupID);
367 
368   void setGroupSeverity(StringRef Group, diag::Severity);
369   void setGroupNoWarningsAsError(StringRef Group, bool);
370 
371   /// Given a group ID, returns the flag that toggles the group.
372   /// For example, for "deprecated-declarations", returns
373   /// Group::DeprecatedDeclarations.
374   static std::optional<diag::Group> getGroupForWarningOption(StringRef);
375 
376   /// Return the lowest-level group that contains the specified diagnostic.
377   std::optional<diag::Group> getGroupForDiag(unsigned DiagID) const;
378 
379   /// Return the lowest-level warning option that enables the specified
380   /// diagnostic.
381   ///
382   /// If there is no -Wfoo flag that controls the diagnostic, this returns null.
383   StringRef getWarningOptionForDiag(unsigned DiagID);
384 
385   /// Return the category number that a specified \p DiagID belongs to,
386   /// or 0 if no category.
387   static unsigned getCategoryNumberForDiag(unsigned DiagID);
388 
389   /// Return the number of diagnostic categories.
390   static unsigned getNumberOfCategories();
391 
392   /// Given a category ID, return the name of the category.
393   static StringRef getCategoryNameFromID(unsigned CategoryID);
394 
395   /// Return true if a given diagnostic falls into an ARC diagnostic
396   /// category.
397   static bool isARCDiagnostic(unsigned DiagID);
398 
399   /// Return true if a given diagnostic is a codegen-time ABI check.
400   static bool isCodegenABICheckDiagnostic(unsigned DiagID);
401 
402   /// Enumeration describing how the emission of a diagnostic should
403   /// be treated when it occurs during C++ template argument deduction.
404   enum SFINAEResponse {
405     /// The diagnostic should not be reported, but it should cause
406     /// template argument deduction to fail.
407     ///
408     /// The vast majority of errors that occur during template argument
409     /// deduction fall into this category.
410     SFINAE_SubstitutionFailure,
411 
412     /// The diagnostic should be suppressed entirely.
413     ///
414     /// Warnings generally fall into this category.
415     SFINAE_Suppress,
416 
417     /// The diagnostic should be reported.
418     ///
419     /// The diagnostic should be reported. Various fatal errors (e.g.,
420     /// template instantiation depth exceeded) fall into this category.
421     SFINAE_Report,
422 
423     /// The diagnostic is an access-control diagnostic, which will be
424     /// substitution failures in some contexts and reported in others.
425     SFINAE_AccessControl
426   };
427 
428   /// Determines whether the given built-in diagnostic ID is
429   /// for an error that is suppressed if it occurs during C++ template
430   /// argument deduction.
431   ///
432   /// When an error is suppressed due to SFINAE, the template argument
433   /// deduction fails but no diagnostic is emitted. Certain classes of
434   /// errors, such as those errors that involve C++ access control,
435   /// are not SFINAE errors.
436   static SFINAEResponse getDiagnosticSFINAEResponse(unsigned DiagID);
437 
438   /// Whether the diagnostic message can be deferred.
439   ///
440   /// For single source offloading languages, a diagnostic message occurred
441   /// in a device host function may be deferred until the function is sure
442   /// to be emitted.
443   static bool isDeferrable(unsigned DiagID);
444 
445   /// Get the string of all diagnostic flags.
446   ///
447   /// \returns A list of all diagnostics flags as they would be written in a
448   /// command line invocation including their `no-` variants. For example:
449   /// `{"-Wempty-body", "-Wno-empty-body", ...}`
450   static std::vector<std::string> getDiagnosticFlags();
451 
452   /// Get the set of all diagnostic IDs in the group with the given name.
453   ///
454   /// \param[out] Diags - On return, the diagnostics in the group.
455   /// \returns \c true if the given group is unknown, \c false otherwise.
456   bool getDiagnosticsInGroup(diag::Flavor Flavor, StringRef Group,
457                              SmallVectorImpl<diag::kind> &Diags) const;
458 
459   /// Get the set of all diagnostic IDs.
460   static void getAllDiagnostics(diag::Flavor Flavor,
461                                 std::vector<diag::kind> &Diags);
462 
463   /// Get the diagnostic option with the closest edit distance to the
464   /// given group name.
465   static StringRef getNearestOption(diag::Flavor Flavor, StringRef Group);
466 
467 private:
468   /// Classify the specified diagnostic ID into a Level, consumable by
469   /// the DiagnosticClient.
470   ///
471   /// The classification is based on the way the client configured the
472   /// DiagnosticsEngine object.
473   ///
474   /// \param Loc The source location for which we are interested in finding out
475   /// the diagnostic state. Can be null in order to query the latest state.
476   DiagnosticIDs::Level
477   getDiagnosticLevel(unsigned DiagID, SourceLocation Loc,
478                      const DiagnosticsEngine &Diag) const LLVM_READONLY;
479 
480   diag::Severity
481   getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,
482                         const DiagnosticsEngine &Diag) const LLVM_READONLY;
483 
484   Class getDiagClass(unsigned DiagID) const;
485 
486   /// Used to report a diagnostic that is finally fully formed.
487   ///
488   /// \returns \c true if the diagnostic was emitted, \c false if it was
489   /// suppressed.
490   bool ProcessDiag(DiagnosticsEngine &Diag,
491                    const DiagnosticBuilder &DiagBuilder) const;
492 
493   /// Used to emit a diagnostic that is finally fully formed,
494   /// ignoring suppression.
495   void EmitDiag(DiagnosticsEngine &Diag, const DiagnosticBuilder &DiagBuilder,
496                 Level DiagLevel) const;
497 
498   /// Whether the diagnostic may leave the AST in a state where some
499   /// invariants can break.
500   bool isUnrecoverable(unsigned DiagID) const;
501 
502   friend class DiagnosticsEngine;
503 };
504 
505 }  // end namespace clang
506 
507 #endif
508