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/LLVM.h"
18 #include "llvm/ADT/IntrusiveRefCntPtr.h"
19 #include "llvm/ADT/StringRef.h"
20 #include <optional>
21 #include <vector>
22 
23 namespace clang {
24   class DiagnosticsEngine;
25   class SourceLocation;
26 
27   // Import the diagnostic enums themselves.
28   namespace diag {
29     enum class Group;
30 
31     // Size of each of the diagnostic categories.
32     enum {
33       DIAG_SIZE_COMMON        =  300,
34       DIAG_SIZE_DRIVER        =  400,
35       DIAG_SIZE_FRONTEND      =  150,
36       DIAG_SIZE_SERIALIZATION =  120,
37       DIAG_SIZE_LEX           =  400,
38       DIAG_SIZE_PARSE         =  700,
39       DIAG_SIZE_AST           =  300,
40       DIAG_SIZE_COMMENT       =  100,
41       DIAG_SIZE_CROSSTU       =  100,
42       DIAG_SIZE_SEMA          = 4500,
43       DIAG_SIZE_ANALYSIS      =  100,
44       DIAG_SIZE_REFACTORING   = 1000,
45     };
46     // Start position for diagnostics.
47     enum {
48       DIAG_START_COMMON        =                          0,
49       DIAG_START_DRIVER        = DIAG_START_COMMON        + static_cast<int>(DIAG_SIZE_COMMON),
50       DIAG_START_FRONTEND      = DIAG_START_DRIVER        + static_cast<int>(DIAG_SIZE_DRIVER),
51       DIAG_START_SERIALIZATION = DIAG_START_FRONTEND      + static_cast<int>(DIAG_SIZE_FRONTEND),
52       DIAG_START_LEX           = DIAG_START_SERIALIZATION + static_cast<int>(DIAG_SIZE_SERIALIZATION),
53       DIAG_START_PARSE         = DIAG_START_LEX           + static_cast<int>(DIAG_SIZE_LEX),
54       DIAG_START_AST           = DIAG_START_PARSE         + static_cast<int>(DIAG_SIZE_PARSE),
55       DIAG_START_COMMENT       = DIAG_START_AST           + static_cast<int>(DIAG_SIZE_AST),
56       DIAG_START_CROSSTU       = DIAG_START_COMMENT       + static_cast<int>(DIAG_SIZE_COMMENT),
57       DIAG_START_SEMA          = DIAG_START_CROSSTU       + static_cast<int>(DIAG_SIZE_CROSSTU),
58       DIAG_START_ANALYSIS      = DIAG_START_SEMA          + static_cast<int>(DIAG_SIZE_SEMA),
59       DIAG_START_REFACTORING   = DIAG_START_ANALYSIS      + static_cast<int>(DIAG_SIZE_ANALYSIS),
60       DIAG_UPPER_LIMIT         = DIAG_START_REFACTORING   + static_cast<int>(DIAG_SIZE_REFACTORING)
61     };
62 
63     class CustomDiagInfo;
64 
65     /// All of the diagnostics that can be emitted by the frontend.
66     typedef unsigned kind;
67 
68     // Get typedefs for common diagnostics.
69     enum {
70 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, CATEGORY,      \
71              NOWERROR, SHOWINSYSHEADER, SHOWINSYSMACRO, DEFFERABLE)            \
72   ENUM,
73 #define COMMONSTART
74 #include "clang/Basic/DiagnosticCommonKinds.inc"
75       NUM_BUILTIN_COMMON_DIAGNOSTICS
76 #undef DIAG
77     };
78 
79     /// Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs
80     /// to either Ignore (nothing), Remark (emit a remark), Warning
81     /// (emit a warning) or Error (emit as an error).  It allows clients to
82     /// map ERRORs to Error or Fatal (stop emitting diagnostics after this one).
83     enum class Severity {
84       // NOTE: 0 means "uncomputed".
85       Ignored = 1, ///< Do not present this diagnostic, ignore it.
86       Remark = 2,  ///< Present this diagnostic as a remark.
87       Warning = 3, ///< Present this diagnostic as a warning.
88       Error = 4,   ///< Present this diagnostic as an error.
89       Fatal = 5    ///< Present this diagnostic as a fatal error.
90     };
91 
92     /// Flavors of diagnostics we can emit. Used to filter for a particular
93     /// kind of diagnostic (for instance, for -W/-R flags).
94     enum class Flavor {
95       WarningOrError, ///< A diagnostic that indicates a problem or potential
96                       ///< problem. Can be made fatal by -Werror.
97       Remark          ///< A diagnostic that indicates normal progress through
98                       ///< compilation.
99     };
100   }
101 
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:
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 
129   diag::Severity getSeverity() const { return (diag::Severity)Severity; }
130   void setSeverity(diag::Severity Value) { Severity = (unsigned)Value; }
131 
132   bool isUser() const { return IsUser; }
133   bool isPragma() const { return IsPragma; }
134 
135   bool isErrorOrFatal() const {
136     return getSeverity() == diag::Severity::Error ||
137            getSeverity() == diag::Severity::Fatal;
138   }
139 
140   bool hasNoWarningAsError() const { return HasNoWarningAsError; }
141   void setNoWarningAsError(bool Value) { HasNoWarningAsError = Value; }
142 
143   bool hasNoErrorAsFatal() const { return HasNoErrorAsFatal; }
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.
149   bool wasUpgradedFromWarning() const { return WasUpgradedFromWarning; }
150   void setUpgradedFromWarning(bool Value) { WasUpgradedFromWarning = Value; }
151 
152   /// Serialize this mapping as a raw integer.
153   unsigned serialize() const {
154     return (IsUser << 7) | (IsPragma << 6) | (HasNoWarningAsError << 5) |
155            (HasNoErrorAsFatal << 4) | (WasUpgradedFromWarning << 3) | Severity;
156   }
157   /// Deserialize a mapping.
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 {
181     Ignored, Note, Remark, Warning, Error, Fatal
182   };
183 
184 private:
185   /// Information for uniquing and looking up custom diags.
186   std::unique_ptr<diag::CustomDiagInfo> CustomDiagInfo;
187 
188 public:
189   DiagnosticIDs();
190   ~DiagnosticIDs();
191 
192   /// Return an ID for a diagnostic with the specified format string and
193   /// level.
194   ///
195   /// If this is the first request for this diagnostic, it is registered and
196   /// created, otherwise the existing ID is returned.
197 
198   // FIXME: Replace this function with a create-only facilty like
199   // createCustomDiagIDFromFormatString() to enforce safe usage. At the time of
200   // writing, nearly all callers of this function were invalid.
201   unsigned getCustomDiagID(Level L, StringRef FormatString);
202 
203   //===--------------------------------------------------------------------===//
204   // Diagnostic classification and reporting interfaces.
205   //
206 
207   /// Given a diagnostic ID, return a description of the issue.
208   StringRef getDescription(unsigned DiagID) const;
209 
210   /// Return true if the unmapped diagnostic levelof the specified
211   /// diagnostic ID is a Warning or Extension.
212   ///
213   /// This only works on builtin diagnostics, not custom ones, and is not
214   /// legal to call on NOTEs.
215   static bool isBuiltinWarningOrExtension(unsigned DiagID);
216 
217   /// Return true if the specified diagnostic is mapped to errors by
218   /// default.
219   static bool isDefaultMappingAsError(unsigned DiagID);
220 
221   /// Get the default mapping for this diagnostic.
222   static DiagnosticMapping getDefaultMapping(unsigned DiagID);
223 
224   /// Determine whether the given built-in diagnostic ID is a Note.
225   static bool isBuiltinNote(unsigned DiagID);
226 
227   /// Determine whether the given built-in diagnostic ID is for an
228   /// extension of some sort.
229   static bool isBuiltinExtensionDiag(unsigned DiagID) {
230     bool ignored;
231     return isBuiltinExtensionDiag(DiagID, ignored);
232   }
233 
234   /// Determine whether the given built-in diagnostic ID is for an
235   /// extension of some sort, and whether it is enabled by default.
236   ///
237   /// This also returns EnabledByDefault, which is set to indicate whether the
238   /// diagnostic is ignored by default (in which case -pedantic enables it) or
239   /// treated as a warning/error by default.
240   ///
241   static bool isBuiltinExtensionDiag(unsigned DiagID, bool &EnabledByDefault);
242 
243   /// Given a group ID, returns the flag that toggles the group.
244   /// For example, for Group::DeprecatedDeclarations, returns
245   /// "deprecated-declarations".
246   static StringRef getWarningOptionForGroup(diag::Group);
247 
248   /// Given a diagnostic group ID, return its documentation.
249   static StringRef getWarningOptionDocumentation(diag::Group GroupID);
250 
251   /// Given a group ID, returns the flag that toggles the group.
252   /// For example, for "deprecated-declarations", returns
253   /// Group::DeprecatedDeclarations.
254   static std::optional<diag::Group> getGroupForWarningOption(StringRef);
255 
256   /// Return the lowest-level group that contains the specified diagnostic.
257   static std::optional<diag::Group> getGroupForDiag(unsigned DiagID);
258 
259   /// Return the lowest-level warning option that enables the specified
260   /// diagnostic.
261   ///
262   /// If there is no -Wfoo flag that controls the diagnostic, this returns null.
263   static StringRef getWarningOptionForDiag(unsigned DiagID);
264 
265   /// Return the category number that a specified \p DiagID belongs to,
266   /// or 0 if no category.
267   static unsigned getCategoryNumberForDiag(unsigned DiagID);
268 
269   /// Return the number of diagnostic categories.
270   static unsigned getNumberOfCategories();
271 
272   /// Given a category ID, return the name of the category.
273   static StringRef getCategoryNameFromID(unsigned CategoryID);
274 
275   /// Return true if a given diagnostic falls into an ARC diagnostic
276   /// category.
277   static bool isARCDiagnostic(unsigned DiagID);
278 
279   /// Enumeration describing how the emission of a diagnostic should
280   /// be treated when it occurs during C++ template argument deduction.
281   enum SFINAEResponse {
282     /// The diagnostic should not be reported, but it should cause
283     /// template argument deduction to fail.
284     ///
285     /// The vast majority of errors that occur during template argument
286     /// deduction fall into this category.
287     SFINAE_SubstitutionFailure,
288 
289     /// The diagnostic should be suppressed entirely.
290     ///
291     /// Warnings generally fall into this category.
292     SFINAE_Suppress,
293 
294     /// The diagnostic should be reported.
295     ///
296     /// The diagnostic should be reported. Various fatal errors (e.g.,
297     /// template instantiation depth exceeded) fall into this category.
298     SFINAE_Report,
299 
300     /// The diagnostic is an access-control diagnostic, which will be
301     /// substitution failures in some contexts and reported in others.
302     SFINAE_AccessControl
303   };
304 
305   /// Determines whether the given built-in diagnostic ID is
306   /// for an error that is suppressed if it occurs during C++ template
307   /// argument deduction.
308   ///
309   /// When an error is suppressed due to SFINAE, the template argument
310   /// deduction fails but no diagnostic is emitted. Certain classes of
311   /// errors, such as those errors that involve C++ access control,
312   /// are not SFINAE errors.
313   static SFINAEResponse getDiagnosticSFINAEResponse(unsigned DiagID);
314 
315   /// Whether the diagnostic message can be deferred.
316   ///
317   /// For single source offloading languages, a diagnostic message occurred
318   /// in a device host function may be deferred until the function is sure
319   /// to be emitted.
320   static bool isDeferrable(unsigned DiagID);
321 
322   /// Get the string of all diagnostic flags.
323   ///
324   /// \returns A list of all diagnostics flags as they would be written in a
325   /// command line invocation including their `no-` variants. For example:
326   /// `{"-Wempty-body", "-Wno-empty-body", ...}`
327   static std::vector<std::string> getDiagnosticFlags();
328 
329   /// Get the set of all diagnostic IDs in the group with the given name.
330   ///
331   /// \param[out] Diags - On return, the diagnostics in the group.
332   /// \returns \c true if the given group is unknown, \c false otherwise.
333   bool getDiagnosticsInGroup(diag::Flavor Flavor, StringRef Group,
334                              SmallVectorImpl<diag::kind> &Diags) const;
335 
336   /// Get the set of all diagnostic IDs.
337   static void getAllDiagnostics(diag::Flavor Flavor,
338                                 std::vector<diag::kind> &Diags);
339 
340   /// Get the diagnostic option with the closest edit distance to the
341   /// given group name.
342   static StringRef getNearestOption(diag::Flavor Flavor, StringRef Group);
343 
344 private:
345   /// Classify the specified diagnostic ID into a Level, consumable by
346   /// the DiagnosticClient.
347   ///
348   /// The classification is based on the way the client configured the
349   /// DiagnosticsEngine object.
350   ///
351   /// \param Loc The source location for which we are interested in finding out
352   /// the diagnostic state. Can be null in order to query the latest state.
353   DiagnosticIDs::Level
354   getDiagnosticLevel(unsigned DiagID, SourceLocation Loc,
355                      const DiagnosticsEngine &Diag) const LLVM_READONLY;
356 
357   diag::Severity
358   getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,
359                         const DiagnosticsEngine &Diag) const LLVM_READONLY;
360 
361   /// Used to report a diagnostic that is finally fully formed.
362   ///
363   /// \returns \c true if the diagnostic was emitted, \c false if it was
364   /// suppressed.
365   bool ProcessDiag(DiagnosticsEngine &Diag) const;
366 
367   /// Used to emit a diagnostic that is finally fully formed,
368   /// ignoring suppression.
369   void EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const;
370 
371   /// Whether the diagnostic may leave the AST in a state where some
372   /// invariants can break.
373   bool isUnrecoverable(unsigned DiagID) const;
374 
375   friend class DiagnosticsEngine;
376 };
377 
378 }  // end namespace clang
379 
380 #endif
381