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