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