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