use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::io::{self, Write};
use super::diagnostics::{
ClangSourceLocation, DiagID, DiagSeverity, DiagnosticEngine, DiagnosticNote, DiagnosticOptions,
FixItHint, SourceRange, StructuredDiagnostic,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DiagnosticCategory {
pub name: String,
pub flag: String,
pub members: Vec<DiagID>,
pub enabled_by_default: bool,
pub parent: Option<String>,
pub description: String,
}
impl DiagnosticCategory {
pub fn new(name: &str, flag: &str, description: &str) -> Self {
Self {
name: name.to_string(),
flag: flag.to_string(),
members: Vec::new(),
enabled_by_default: true,
parent: None,
description: description.to_string(),
}
}
pub fn with_default_enabled(mut self, enabled: bool) -> Self {
self.enabled_by_default = enabled;
self
}
pub fn with_parent(mut self, parent: &str) -> Self {
self.parent = Some(parent.to_string());
self
}
pub fn add_member(&mut self, id: DiagID) {
self.members.push(id);
}
}
pub struct DiagnosticCategoryRegistry {
pub categories: BTreeMap<String, DiagnosticCategory>,
pub id_to_category: HashMap<DiagID, Vec<String>>,
pub group_to_members: HashMap<String, Vec<String>>,
}
impl DiagnosticCategoryRegistry {
pub fn new() -> Self {
let mut reg = Self {
categories: BTreeMap::new(),
id_to_category: HashMap::new(),
group_to_members: HashMap::new(),
};
reg.register_all();
reg
}
fn register_all(&mut self) {
self.register_wall_categories();
self.register_wextra_categories();
self.register_wpedantic_categories();
self.register_wconversion_categories();
self.register_wshadow_categories();
self.register_wunused_categories();
self.register_wformat_categories();
self.register_wsign_compare_categories();
self.register_wswitch_categories();
self.register_wtautological_categories();
self.register_wnull_dereference_categories();
self.register_wreturn_type_categories();
self.register_wuninitialized_categories();
self.register_wparentheses_categories();
self.register_wmisleading_indentation_categories();
self.register_wimplicit_fallthrough_categories();
self.register_wstrict_aliasing_categories();
self.register_wcast_categories();
self.register_wdouble_promotion_categories();
self.register_wfloat_equal_categories();
self.register_wmissing_field_init_categories();
self.register_wmissing_prototypes_categories();
self.register_wstrict_prototypes_categories();
self.register_wold_style_definition_categories();
self.register_wpointer_categories();
self.register_wunreachable_code_categories();
self.register_wwrite_strings_categories();
self.register_syntax_error_categories();
self.register_type_error_categories();
self.register_semantic_error_categories();
self.register_linker_error_categories();
self.register_preprocessor_error_categories();
self.register_template_error_categories();
self.register_constraint_error_categories();
self.register_note_categories();
self.register_fixit_categories();
}
fn register_wall_categories(&mut self) {
let cats = vec![
("unused-variable", "-Wunused-variable", "Unused variable declaration"),
("unused-parameter", "-Wunused-parameter", "Unused function parameter"),
("unused-function", "-Wunused-function", "Unused static function"),
("unused-value", "-Wunused-value", "Expression result unused"),
("unused-label", "-Wunused-label", "Unused label"),
("sign-compare", "-Wsign-compare", "Comparison between signed and unsigned"),
("format", "-Wformat", "printf/scanf format string issues"),
("format-security", "-Wformat-security", "Format string is not a string literal"),
("parentheses", "-Wparentheses", "Suggest parentheses around expression"),
("switch", "-Wswitch", "Switch statement missing enum cases"),
("implicit-fallthrough", "-Wimplicit-fallthrough", "Implicit fallthrough in switch"),
("tautological-compare", "-Wtautological-compare", "Self-comparison always true/false"),
("return-type", "-Wreturn-type", "Non-void function without return"),
("uninitialized", "-Wuninitialized", "Use of uninitialized variable"),
("array-bounds", "-Warray-bounds", "Array index out of bounds"),
("char-subscripts", "-Wchar-subscripts", "Array subscript is of type 'char'"),
("comment", "-Wcomment", "Multi-line comment issues"),
("int-conversion", "-Wint-conversion", "Implicit integer conversion"),
("int-in-bool-context", "-Wint-in-bool-context", "Integer used as boolean"),
("main", "-Wmain", "Suspicious main function signature"),
("misleading-indentation", "-Wmisleading-indentation", "Misleading indentation"),
("missing-braces", "-Wmissing-braces", "Subobject initializer missing braces"),
("multichar", "-Wmultichar", "Multi-character character constant"),
("nonnull", "-Wnonnull", "Null passed to nonnull parameter"),
("pointer-sign", "-Wpointer-sign", "Pointer signedness mismatch"),
("reorder", "-Wreorder", "Member initializer order mismatch"),
("return-local-addr", "-Wreturn-local-addr", "Returning address of local"),
("sequence-point", "-Wsequence-point", "Unsequenced modification"),
("shift-count-overflow", "-Wshift-count-overflow", "Shift count >= type width"),
("shift-negative-value", "-Wshift-negative-value", "Shifting negative value"),
("sizeof-array-argument", "-Wsizeof-array-argument", "sizeof on array parameter"),
("sizeof-pointer-memaccess", "-Wsizeof-pointer-memaccess", "Suspicious memaccess size"),
("strict-aliasing", "-Wstrict-aliasing", "Type-punning violates aliasing rules"),
("unknown-pragmas", "-Wunknown-pragmas", "Unknown pragma directive"),
("unused-but-set-variable", "-Wunused-but-set-variable", "Variable set but not used"),
("unused-result", "-Wunused-result", "Unused result of function"),
("unused-local-typedef", "-Wunused-local-typedef", "Unused local typedef"),
("delete-non-virtual-dtor", "-Wdelete-non-virtual-dtor", "Delete on non-virtual destructor"),
("delete-abstract-non-virtual-dtor", "-Wdelete-abstract-non-virtual-dtor", "Delete on abstract class with non-virtual destructor"),
("memset-transposed-args", "-Wmemset-transposed-args", "Possibly transposed memset args"),
("suspicious-bzero", "-Wsuspicious-bzero", "Possibly incorrect bzero size"),
("absolute-value", "-Wabsolute-value", "Wrong type argument to absolute value"),
("self-assign", "-Wself-assign", "Explicit self-assignment"),
("self-assign-overloaded", "-Wself-assign-overloaded", "Self-assignment with overloaded operator="),
("self-move", "-Wself-move", "Explicit self-move"),
("unused-private-field", "-Wunused-private-field", "Private field not used"),
("unused-lambda-capture", "-Wunused-lambda-capture", "Lambda capture not used"),
("unused-const-variable", "-Wunused-const-variable", "Unused const variable"),
("infinite-recursion", "-Winfinite-recursion", "Function infinitely recurses"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(true)
.with_parent("all");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("all".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wextra_categories(&mut self) {
let cats = vec![
("unused-parameter", "-Wunused-parameter", "Unused function parameter (extra)"),
("sign-compare-extra", "-Wsign-compare", "Additional signed/unsigned compare"),
("missing-field-initializers", "-Wmissing-field-initializers", "Missing initializer for struct field"),
("type-limits", "-Wtype-limits", "Comparison always true due to type limits"),
("empty-body", "-Wempty-body", "Empty body in if/else/for/while"),
("enum-compare", "-Wenum-compare", "Comparison of different enum types"),
("implicit-fallthrough-per-function", "-Wimplicit-fallthrough-per-function", "Fallthrough per function"),
("ignored-qualifiers", "-Wignored-qualifiers", "Ignored type qualifier on return type"),
("init-self", "-Winit-self", "Variable initialized with itself"),
("logical-not-parentheses", "-Wlogical-not-parentheses", "! binds tighter than comparison"),
("logical-op-parentheses", "-Wlogical-op-parentheses", "Logical operator inside other expression"),
("missing-include-dirs", "-Wmissing-include-dirs", "Missing include directories"),
("mismatched-tags", "-Wmismatched-tags", "Mismatched struct/class tags"),
("missing-variable-declarations", "-Wmissing-variable-declarations", "No previous declaration for global"),
("pointer-compare", "-Wpointer-compare", "Comparison between pointer and zero"),
("redundant-decls", "-Wredundant-decls", "Redundant declarations"),
("sometimes-uninitialized", "-Wsometimes-uninitialized", "Variable sometimes uninitialized"),
("tautological-undefined-compare", "-Wtautological-undefined-compare", "Undefined behavior comparison"),
("unused-function-extra", "-Wunused-function", "Additional unused function checks"),
("unused-label-extra", "-Wunused-label", "Additional unused label checks"),
("unused-value-extra", "-Wunused-value", "Additional unused value checks"),
("vexing-parse", "-Wvexing-parse", "Most vexing parse ambiguity"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false)
.with_parent("extra");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("extra".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wpedantic_categories(&mut self) {
let cats = vec![
("pedantic", "-Wpedantic", "Issues mandated by the language standard"),
("long-long", "-Wlong-long", "ISO C90 does not support long long"),
("variadic-macros", "-Wvariadic-macros", "Variadic macros are a C99 feature"),
("c99-extensions", "-Wc99-extensions", "C99 extension used"),
("c11-extensions", "-Wc11-extensions", "C11 extension used"),
("gnu-extensions", "-Wgnu", "GNU extension used"),
("extra-semi", "-Wextra-semi", "Extra semicolon after member function"),
("zero-length-array", "-Wzero-length-array", "ISO C forbids zero-size array"),
("vla", "-Wvla", "Variable length array used"),
("c++20-extensions", "-Wc++20-extensions", "C++20 extension used"),
("c++23-extensions", "-Wc++23-extensions", "C++23 extension used"),
("c++20-compat", "-Wc++20-compat", "C++ construct incompatible with C++20"),
("c++20-compat-pedantic", "-Wc++20-compat-pedantic", "C++ pedantic compat"),
("anonymous-struct", "-Wpedantic", "Anonymous struct is a GNU extension"),
("pointer-arith-void", "-Wpointer-arith", "Arithmetic on void* is a GNU ext"),
("four-char-constants", "-Wfour-char-constants", "Multi-character char constant"),
("dollar-in-identifier", "-Wdollar-in-identifier-extension", "$ in identifier"),
("keyword-macro", "-Wkeyword-macro", "Keyword used as macro name"),
("variadic-macros-pedantic", "-Wvariadic-macros", "Variadic macro pedantic"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false)
.with_parent("pedantic");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("pedantic".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wconversion_categories(&mut self) {
let cats = vec![
("conversion", "-Wconversion", "Implicit type conversion may alter value"),
("conversion-null", "-Wconversion-null", "Conversion of null pointer constant"),
("sign-conversion", "-Wsign-conversion", "Implicit sign conversion"),
("float-conversion", "-Wfloat-conversion", "Implicit float to int conversion"),
("shorten-64-to-32", "-Wshorten-64-to-32", "Implicit 64 to 32 bit shortening"),
("bitfield-conversion", "-Wbitfield-conversion", "Bit-field conversion truncation"),
("bool-conversion", "-Wbool-conversion", "Conversion to bool"),
("enum-conversion", "-Wenum-conversion", "Implicit enum conversion"),
("implicit-int-conversion", "-Wimplicit-int-conversion", "Implicit int truncation"),
("implicit-float-conversion", "-Wimplicit-float-conversion", "Implicit float truncation"),
("nullable-to-nonnull-conversion", "-Wnullable-to-nonnull-conversion", "Nullable to nonnull"),
("int-to-void-pointer-cast", "-Wint-to-void-pointer-cast", "Int to void* conversion"),
("implicit-fixed-point-conversion", "-Wimplicit-fixed-point-conversion", "Fixed point conv"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false)
.with_parent("conversion");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("conversion".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wshadow_categories(&mut self) {
let cats = vec![
("shadow", "-Wshadow", "Local variable shadows another variable"),
("shadow-field", "-Wshadow-field", "Constructor parameter shadows field"),
("shadow-field-in-constructor", "-Wshadow-field-in-constructor", "Shadow field in ctor"),
("shadow-field-in-constructor-modified", "-Wshadow-field-in-constructor-modified", "Shadow field modified"),
("shadow-ivar", "-Wshadow-ivar", "Local shadows instance variable"),
("shadow-all", "-Wshadow-all", "All shadow warnings"),
("shadow-uncaptured-local", "-Wshadow-uncaptured-local", "Local could be captured"),
("shadow-field-in-nested-class", "-Wshadow-field-in-nested-class", "Field shadow nested"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false)
.with_parent("shadow");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("shadow".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wunused_categories(&mut self) {
let cats = vec![
("unused-variable", "-Wunused-variable", "Unused variable"),
("unused-parameter", "-Wunused-parameter", "Unused parameter"),
("unused-function", "-Wunused-function", "Unused static function"),
("unused-label", "-Wunused-label", "Unused label"),
("unused-value", "-Wunused-value", "Expression result unused"),
("unused-result", "-Wunused-result", "Function result unused"),
("unused-local-typedef", "-Wunused-local-typedef", "Unused local typedef"),
("unused-private-field", "-Wunused-private-field", "Unused private field"),
("unused-lambda-capture", "-Wunused-lambda-capture", "Unused lambda capture"),
("unused-const-variable", "-Wunused-const-variable", "Unused const variable"),
("unused-but-set-variable", "-Wunused-but-set-variable", "Set but unused variable"),
("unused-but-set-parameter", "-Wunused-but-set-parameter", "Set but unused param"),
("unused-macro", "-Wunused-macro", "Unused macro definition"),
("unused-member-function", "-Wunused-member-function", "Unused member function"),
("unused-template", "-Wunused-template", "Unused template"),
("unused-exception-parameter", "-Wunused-exception-parameter", "Unused catch param"),
("unused-volatile", "-Wunused-volatile", "Unused volatile variable"),
("unused-import", "-Wunused-import", "Unused import declaration"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(name == "unused-variable")
.with_parent("unused");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("unused".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wformat_categories(&mut self) {
let cats = vec![
("format", "-Wformat", "printf/scanf format string validation"),
("format-security", "-Wformat-security", "Format string not a literal"),
("format-extra-args", "-Wformat-extra-args", "Extra arguments to format"),
("format-zero-length", "-Wformat-zero-length", "Zero-length format string"),
("format-nonliteral", "-Wformat-nonliteral", "Format string not a literal"),
("format-contains-nul", "-Wformat-contains-nul", "Format string contains NUL"),
("format-invalid-specifier", "-Wformat-invalid-specifier", "Invalid format specifier"),
("format-overflow", "-Wformat-overflow", "Format overflow at compile time"),
("format-pedantic", "-Wformat-pedantic", "Pedantic format checks"),
("format-y2k", "-Wformat-y2k", "Two-digit year in format"),
("format-nonstandard", "-Wformat-non-standard", "Non-standard format specifier"),
("format-truncation", "-Wformat-truncation", "Output may be truncated"),
("format-signedness", "-Wformat-signedness", "Sign mismatch in format arg"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(name == "format")
.with_parent("format");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("format".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wsign_compare_categories(&mut self) {
let cats = vec![
("sign-compare", "-Wsign-compare", "Signed/unsigned comparison"),
("sign-conversion", "-Wsign-conversion", "Implicit sign conversion"),
("sign-promo", "-Wsign-promo", "Overload resolution promotes unsigned"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("sign-compare");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("sign-compare".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wswitch_categories(&mut self) {
let cats = vec![
("switch", "-Wswitch", "Switch missing enum cases"),
("switch-enum", "-Wswitch-enum", "Switch missing named enum cases"),
("switch-default", "-Wswitch-default", "Switch missing default label"),
("switch-bool", "-Wswitch-bool", "Switch on boolean value"),
("switch-out-of-range", "-Wswitch-out-of-range", "Case value out of range"),
("switch-unreachable-default", "-Wswitch-unreachable-default", "Default unreachable"),
("covered-switch-default", "-Wcovered-switch-default", "All cases covered, default unused"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(name == "switch")
.with_parent("switch");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("switch".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wtautological_categories(&mut self) {
let cats = vec![
("tautological-compare", "-Wtautological-compare", "Self-comparison is tautological"),
("tautological-constant-compare", "-Wtautological-constant-compare", "Constant compare"),
("tautological-pointer-compare", "-Wtautological-pointer-compare", "Pointer compare"),
("tautological-overlap-compare", "-Wtautological-overlap-compare", "Overlap compare"),
("tautological-undefined-compare", "-Wtautological-undefined-compare", "UB compare"),
("tautological-type-limit-compare", "-Wtautological-type-limit-compare", "Type limit compare"),
("tautological-bitwise-compare", "-Wtautological-bitwise-compare", "Bitwise compare"),
("tautological-out-of-range", "-Wtautological-out-of-range", "Out of range compare"),
("tautological-unsigned-zero-compare", "-Wtautological-unsigned-zero-compare", "Unsigned >= 0"),
("tautological-unsigned-enum-zero-compare", "-Wtautological-unsigned-enum-zero-compare", "Unsigned enum >= 0"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(name == "tautological-compare")
.with_parent("tautological");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("tautological".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wnull_dereference_categories(&mut self) {
let cats = vec![
("null-dereference", "-Wnull-dereference", "Null pointer dereference"),
("null-arithmetic", "-Wnull-arithmetic", "Arithmetic on null pointer"),
("null-conversion", "-Wnull-conversion", "Null passed where non-null expected"),
("nonnull", "-Wnonnull", "Null passed to nonnull attribute parameter"),
("nonnull-compare", "-Wnonnull-compare", "Nonnull parameter compared to null"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("null-dereference");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("null-dereference".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wreturn_type_categories(&mut self) {
let cats = vec![
("return-type", "-Wreturn-type", "Non-void function without return"),
("return-local-addr", "-Wreturn-local-addr", "Returning address of local"),
("return-stack-address", "-Wreturn-stack-address", "Returning stack address"),
("return-std-move", "-Wreturn-std-move", "Unnecessary std::move in return"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(name == "return-type")
.with_parent("return-type");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("return-type".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wuninitialized_categories(&mut self) {
let cats = vec![
("uninitialized", "-Wuninitialized", "Use of uninitialized variable"),
("sometimes-uninitialized", "-Wsometimes-uninitialized", "Sometimes uninitialized"),
("conditional-uninitialized", "-Wconditional-uninitialized", "Conditionally uninit"),
("maybe-uninitialized", "-Wmaybe-uninitialized", "Possibly uninitialized"),
("static-self-init", "-Wstatic-self-init", "Static variable self-initialized"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(name == "uninitialized")
.with_parent("uninitialized");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("uninitialized".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wparentheses_categories(&mut self) {
let cats = vec![
("parentheses", "-Wparentheses", "Suggest parentheses in expression"),
("parentheses-equality", "-Wparentheses-equality", "= in conditional context"),
("bitwise-parentheses", "-Wbitwise-conditional-parentheses", "& or | in conditional"),
("logical-op-parentheses", "-Wlogical-op-parentheses", "&& within ||"),
("shift-op-parentheses", "-Wshift-op-parentheses", "Shift-parentheses"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(name == "parentheses")
.with_parent("parentheses");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("parentheses".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wmisleading_indentation_categories(&mut self) {
let cats = vec![
("misleading-indentation", "-Wmisleading-indentation", "Misleading indentation"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(true);
self.categories.insert(name.to_string(), cat);
}
}
fn register_wimplicit_fallthrough_categories(&mut self) {
let cats = vec![
("implicit-fallthrough", "-Wimplicit-fallthrough", "Unannotated fallthrough"),
("implicit-fallthrough-per-function", "-Wimplicit-fallthrough-per-function",
"Unannotated fallthrough per function"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(name == "implicit-fallthrough");
self.categories.insert(name.to_string(), cat);
}
}
fn register_wstrict_aliasing_categories(&mut self) {
let cats = vec![
("strict-aliasing", "-Wstrict-aliasing", "Type-punning may violate aliasing"),
("strict-aliasing=0", "-Wstrict-aliasing=0", "Strict aliasing level 0"),
("strict-aliasing=1", "-Wstrict-aliasing=1", "Strict aliasing level 1"),
("strict-aliasing=2", "-Wstrict-aliasing=2", "Strict aliasing level 2"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("strict-aliasing");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("strict-aliasing".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wcast_categories(&mut self) {
let cats = vec![
("cast-align", "-Wcast-align", "Cast increases required alignment"),
("cast-qual", "-Wcast-qual", "Cast drops const/volatile qualifier"),
("bad-function-cast", "-Wbad-function-cast", "Function cast"),
("cast-function-type", "-Wcast-function-type", "Incompatible function cast"),
("old-style-cast", "-Wold-style-cast", "C-style cast used in C++"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("cast");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("cast".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wdouble_promotion_categories(&mut self) {
let cats = vec![
("double-promotion", "-Wdouble-promotion", "Implicit float to double promotion"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false);
self.categories.insert(name.to_string(), cat);
}
}
fn register_wfloat_equal_categories(&mut self) {
let cats = vec![
("float-equal", "-Wfloat-equal", "Floating-point equality comparison"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false);
self.categories.insert(name.to_string(), cat);
}
}
fn register_wmissing_field_init_categories(&mut self) {
let cats = vec![
("missing-field-initializers", "-Wmissing-field-initializers",
"Missing initializer for field"),
("excess-initializers", "-Wexcess-initializers", "Too many initializers"),
("override-init", "-Woverride-init", "Initializer overrides previous"),
("designated-init", "-Wdesignated-init", "C99 designated initializer"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("missing-field");
self.categories.insert(name.to_string(), cat);
}
}
fn register_wmissing_prototypes_categories(&mut self) {
let cats = vec![
("missing-prototypes", "-Wmissing-prototypes", "No previous prototype for global function"),
("missing-declarations", "-Wmissing-declarations", "No previous declaration for global"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false);
self.categories.insert(name.to_string(), cat);
}
}
fn register_wstrict_prototypes_categories(&mut self) {
let cats = vec![
("strict-prototypes", "-Wstrict-prototypes", "Function declaration without prototype"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false);
self.categories.insert(name.to_string(), cat);
}
}
fn register_wold_style_definition_categories(&mut self) {
let cats = vec![
("old-style-definition", "-Wold-style-definition", "K&R-style function definition"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false);
self.categories.insert(name.to_string(), cat);
}
}
fn register_wpointer_categories(&mut self) {
let cats = vec![
("pointer-arith", "-Wpointer-arith", "Arithmetic on void* or function pointer"),
("pointer-sign", "-Wpointer-sign", "Pointer signedness mismatch in assignment"),
("pointer-to-int-cast", "-Wpointer-to-int-cast", "Pointer to integer cast"),
("int-to-pointer-cast", "-Wint-to-pointer-cast", "Integer to pointer cast"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("pointer");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("pointer".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_wunreachable_code_categories(&mut self) {
let cats = vec![
("unreachable-code", "-Wunreachable-code", "Unreachable code"),
("unreachable-code-break", "-Wunreachable-code-break", "Unreachable break"),
("unreachable-code-return", "-Wunreachable-code-return", "Unreachable return"),
("unreachable-code-loop-increment", "-Wunreachable-code-loop-increment",
"Unreachable loop increment"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false)
.with_parent("unreachable-code");
self.categories.insert(name.to_string(), cat);
}
}
fn register_wwrite_strings_categories(&mut self) {
let cats = vec![
("write-strings", "-Wwrite-strings",
"String literal conversion discards const qualifier"),
("c++11-compat-deprecated-writable-strings", "-Wc++11-compat-deprecated-writable-strings",
"Writable string literals deprecated in C++11"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_default_enabled(false);
self.categories.insert(name.to_string(), cat);
}
}
fn register_syntax_error_categories(&mut self) {
let cats = vec![
("expected-semi-after-expr", "-Werror", "Expected ';' after expression"),
("expected-semi-after-decl", "-Werror", "Expected ';' after declaration"),
("expected-expression", "-Werror", "Expected expression"),
("expected-type", "-Werror", "Expected type"),
("expected-identifier", "-Werror", "Expected identifier"),
("expected-rbrace", "-Werror", "Expected '}'"),
("expected-rparen", "-Werror", "Expected ')'"),
("expected-rbracket", "-Werror", "Expected ']'"),
("expected-comma", "-Werror", "Expected ','"),
("expected-colon", "-Werror", "Expected ':'"),
("expected-func-body", "-Werror", "Expected function body"),
("expected-statement", "-Werror", "Expected statement"),
("expected-declaration", "-Werror", "Expected declaration"),
("expected-parameter", "-Werror", "Expected parameter declaration"),
("expected-template-arg", "-Werror", "Expected template argument"),
("expected-qualified-name", "-Werror", "Expected qualified name"),
("expected-class-name", "-Werror", "Expected class name"),
("expected-enum-name", "-Werror", "Expected enum name"),
("expected-constant-expr", "-Werror", "Expected constant expression"),
("expected-integer-literal", "-Werror", "Expected integer literal"),
("expected-string-literal", "-Werror", "Expected string literal"),
("expected-initializer", "-Werror", "Expected initializer"),
("expected-equal", "-Werror", "Expected '='"),
("missing-token", "-Werror", "Missing token"),
("extraneous-token", "-Werror", "Extraneous token before end of statement"),
("unexpected-token", "-Werror", "Unexpected token"),
("mismatched-delimiter", "-Werror", "Mismatched delimiter"),
("invalid-suffix", "-Werror", "Invalid suffix on literal"),
("invalid-digit", "-Werror", "Invalid digit in numeric constant"),
("duplicate-decl-specifier", "-Werror", "Duplicate declaration specifier"),
("duplicate-member", "-Werror", "Duplicate member name"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("syntax-error");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("syntax-error".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_type_error_categories(&mut self) {
let cats = vec![
("undeclared-identifier", "-Werror", "Use of undeclared identifier"),
("undeclared-var-use", "-Werror", "Use of undeclared variable"),
("undeclared-function", "-Werror", "Call to undeclared function"),
("undeclared-type", "-Werror", "Unknown type name"),
("redefinition", "-Werror", "Redefinition of identifier"),
("redefinition-different-type", "-Werror", "Redefinition with different type"),
("redefinition-of-label", "-Werror", "Redefinition of label"),
("redefinition-of-enumerator", "-Werror", "Redefinition of enumerator"),
("redefinition-of-typedef", "-Werror", "Redefinition of typedef"),
("conflicting-types", "-Werror", "Conflicting type declarations"),
("ambiguous-reference", "-Werror", "Ambiguous reference"),
("cannot-initialize", "-Werror", "Cannot initialize variable"),
("cannot-convert", "-Werror", "Cannot convert between types"),
("cannot-convert-to-pointer", "-Werror", "Cannot convert to pointer type"),
("cannot-convert-to-reference", "-Werror", "Cannot convert to reference type"),
("cannot-bind-bitfield", "-Werror", "Cannot bind non-const ref to bit-field"),
("cannot-bind-to-temporary", "-Werror", "Cannot bind to temporary"),
("incompatible-types", "-Werror", "Incompatible types"),
("incompatible-pointer-types", "-Werror", "Incompatible pointer types"),
("invalid-operand-types", "-Werror", "Invalid operand types for operator"),
("no-viable-conversion", "-Werror", "No viable conversion"),
("abstract-class", "-Werror", "Cannot instantiate abstract class"),
("invalid-use-of-incomplete-type", "-Werror", "Use of incomplete type"),
("field-has-incomplete-type", "-Werror", "Field has incomplete type"),
("arithmetic-on-void-ptr", "-Werror", "Arithmetic on pointer to void"),
("assignment-to-const", "-Werror", "Cannot assign to const variable"),
("assignment-to-array", "-Werror", "Cannot assign to array"),
("member-ref-on-non-struct", "-Werror", "Member reference on non-struct type"),
("invalid-use-of-this", "-Werror", "Invalid use of 'this'"),
("invalid-use-of-void", "-Werror", "Invalid use of void expression"),
("invalid-array-size", "-Werror", "Invalid array size"),
("negative-array-size", "-Werror", "Array size is negative"),
("zero-size-array", "-Werror", "Zero-size array not allowed in ISO C"),
("flexible-array-member", "-Werror", "Flexible array member not at end"),
("void-var-decl", "-Werror", "Variable has incomplete type 'void'"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("type-error");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("type-error".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_semantic_error_categories(&mut self) {
let cats = vec![
("no-matching-function", "-Werror", "No matching function for call"),
("no-matching-member-function", "-Werror", "No matching member function"),
("no-matching-constructor", "-Werror", "No matching constructor"),
("too-many-args", "-Werror", "Too many arguments to function call"),
("too-few-args", "-Werror", "Too few arguments to function call"),
("call-to-deleted", "-Werror", "Call to deleted function"),
("call-to-inaccessible", "-Werror", "Call to inaccessible member"),
("call-to-pure-virtual", "-Werror", "Call to pure virtual function"),
("call-to-non-function", "-Werror", "Called object is not a function"),
("default-arg-mismatch", "-Werror", "Default argument mismatch"),
("access-private", "-Werror", "Inaccessible private member"),
("access-protected", "-Werror", "Inaccessible protected member"),
("inaccessible-base", "-Werror", "Inaccessible base class"),
("override-mismatch", "-Werror", "Function marked override does not override"),
("final-override", "-Werror", "Cannot override final function"),
("constexpr-body-no-return", "-Werror", "constexpr body has no return stmt"),
("constexpr-var-requires-init", "-Werror", "constexpr variable needs init"),
("static-assert-failed", "-Werror", "Static assertion failed"),
("duplicate-case", "-Werror", "Duplicate case value in switch"),
("duplicate-default", "-Werror", "Multiple default labels in switch"),
("duplicate-base", "-Werror", "Duplicate base class"),
("multiple-storage-classes", "-Werror", "Multiple storage classes"),
("thread-non-static", "-Werror", "thread_local on non-static variable"),
("constexpr-never-constant", "-Werror", "Constexpr function never constant"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("semantic-error");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("semantic-error".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_linker_error_categories(&mut self) {
let cats = vec![
("undefined-reference", "-Werror", "Undefined reference to symbol"),
("duplicate-symbol", "-Werror", "Duplicate symbol definition"),
("unresolved-symbol", "-Werror", "Unresolved external symbol"),
("mismatched-visibility", "-Werror", "Symbol visibility mismatch"),
("missing-entry-point", "-Werror", "Missing entry point (main)"),
("incompatible-object-format", "-Werror", "Incompatible object file format"),
("wrong-architecture", "-Werror", "Object file built for wrong architecture"),
("missing-library", "-Werror", "Cannot find library"),
("missing-dso-needed", "-Werror", "Shared library referenced but not linked"),
("circular-dependency", "-Werror", "Circular library dependency"),
("version-mismatch", "-Werror", "Library ABI version mismatch"),
("weak-symbol-override", "-Werror", "Weak symbol overridden multiple times"),
("common-symbol-size-mismatch", "-Werror", "Common symbol size mismatch"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("linker-error");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("linker-error".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_preprocessor_error_categories(&mut self) {
let cats = vec![
("pp-hash-error", "-Werror", "#error directive encountered"),
("pp-include-not-found", "-Werror", "Cannot open include file"),
("pp-include-too-deep", "-Werror", "Include nested too deeply"),
("pp-macro-redefined", "-Werror", "Macro redefined"),
("pp-macro-not-defined", "-Werror", "Macro not defined for #undef"),
("pp-unterminated-comment", "-Werror", "Unterminated /* comment"),
("pp-unterminated-string", "-Werror", "Missing terminating \" character"),
("pp-unterminated-char", "-Werror", "Missing terminating ' character"),
("pp-invalid-token", "-Werror", "Invalid preprocessing token"),
("pp-nested-comment", "-Werror", "'/*' within block comment"),
("pp-bad-paste", "-Werror", "Pasting formed invalid token"),
("pp-bad-stringify", "-Werror", "# operator not followed by macro param"),
("pp-extra-endif", "-Werror", "#endif without #if"),
("pp-missing-endif", "-Werror", "Unterminated #if/#ifdef/#ifndef"),
("pp-else-after-else", "-Werror", "#else after #else"),
("pp-elif-after-else", "-Werror", "#elif after #else"),
("pp-empty-file-name", "-Werror", "Empty file name in #include"),
("pp-invalid-directive", "-Werror", "Invalid preprocessing directive"),
("pp-nonportable-path", "-Werror", "Non-portable path in #include"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("preprocessor-error");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("preprocessor-error".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_template_error_categories(&mut self) {
let cats = vec![
("template-arg-deduction-failed", "-Werror", "Template argument deduction failed"),
("cannot-deduce-template-args", "-Werror", "Cannot deduce template arguments"),
("ambiguous-template-args", "-Werror", "Ambiguous template arguments"),
("recursive-template", "-Werror", "Recursive template instantiation"),
("template-depth-exceeded", "-Werror", "Template instantiation depth exceeded"),
("incomplete-template-def", "-Werror", "Template definition not visible"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("template-error");
self.categories.insert(name.to_string(), cat);
self.group_to_members
.entry("template-error".to_string())
.or_default()
.push(name.to_string());
}
}
fn register_constraint_error_categories(&mut self) {
let cats = vec![
("constraint-not-satisfied", "-Werror", "Constraint not satisfied"),
("concept-check-failed", "-Werror", "Concept check failed"),
("requires-clause-violated", "-Werror", "Requires clause violated"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc)
.with_parent("constraint-error");
self.categories.insert(name.to_string(), cat);
}
}
fn register_note_categories(&mut self) {
let cats = vec![
("previous-definition", "-Wnote", "Previous definition is here"),
("previous-declaration", "-Wnote", "Previous declaration is here"),
("previous-builtin-declaration", "-Wnote", "Built-in declaration is here"),
("candidate-function", "-Wnote", "Candidate function not viable"),
("candidate-constructor", "-Wnote", "Candidate constructor not viable"),
("candidate-template", "-Wnote", "Candidate template ignored"),
("forward-declaration", "-Wnote", "Forward declaration of X is here"),
("declaration-here", "-Wnote", "Declaration here"),
("definition-here", "-Wnote", "Definition is here"),
("in-instantiation-of", "-Wnote", "In instantiation of template"),
("in-expansion-of-macro", "-Wnote", "In expansion of macro"),
("in-substitution-of", "-Wnote", "In substitution of template args"),
("while-deducing-template-args", "-Wnote", "While deducing template args"),
("macro-defined-here", "-Wnote", "Macro defined here"),
("macro-expanded-here", "-Wnote", "Macro expanded from here"),
("preexpanded-from-macro", "-Wnote", "Expanded from macro"),
("template-declared-here", "-Wnote", "Template declared here"),
("specialization-here", "-Wnote", "Specialization declared here"),
("parameter-here", "-Wnote", "Parameter declared here"),
("base-class-here", "-Wnote", "Base class declared here"),
("member-declared-here", "-Wnote", "Member declared here"),
("type-mismatch", "-Wnote", "Type mismatch: expected X, got Y"),
("value-here", "-Wnote", "Value is here"),
("uninitialized-here", "-Wnote", "Variable may be uninitialized when used here"),
("assigned-here", "-Wnote", "Variable assigned here"),
("condition-here", "-Wnote", "Condition evaluated here"),
("loop-here", "-Wnote", "Loop body is here"),
("field-declared-here", "-Wnote", "Field declared here"),
("enum-declared-here", "-Wnote", "Enum declared here"),
("typedef-here", "-Wnote", "Typedef declared here"),
("namespace-here", "-Wnote", "Namespace declared here"),
("conversion-candidate", "-Wnote", "Conversion candidate"),
("return-type-here", "-Wnote", "Return type context"),
("note-in-evaluating-expr", "-Wnote", "In evaluation of constant expression"),
("note-sfinae-explanation", "-Wnote", "SFINAE explanation"),
("note-module-import", "-Wnote", "Module imported here"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc);
self.categories.insert(name.to_string(), cat);
}
}
fn register_fixit_categories(&mut self) {
let cats = vec![
("fixit-insert-semicolon", "-Wfixit", "Insert ';'"),
("fixit-insert-rbrace", "-Wfixit", "Insert '}'"),
("fixit-insert-rparen", "-Wfixit", "Insert ')'"),
("fixit-insert-rbracket", "-Wfixit", "Insert ']'"),
("fixit-insert-cast", "-Wfixit", "Insert cast"),
("fixit-insert-const", "-Wfixit", "Insert const qualifier"),
("fixit-insert-static", "-Wfixit", "Insert static"),
("fixit-replace-identifier", "-Wfixit", "Replace identifier"),
("fixit-replace-assign-with-compare", "-Wfixit", "Replace '=' with '=='"),
("fixit-insert-missing-include", "-Wfixit", "Insert #include directive"),
("fixit-insert-typename", "-Wfixit", "Insert 'typename' keyword"),
("fixit-remove-token", "-Wfixit", "Remove extraneous token"),
("fixit-wrap-in-parens", "-Wfixit", "Wrap expression in parentheses"),
("fixit-insert-braces", "-Wfixit", "Insert braces around statement"),
("fixit-insert-null-check", "-Wfixit", "Insert null check"),
("fixit-delete-keyword", "-Wfixit", "Delete keyword to resolve ambiguity"),
("fixit-change-type", "-Wfixit", "Change type to resolve conversion"),
];
for (name, flag, desc) in cats {
let cat = DiagnosticCategory::new(name, flag, desc);
self.categories.insert(name.to_string(), cat);
}
}
pub fn categories_for(&self, id: DiagID) -> Vec<&str> {
self.id_to_category
.get(&id)
.map(|v| v.iter().map(|s| s.as_str()).collect())
.unwrap_or_default()
}
pub fn len(&self) -> usize {
self.categories.len()
}
pub fn is_empty(&self) -> bool {
self.categories.is_empty()
}
}
impl Default for DiagnosticCategoryRegistry {
fn default() -> Self {
Self::new()
}
}
pub struct FullDiagnosticMessageCatalog;
impl FullDiagnosticMessageCatalog {
pub fn message_for(id: DiagID) -> &'static str {
match id {
DiagID::ErrExpectedSemiAfterExpr => "expected ';' after expression",
DiagID::ErrExpectedSemiAfterDecl => "expected ';' after declaration",
DiagID::ErrExpectedExpression => "expected expression",
DiagID::ErrExpectedType => "expected type",
DiagID::ErrExpectedIdentifier => "expected identifier",
DiagID::ErrExpectedRBrace => "expected '}'",
DiagID::ErrExpectedRParen => "expected ')'",
DiagID::ErrExpectedRBracket => "expected ']'",
DiagID::ErrExpectedComma => "expected ','",
DiagID::ErrExpectedColon => "expected ':'",
DiagID::ErrExpectedFuncBody => "expected function body after function declarator",
DiagID::ErrExpectedStatement => "expected statement",
DiagID::ErrExpectedDeclaration => "expected declaration",
DiagID::ErrExpectedParameter => "expected parameter declarator",
DiagID::ErrExpectedTemplateArg => "expected template argument",
DiagID::ErrExpectedQualifiedName => "expected qualified name",
DiagID::ErrExpectedNamespaceName => "expected namespace name",
DiagID::ErrExpectedClassName => "expected class name",
DiagID::ErrExpectedEnumName => "expected enum name",
DiagID::ErrExpectedConstantExpression => "expected constant expression",
DiagID::ErrExpectedStringLiteral => "expected string literal",
DiagID::ErrExpectedIntegerLiteral => "expected integer literal",
DiagID::ErrExpectedFloatingLiteral => "expected floating-point literal",
DiagID::ErrExpectedCharLiteral => "expected character literal",
DiagID::ErrExpectedBooleanLiteral => "expected boolean literal",
DiagID::ErrExpectedNullPtr => "expected null pointer literal",
DiagID::ErrExpectedThis => "expected 'this'",
DiagID::ErrExpectedBaseSpecifier => "expected base specifier",
DiagID::ErrExpectedMemberName => "expected member name or ';' after declaration",
DiagID::ErrExpectedInitializer => "expected initializer",
DiagID::ErrExpectedEqual => "expected '='",
DiagID::ErrExpectedArrow => "expected '->'",
DiagID::ErrExpectedDot => "expected '.'",
DiagID::ErrExpectedStar => "expected '*'",
DiagID::ErrExpectedAmpersand => "expected '&'",
DiagID::ErrExpectedOperator => "expected operator",
DiagID::ErrExpectedLoopBody => "expected loop body",
DiagID::ErrExpectedSwitchBody => "expected switch body",
DiagID::ErrExpectedCaseOrDefault => "expected 'case' or 'default'",
DiagID::ErrExpectedCatch => "expected 'catch'",
DiagID::ErrUndeclaredIdentifier => "use of undeclared identifier '{}'",
DiagID::ErrUndeclaredVarUse => "use of undeclared variable '{}'",
DiagID::ErrUndeclaredFunction => "call to undeclared function '{}'; ISO C99 and later do not support implicit function declarations",
DiagID::ErrUndeclaredType => "unknown type name '{}'",
DiagID::ErrUndeclaredLabel => "use of undeclared label '{}'",
DiagID::ErrUndeclaredNamespace => "no member named '{}' in namespace '{}'",
DiagID::ErrUndeclaredTemplate => "use of undeclared template '{}'",
DiagID::ErrRedefinition => "redefinition of '{}'",
DiagID::ErrRedefinitionDifferentType => "redefinition of '{}' with a different type: '{}' vs '{}'",
DiagID::ErrRedefinitionOfLabel => "redefinition of label '{}'",
DiagID::ErrRedefinitionOfEnumerator => "redefinition of enumerator '{}'",
DiagID::ErrRedefinitionOfTypedef => "redefinition of '{}' as different kind of symbol",
DiagID::ErrRedefinitionOfModule => "redefinition of module '{}'",
DiagID::ErrRedeclaration => "redeclaration of '{}'",
DiagID::ErrRedeclarationDifferentKind => "redeclaration of '{}' as different kind of symbol",
DiagID::ErrConflictTypes => "conflicting types for '{}'",
DiagID::ErrAmbiguousReference => "reference to '{}' is ambiguous",
DiagID::ErrAmbiguousOverload => "call to '{}' is ambiguous",
DiagID::ErrAmbiguousConversion => "ambiguous conversion from '{}' to '{}'",
DiagID::ErrAmbiguousBase => "ambiguous conversion from derived class '{}' to base class '{}'",
DiagID::ErrCannotInitialize => "cannot initialize a variable of type '{}' with an lvalue of type '{}'",
DiagID::ErrCannotInitializeWithInitList => "cannot initialize with an initializer list",
DiagID::ErrCannotConvert => "cannot convert '{}' to '{}' without a conversion operator",
DiagID::ErrCannotConvertToPointer => "cannot convert '{}' to pointer type '{}'",
DiagID::ErrCannotConvertToReference => "cannot convert '{}' to reference type '{}'",
DiagID::ErrCannotConvertFunctionToPointer => "cannot convert function type '{}' to '{}'",
DiagID::ErrCannotConvertVectorType => "cannot convert between vector values of different size",
DiagID::ErrCannotBindBitfield => "cannot bind non-const lvalue reference to a bit-field",
DiagID::ErrCannotBindToTemporary => "cannot bind non-const lvalue reference of type '{}' to a temporary of type '{}'",
DiagID::ErrCannotPassNonTriviallyCopyable => "cannot pass object of non-trivially-copyable type '{}' through variadic function",
DiagID::ErrCannotCatchType => "cannot catch '{}' by value because it is abstract",
DiagID::ErrCannotThrowType => "cannot throw object of incomplete type '{}'",
DiagID::ErrIncompatibleTypes => "incompatible types: assigning to '{}' from '{}'",
DiagID::ErrIncompatiblePointerTypes => "incompatible pointer types passing '{}' to parameter of type '{}'",
DiagID::ErrIncompatibleIntegerConversion => "incompatible integer to pointer conversion passing '{}' to parameter of type '{}'",
DiagID::ErrIncompatiblePointerToInteger => "incompatible pointer to integer conversion assigning to '{}' from '{}'",
DiagID::ErrIncompatibleOperandTypes => "invalid operands to binary expression ('{}' and '{}')",
DiagID::ErrIncompatibleVectorTypes => "incompatible vector types",
DiagID::ErrIncompatibleBlockPointer => "incompatible block pointer types",
DiagID::ErrNoMatchingFunction => "no matching function for call to '{}'",
DiagID::ErrNoMatchingMemberFunction => "no matching member function for call to '{}'",
DiagID::ErrNoMatchingConstructor => "no matching constructor for initialization of '{}'",
DiagID::ErrNoMatchingConversion => "no matching conversion for '{}'",
DiagID::ErrTooManyArgs => "too many arguments to function call, expected {}, have {}",
DiagID::ErrTooFewArgs => "too few arguments to function call, expected {}, have {}",
DiagID::ErrCallToDeleted => "call to deleted function '{}'",
DiagID::ErrCallToInaccessible => "calling '{}' is inaccessible",
DiagID::ErrCallToPureVirtual => "call to pure virtual member function '{}'",
DiagID::ErrCallToNonCallable => "called object type '{}' is not a function or function pointer",
DiagID::ErrCallToNonFunction => "called object is not a function or function pointer",
DiagID::ErrDefaultArgMismatch => "default argument mismatch",
DiagID::ErrTemplateArgDeductionFailed => "template argument deduction failed for '{}'",
DiagID::ErrCannotDeduceTemplateArgs => "cannot deduce template arguments for '{}'",
DiagID::ErrAmbiguousTemplateArgs => "ambiguous template arguments for '{}'",
DiagID::ErrNoViableConversion => "no viable conversion from '{}' to '{}'",
DiagID::ErrInvalidExplicitSpecifier => "invalid explicit specifier",
DiagID::ErrRecursiveTemplateInstantiation => "recursive template instantiation exceeded maximum depth of {}",
DiagID::ErrUnaryAmpersandOnTemporary => "taking the address of a temporary object of type '{}'",
DiagID::ErrUnaryAmpersandOnBitfield => "taking the address of a bit-field",
DiagID::ErrUnaryAmpersandOnRegister => "taking the address of a register variable is not allowed",
DiagID::ErrDivisionByZeroConst => "division by zero in constant expression",
DiagID::ErrShiftByNegative => "shift count is negative",
DiagID::ErrShiftByExcess => "shift count >= width of type",
DiagID::ErrModuloByZero => "remainder by zero in constant expression",
DiagID::ErrAssignmentToConst => "cannot assign to variable with const-qualified type '{}'",
DiagID::ErrAssignmentToArray => "array type '{}' is not assignable",
DiagID::ErrAssignmentToFunction => "cannot assign to a function",
DiagID::ErrIncrementOfBoolean => "ISO C++17 does not allow incrementing expression of type bool",
DiagID::ErrIncrementOfEnum => "cannot increment value of type '{}'",
DiagID::ErrIncrementOfReadOnly => "cannot increment a read-only reference",
DiagID::ErrDecrementOfBoolean => "ISO C++17 does not allow decrementing expression of type bool",
DiagID::ErrArithmeticOnVoidPtr => "arithmetic on a pointer to void",
DiagID::ErrArithmeticOnFunctionPtr => "arithmetic on a pointer to function",
DiagID::ErrSubscriptOnArray => "subscript of array type '{}'",
DiagID::ErrSubscriptOnPointer => "subscript of pointer to incomplete type '{}'",
DiagID::ErrSubscriptOnNonArray => "subscripted value is not an array, pointer, or vector",
DiagID::ErrMemberRefOnNonStruct => "member reference base type '{}' is not a structure or union",
DiagID::ErrMemberRefOnNonClass => "member reference type '{}' is not a pointer",
DiagID::ErrAbstractClass => "cannot instantiate abstract class '{}'",
DiagID::ErrAbstractDecl => "cannot declare variable of abstract type '{}'",
DiagID::ErrPureVirtualCall => "pure virtual function '{}' called",
DiagID::ErrPrivateMember => "'{}' is a private member of '{}'",
DiagID::ErrProtectedMember => "'{}' is a protected member of '{}'",
DiagID::ErrInaccessibleBase => "inaccessible base class '{}' in conversion",
DiagID::ErrAmbiguousBaseClass => "ambiguous conversion from '{}' to '{}'",
DiagID::ErrVirtualBaseClass => "virtual base class '{}' cannot be directly constructed",
DiagID::ErrOverrideMismatch => "'{}' marked 'override' but does not override any member",
DiagID::ErrFinalOverride => "cannot override 'final' function '{}'",
DiagID::ErrMissingVtable => "undefined reference to vtable for '{}'",
DiagID::ErrMissingTypeinfo => "undefined reference to typeinfo for '{}'",
DiagID::ErrInvalidUseOfThis => "invalid use of 'this' outside of a non-static member function",
DiagID::ErrInvalidUseOfMember => "invalid use of non-static data member '{}'",
DiagID::ErrInvalidUseOfTemplate => "invalid use of template name without argument list",
DiagID::ErrInvalidUseOfAuto => "invalid use of 'auto'",
DiagID::ErrInvalidUseOfDecltype => "invalid use of 'decltype'",
DiagID::ErrInvalidUseOfVoid => "invalid use of void expression",
DiagID::ErrInvalidUseOfIncompleteType => "invalid use of incomplete type '{}'",
DiagID::ErrFieldOfIncompleteType => "field has incomplete type '{}'",
DiagID::ErrSizeOfIncompleteType => "invalid application of 'sizeof' to an incomplete type '{}'",
DiagID::ErrAlignOfIncompleteType => "invalid application of 'alignof' to an incomplete type '{}'",
DiagID::ErrDeleteOfIncompleteType => "deletion of pointer to incomplete type '{}'",
DiagID::ErrDeleteOfAbstractClass => "deletion of abstract class '{}' is not allowed",
DiagID::ErrNewOfAbstractClass => "cannot allocate an object of abstract type '{}'",
DiagID::ErrThrowOfIncompleteType => "cannot throw object of incomplete type '{}'",
DiagID::ErrCatchOfIncompleteType => "cannot catch incomplete type '{}'",
DiagID::ErrReturnOfIncompleteType => "cannot return incomplete type '{}' from function",
DiagID::ErrParamOfIncompleteType => "parameter has incomplete type '{}'",
DiagID::ErrPPHashError => "{}",
DiagID::ErrPPExpectedNewline => "expected newline at end of directive",
DiagID::ErrPPExpectedIdentifier => "expected identifier in directive",
DiagID::ErrPPExpectedValue => "expected value in expression",
DiagID::ErrPPExpectedInclude => "expected \"FILENAME\" or <FILENAME>",
DiagID::ErrPPExpectedConditional => "expected expression in preprocessor conditional",
DiagID::ErrPPExpectedEndif => "expected #endif",
DiagID::ErrPPIncludeNotFound => "'{}' file not found",
DiagID::ErrPPIncludeTooDeep => "#include nested too deeply",
DiagID::ErrPPMacroRedefined => "'{}' macro redefined",
DiagID::ErrPPMacroNotDefined => "'{}' is not defined, evaluates to 0",
DiagID::ErrPPMacroExpansionTooDeep => "macro expansion too deeply nested",
DiagID::ErrPPInvalidToken => "invalid preprocessing token",
DiagID::ErrPPUnterminatedComment => "unterminated /* comment",
DiagID::ErrPPUnterminatedString => "missing terminating '\"' character",
DiagID::ErrPPUnterminatedChar => "missing terminating '\\'' character",
DiagID::ErrPPDirectiveInMacroArg => "directive in macro argument list",
DiagID::ErrPPEmptyFileName => "empty filename in #include directive",
DiagID::ErrPPInvalidDirective => "invalid preprocessing directive",
DiagID::ErrPPNestedComment => "'/*' within block comment",
DiagID::ErrPPBadPaste => "pasting formed '{}', an invalid preprocessing token",
DiagID::ErrPPBadStringify => "'#' is not followed by a macro parameter",
DiagID::ErrPPVariadicMacro => "ISO C++11 requires at least one argument for the '...' in a variadic macro",
DiagID::ErrPPExpectedFeatureName => "expected feature name",
DiagID::ErrPPDefinedInMacro => "operator 'defined' requires an identifier",
DiagID::ErrPPLineDirective => "#line directive requires a simple digit sequence",
DiagID::ErrPPPragmaExpected => "expected string literal in #pragma directive",
DiagID::ErrPPPragmaUnsupported => "unsupported #pragma '{}'",
DiagID::ErrPPMissingEndif => "unterminated conditional directive",
DiagID::ErrPPExtraEndif => "#endif without #if",
DiagID::ErrPPElseAfterElse => "#else after #else",
DiagID::ErrPPElifAfterElse => "#elif after #else",
DiagID::ErrPPNonPortablePath => "non-portable path to file '{}'",
DiagID::ErrPPIncludeNextOutsideHeader => "#include_next in primary source file",
DiagID::ErrPPImportInMacro => "import of module '{}' in macro body",
DiagID::ErrStaticAssert => "static assertion failed",
DiagID::ErrStaticAssertFailed => "static assertion failed: {}",
DiagID::ErrInvalidArraySize => "invalid array size",
DiagID::ErrNegativeArraySize => "array size is negative",
DiagID::ErrZeroSizeArray => "zero size arrays are an extension",
DiagID::ErrFlexibleArrayMember => "flexible array member '{}' not at end of struct",
DiagID::ErrFlexibleArrayInUnion => "flexible array member '{}' in a union is not allowed",
DiagID::ErrFlexibleArrayInEmptyStruct => "flexible array member in an otherwise empty struct",
DiagID::ErrConstexprBodyNoReturn => "constexpr function never produces a constant expression",
DiagID::ErrConstexprFunctionNeverConstant => "constexpr function's return type '{}' is not a literal type",
DiagID::ErrConstexprVarRequiresInit => "default initialization of an object of const type '{}' without a user-provided default constructor",
DiagID::ErrConstexprVarRequiresConstInit => "constexpr variable '{}' must be initialized by a constant expression",
DiagID::ErrDuplicateMember => "duplicate member '{}'",
DiagID::ErrDuplicateBase => "duplicate base class '{}'",
DiagID::ErrDuplicateCase => "duplicate case value '{}'",
DiagID::ErrDuplicateDefault => "multiple default labels in switch statement",
DiagID::ErrDuplicateAttribute => "duplicate attribute '{}'",
DiagID::ErrDuplicateAttributeArg => "duplicate argument in attribute '{}'",
DiagID::ErrMultipleStorageClasses => "multiple storage classes in declaration",
DiagID::ErrStorageClassForFunction => "storage class specified for function parameter",
DiagID::ErrTypeSpecifierRepeated => "cannot combine with previous '{}' declaration specifier",
DiagID::ErrIllegalStorageClassOnMember => "storage class on member is not allowed",
DiagID::ErrThreadNonStatic => "'thread_local' can only be applied to variables with static or thread storage duration",
DiagID::ErrThreadLocalInPlainObject => "thread-local storage class is not valid for this declaration",
DiagID::ErrAtomicSpecifierBadType => "_Atomic cannot be applied to function type",
DiagID::ErrAlignasOnBitField => "'_Alignas' attribute cannot be applied to a bit-field",
DiagID::ErrAlignasUnderaligned => "requested alignment is less than minimum alignment of {} for type '{}'",
DiagID::NotePreviousDefinition => "previous definition is here",
DiagID::NotePreviousDeclaration => "previous declaration is here",
DiagID::NotePreviousImplicitDeclaration => "previous implicit declaration is here",
DiagID::NotePreviousBuiltinDeclaration => "built-in declaration here",
DiagID::NotePreviousUse => "previous use is here",
DiagID::NotePreviousIf => "to match this '{'",
DiagID::NotePreviousElse => "previous 'else' is here",
DiagID::NoteMatching => "to match this '('",
DiagID::NoteCandidateFunction => "candidate function not viable: {}",
DiagID::NoteCandidateConstructor => "candidate constructor not viable: {}",
DiagID::NoteCandidateTemplate => "candidate template ignored: {}",
DiagID::NoteCandidateOperator => "candidate operator not viable: {}",
DiagID::NoteTemplateParamDeclaredHere => "template parameter is declared here",
DiagID::NoteDefaultArgInstantiatedHere => "default argument instantiated here",
DiagID::NoteInInstantiationOf => "in instantiation of template class '{}' requested here",
DiagID::NoteInExpansionOf => "in expansion of macro '{}'",
DiagID::NoteInSubstitutionOf => "in substitution of template argument '{}'",
DiagID::NoteInDefinitionOf => "in definition of '{}'",
DiagID::NoteInMemberFunction => "in member function '{}'",
DiagID::NoteInLambdaFunction => "in lambda function",
DiagID::NoteWhileDeducingTemplateArgs => "while deducing template arguments for '{}'",
DiagID::NoteDuringTemplateArgDeduction => "during template argument deduction for '{}'",
DiagID::NoteForwardDeclaration => "forward declaration of '{}' is here",
DiagID::NoteForwardDeclarationOfClass => "forward declaration of class '{}' is here",
DiagID::NoteForwardDeclarationOfEnum => "forward declaration of enum '{}' is here",
DiagID::NoteDeclarationHere => "declaration of '{}' here",
DiagID::NoteDefinitionHere => "definition of '{}' here",
DiagID::NoteHeaderHere => "header is here",
DiagID::NoteIncludeLocation => "in file included from {}:{}:",
DiagID::NotePreviousInclude => "previous include is here",
DiagID::NoteMacroDefinedHere => "macro '{}' defined here",
DiagID::NoteMacroExpandedHere => "expanded from macro '{}'",
DiagID::NotePragmaHere => "#pragma is here",
DiagID::NoteUninitializedHere => "variable '{}' may be uninitialized when used here",
DiagID::NoteAssignedHere => "assigned here",
DiagID::NoteConditionHere => "condition is here",
DiagID::NoteLoopHere => "loop is here",
DiagID::NoteExceptionHere => "exception thrown here",
DiagID::NoteConversionHere => "conversion candidate is here",
DiagID::NoteOverloadResolutionHere => "in overload resolution for '{}'",
DiagID::NoteTypeMismatch => "type mismatch: expected '{}', got '{}'",
DiagID::NoteValueHere => "value {}",
DiagID::NoteParameterHere => "passing argument to parameter '{}' here",
DiagID::NoteReturnValueHere => "returning from function here",
DiagID::NoteDestructorHere => "destructor called here",
DiagID::NoteConstructorHere => "constructor called here",
DiagID::NoteBaseClassHere => "base class '{}' declared here",
DiagID::NoteMemberDeclaredHere => "member '{}' declared here",
DiagID::NoteLambdaCaptureHere => "lambda capture is here",
DiagID::NoteLambdaHere => "lambda expression begins here",
DiagID::NoteTemplateHere => "template declared here",
DiagID::NoteSpecializationHere => "specialization declared here",
DiagID::NoteExplicitInstantiationHere => "explicit instantiation is here",
DiagID::NotePartialSpecializationHere => "partial specialization is here",
DiagID::NoteDefaultTemplateArgHere => "default template argument declared here",
DiagID::NoteDeducedTypeHere => "type '{}' deduced as '{}' here",
DiagID::NoteWhileBuildingDeductionGuide => "while building deduction guide for '{}'",
DiagID::NoteShadowDeclHere => "previous declaration is here",
DiagID::NotePreviousShadowDecl => "previous shadow declaration is here",
DiagID::NoteFieldDeclaredHere => "field '{}' is declared here",
DiagID::NoteBitFieldDeclaredHere => "bit-field is declared here",
DiagID::NoteAnonymousStructHere => "anonymous struct declared here",
DiagID::NoteAnonymousUnionHere => "anonymous union declared here",
DiagID::NoteEnumDeclaredHere => "enum '{}' declared here",
DiagID::NoteTypedefHere => "typedef '{}' is here",
DiagID::NoteUsingDeclHere => "using declaration is here",
DiagID::NoteNamespaceHere => "namespace '{}' declared here",
DiagID::NoteModuleHere => "module '{}' declared here",
DiagID::NoteImportHere => "imported here",
DiagID::NoteExportHere => "exported here",
DiagID::NoteInEvaluatingExpr => "in evaluation of constant expression",
DiagID::NoteInEvaluatingConstraint => "in evaluation of constraint",
DiagID::NoteInConstraintNormalization => "while normalizing constraint",
DiagID::NoteWhileCheckingConstraintSatisfaction => "while checking constraint satisfaction",
DiagID::NoteConceptHere => "concept '{}' defined here",
DiagID::NoteRequirementHere => "requirement declared here",
DiagID::NoteReturnTypeHere => "return type is here",
DiagID::NoteCallableHere => "called object is here",
DiagID::NoteInstantiatedFrom => "instantiated from '{}'",
DiagID::NoteExpandedFrom => "expanded from '{}'",
DiagID::NoteInRecursiveInstantiation => "in recursive instantiation of '{}'",
DiagID::NoteArrayInitHere => "array initialization here",
DiagID::NoteDesignatedInitHere => "designated initializer here",
DiagID::NoteAggregateInitHere => "aggregate initialization here",
DiagID::NoteDefaultInitHere => "default initialization here",
DiagID::NoteValueInitHere => "value initialization here",
DiagID::NoteCopyInitHere => "copy initialization here",
DiagID::NoteDirectInitHere => "direct initialization here",
DiagID::NoteListInitHere => "list initialization here",
DiagID::NoteReferenceInitHere => "reference initialization here",
DiagID::WUnusedVariable => "unused variable '{}'",
DiagID::WUnusedParameter => "unused parameter '{}'",
DiagID::WUnusedFunction => "unused function '{}'",
DiagID::WUnusedLabel => "unused label '{}'",
DiagID::WUnusedValue => "expression result unused",
DiagID::WUnusedResult => "ignoring return value of function declared with 'warn_unused_result' attribute",
DiagID::WSignCompare => "comparison of integers of different signs: '{}' and '{}'",
DiagID::WSignConversion => "implicit conversion changes signedness: '{}' to '{}'",
DiagID::WSignPromo => "overload resolution chose to promote from unsigned to signed",
DiagID::WConversion => "implicit conversion loses precision: '{}' to '{}'",
DiagID::WConversionLoss => "implicit conversion loses integer precision: '{}' to '{}'",
DiagID::WFormat => "format specifies type '{}' but the argument has type '{}'",
DiagID::WFormatSecurity => "format string is not a string literal (potentially insecure)",
DiagID::WShadow => "declaration shadows a local variable",
DiagID::WParentheses => "'&'/'|' within '&&'/'||' [-Wparentheses]",
DiagID::WImplicitFallthrough => "unannotated fall-through between switch labels",
DiagID::WStrictAliasing => "dereferencing type-punned pointer will break strict-aliasing rules",
DiagID::WReturnType => "control reaches end of non-void function",
DiagID::WUninitialized => "variable '{}' is uninitialized when used here",
DiagID::WTautologicalCompare => "self-comparison always evaluates to {}",
DiagID::WNullDereference => "null pointer dereference",
DiagID::WDivisionByZero => "division by zero is undefined",
DiagID::WArrayBounds => "array index {} is past the end of the array",
DiagID::WUnreachableCode => "code will never be executed",
DiagID::WCharSubscripts => "array subscript is of type 'char'",
DiagID::WMainReturnType => "return type of 'main' is not 'int'",
DiagID::WMain => "first parameter of 'main' (argument count) should be of type 'int'",
DiagID::WMissingBraces => "suggest braces around initialization of subobject",
DiagID::WMissingFieldInitializers => "missing field '{}' initializer",
DiagID::WMissingPrototypes => "no previous prototype for function '{}'",
DiagID::WDeprecated => "'{}' is deprecated",
DiagID::WDeprecatedDeclarations => "'{}' is deprecated: {}",
DiagID::WExtra => "extra tokens at end of #include directive",
DiagID::WExtraSemi => "extra ';' after member function definition",
DiagID::WOverloadedVirtual => "'{}' hides overloaded virtual function",
DiagID::WReorder => "field '{}' will be initialized after field '{}'",
DiagID::WAttributes => "unknown attribute '{}' ignored",
DiagID::WUserDefinedLiterals => "user-defined literal suffixes not starting with '_' are reserved",
DiagID::WPointerArith => "arithmetic on a pointer to void is a GNU extension",
DiagID::WPointerToIntCast => "cast to smaller integer type '{}' from '{}' may lose data",
DiagID::WIntToPointerCast => "cast to '{}' from smaller integer type '{}' may lose data",
DiagID::WCastAlign => "cast from '{}' to '{}' increases required alignment from {} to {}",
DiagID::WCastQual => "cast from '{}' to '{}' drops const qualifier",
DiagID::WBadFunctionCast => "cast from '{}' to '{}' converts to incompatible function type",
DiagID::WFloatEqual => "comparing floating point with == or != is unsafe",
DiagID::WUndef => "'{}' is not defined, evaluates to 0",
DiagID::WTrigraphs => "trigraph ignored",
DiagID::WComment => "'/*' within block comment",
DiagID::WMultichar => "multi-character character constant",
DiagID::WCharLiteralAscii => "char literal with non-ASCII character",
DiagID::WImplicitInt => "type specifier missing, defaults to 'int'",
DiagID::WImplicitFunctionDeclarations => "implicit declaration of function '{}' is invalid in C99",
DiagID::WIntConversion => "implicit conversion from '{}' to '{}' changes value from {} to {}",
DiagID::WIntInBoolContext => "converting integer to bool",
DiagID::WLogicalNotParentheses => "logical not is only applied to left hand side of comparison",
DiagID::WLongLong => "'long long' is an extension when C90 mode is enabled",
DiagID::WNestedExterns => "'extern' inside function scope",
DiagID::WNoNewlineEOF => "no newline at end of file",
DiagID::WOldStyleCast => "use of old-style cast",
DiagID::WOldStyleDefinition => "old-style function definition",
DiagID::WOverlengthStrings => "string literal of length {} bytes exceeds maximum length {}",
DiagID::WPointerSign => "pointer targets in passing argument {} of '{}' differ in signedness",
DiagID::WRedundantDecls => "redundant redeclaration of '{}'",
DiagID::WReturnTypeCVQual => "'const'/'volatile' type qualifiers on return type have no effect",
DiagID::WSequencePoint => "unsequenced modification and access to '{}'",
DiagID::WShiftCountOverflow => "shift count >= width of type",
DiagID::WShiftCountNegative => "shift count is negative",
DiagID::WShiftNegativeValue => "shifting a negative signed value is undefined",
DiagID::WStringPlusInt => "adding 'int' to a string does not append to the string",
DiagID::WStringPlusChar => "adding 'char' to a string pointer",
DiagID::WStringConversion => "implicit conversion turns string literal into bool",
DiagID::WTypeLimits => "comparison of unsigned expression {} 0 is always {}",
DiagID::WVexingParse => "parentheses were disambiguated as a function declaration",
DiagID::WVLA => "variable length array used",
DiagID::WVolatileRegisterVar => "a volatile register variable is used",
DiagID::WWriteStrings => "converting a string literal to 'char *' discards const qualifier",
DiagID::WZeroLengthArray => "zero size arrays are an extension",
DiagID::WClangSpecific => "clang-specific diagnostic",
DiagID::WUnknownPragmas => "unknown pragma ignored",
DiagID::WUnknownWarningOption => "unknown warning option '{}'",
DiagID::WVariadicMacros => "variadic macros are a C99 feature",
DiagID::WMost => "most-related warnings",
DiagID::WInfiniteRecursion => "all paths through this function will call itself",
DiagID::WSuspiciousBzero => "suspicious bzero call; size may be wrong",
DiagID::WSuspiciousMemaccess => "suspicious memaccess call; size may be wrong",
DiagID::WNonPODVarargs => "passing object of non-trivially-copyable type through variadic arguments",
DiagID::WStringCompression => "string compression",
DiagID::WEnumTooLarge => "enumeration values exceed range of largest integer",
DiagID::WExcessInitializers => "excess elements in initializer",
DiagID::WFloatOverflowConversion => "implicit conversion from '{}' to '{}' changes value",
DiagID::WFloatZeroConversion => "implicit conversion from '{}' to '{}' turns non-zero to zero",
DiagID::WMismatchedNewDelete => "'operator delete' does not match 'operator new'",
DiagID::WMismatchedReturnTypes => "mismatched return types",
DiagID::WMismatchedTags => "class '{}' was previously declared as a struct",
DiagID::WNonVirtualDtor => "'{}' has virtual functions but non-virtual destructor",
DiagID::WSentinel => "missing sentinel in function call",
DiagID::WStringConcat => "string concatenation",
DiagID::WStringLiteralConversion => "implicit conversion of string literal to 'bool'",
DiagID::WStructPadding => "padding struct to align '{}'",
DiagID::WThreadSafety => "thread safety analysis warning",
DiagID::WThreadSafetyAnalysis => "thread safety analysis: {}",
DiagID::WUnavailableDeclarations => "'{}' is unavailable",
DiagID::WUnguardedAvailability => "'{}' is only available on {} {} or newer",
DiagID::WUnsupportedTargetOpt => "unsupported target option '{}'",
DiagID::WEmptyBody => "empty body in {} statement",
DiagID::WEmptyTranslationUnit => "empty translation unit",
DiagID::FatalNoInputFiles => "no input files",
DiagID::FatalCannotOpenFile => "cannot open file '{}': {}",
DiagID::FatalCannotWriteFile => "cannot write to file '{}': {}",
DiagID::FatalCannotExecuteBinary => "cannot execute binary '{}'",
DiagID::FatalOutOfMemory => "out of memory",
DiagID::FatalStackExhausted => "stack exhausted",
DiagID::FatalIncludeTooDeep => "#include nested too deeply",
DiagID::FatalMacroExpansionTooDeep => "macro expansion too deeply nested",
DiagID::FatalTemplateInstantiationTooDeep => "template instantiation depth exceeded",
DiagID::FatalRecursiveTemplateInstantiation => "recursive template instantiation",
DiagID::FatalTooManyErrors => "too many errors emitted, stopping now",
DiagID::FatalInvalidTarget => "invalid target '{}'",
DiagID::FatalUnsupportedTarget => "unsupported target '{}'",
DiagID::FatalInvalidArch => "invalid architecture '{}' for target '{}'",
DiagID::FatalInvalidCPU => "invalid CPU '{}' for target '{}'",
DiagID::FatalInvalidFeature => "invalid feature '{}' for target '{}'",
DiagID::FatalInvalidOption => "invalid option '{}'",
DiagID::FatalConflictingOptions => "conflicting options '{}' and '{}'",
DiagID::FatalMissingArg => "argument to '{}' is missing (expected {} value)",
DiagID::FatalUnknownArg => "unknown argument '{}'",
DiagID::FatalInvalidValue => "invalid value '{}' for '{}'",
DiagID::FatalModuleBuildFailed => "module build failed for '{}'",
DiagID::FatalModuleFileOutOfDate => "module file is out of date",
DiagID::FatalModuleFileInvalid => "module file is invalid",
DiagID::FatalCyclicModuleDependency => "cyclic module dependency detected",
DiagID::FatalPCHOutOfDate => "PCH file is out of date",
DiagID::FatalPCHInvalid => "PCH file is invalid",
DiagID::FatalPCHCXXMismatch => "PCH file was built for a different C++ dialect",
DiagID::FatalPCHTargetMismatch => "PCH file was built for a different target",
DiagID::FatalPCHLanguageMismatch => "PCH file was built for a different language",
DiagID::FatalPCHVersionMismatch => "PCH file was built by a different compiler version",
DiagID::FatalPCHHasDifferentModuleCache => "PCH file uses a different module cache path",
DiagID::FatalCannotFindHeaders => "cannot find headers for target '{}'",
DiagID::FatalCannotFindSDK => "cannot find SDK for target '{}'",
DiagID::FatalSDKTooOld => "SDK version {} is too old (minimum: {})",
DiagID::FatalSDKMissingRequired => "SDK is missing required component '{}'",
DiagID::FatalBrokenInstallation => "broken compiler installation; check your paths",
DiagID::FatalInternalError => "internal compiler error: {}",
DiagID::FatalUnimplemented => "unimplemented feature: {}",
DiagID::FatalUnreachable => "unreachable code executed in compiler",
DiagID::FatalLTOError => "LTO error: {}",
DiagID::FatalBackendError => "backend error: {}",
DiagID::FatalCodeGenError => "code generation error: {}",
DiagID::FatalLinkError => "linker error: {}",
DiagID::FatalProfileDataError => "profile data error: {}",
DiagID::FatalSanitizerError => "sanitizer runtime error: {}",
DiagID::RemarkBackendOptimization => "{}",
DiagID::RemarkLoopVectorized => "vectorized loop",
DiagID::RemarkLoopInterleaved => "interleaved loop",
DiagID::RemarkSLPVectorized => "SLP vectorized",
DiagID::RemarkInlinedFunction => "'{}' inlined into '{}'",
DiagID::RemarkNotInlined => "'{}' not inlined into '{}' because {}",
DiagID::RemarkLoopUnrolled => "unrolled loop by a factor of {}",
DiagID::RemarkLoopUnrolledPartial => "partially unrolled loop by a factor of {}",
DiagID::RemarkLoopUnrolledComplete => "completely unrolled loop",
DiagID::RemarkLoopUnrolledFailed => "failed to unroll loop",
DiagID::RemarkLoopFused => "fused {} loops",
DiagID::RemarkLoopDistributed => "distributed loop",
DiagID::RemarkLoopInterchanged => "interchanged loop",
DiagID::RemarkLoopUnswitched => "unswitched loop",
DiagID::RemarkLoopVersioned => "versioned loop",
DiagID::RemarkLicmMoved => "LICM moved",
DiagID::RemarkGVNLoadEliminated => "load eliminated by GVN",
DiagID::RemarkMemCpyOptimized => "memcpy optimized",
DiagID::RemarkDivRemExpanded => "div/rem expanded",
DiagID::RemarkFloat2IntConverted => "float to int conversion",
DiagID::RemarkConstantHoisted => "constant hoisted",
DiagID::RemarkReassociated => "reassociated expression",
DiagID::RemarkInstructionCount => "instruction count: {}",
DiagID::RemarkInlineCost => "inline cost",
DiagID::RemarkFunctionCloned => "function cloned",
DiagID::RemarkCallSiteInlined => "call site inlined",
DiagID::RemarkCallSiteNotInlined => "call site not inlined: {}",
DiagID::RemarkProfileDataApplied => "profile data applied",
DiagID::RemarkProfileDataMissing => "profile data missing",
DiagID::RemarkInstrProfApplied => "instrumentation profile applied",
DiagID::RemarkSampleProfileApplied => "sample profile applied",
DiagID::RemarkAnnotationStackAccess => "stack access annotation",
DiagID::RemarkAnnotationJumpTable => "jump table annotation",
DiagID::RemarkAnnotationVectorWidth => "vector width annotation: {}",
DiagID::RemarkAnnotationInterleaveCount => "interleave count annotation: {}",
DiagID::RemarkAnnotationUnrollCount => "unroll count annotation: {}",
DiagID::RemarkAnnotationLoopDistribution => "loop distribution annotation",
DiagID::RemarkSFINAEExplanation => "{}",
DiagID::RemarkCandidateDisabled => "candidate disabled: {}",
DiagID::RemarkCandidateNotViable => "candidate not viable: {}",
DiagID::RemarkTemplateDeductionFailure => "template deduction failure: {}",
DiagID::RemarkOverloadCandidateDropped => "overload candidate dropped: {}",
DiagID::RemarkUsingShadowDecl => "using shadow declaration",
DiagID::RemarkModuleImport => "module '{}' imported",
DiagID::RemarkModuleExport => "module '{}' exported",
DiagID::RemarkModuleBuild => "module '{}' built",
DiagID::RemarkPragmaWarning => "{}",
DiagID::RemarkPragmaMessage => "{}",
DiagID::RemarkBackendPlugin => "{}",
DiagID::IgnoredMacroDefined => "macro is defined",
DiagID::IgnoredMacroUndefined => "macro is undefined",
DiagID::IgnoredPragma => "unknown pragma ignored",
DiagID::IgnoredAttribute => "unknown attribute ignored",
DiagID::IgnoredExtension => "extension ignored",
DiagID::IgnoredWarning => "warning ignored",
DiagID::IgnoredDeprecated => "deprecated diagnostic ignored",
_ => "unknown diagnostic",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AnsiColor {
Bold,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White,
Reset,
}
impl AnsiColor {
pub fn code(&self) -> &'static str {
match self {
AnsiColor::Bold => "\x1b[1m",
AnsiColor::Red => "\x1b[31m",
AnsiColor::Green => "\x1b[32m",
AnsiColor::Yellow => "\x1b[33m",
AnsiColor::Blue => "\x1b[34m",
AnsiColor::Magenta => "\x1b[35m",
AnsiColor::Cyan => "\x1b[36m",
AnsiColor::White => "\x1b[37m",
AnsiColor::Reset => "\x1b[0m",
}
}
pub fn wrap(&self, text: &str) -> String {
format!("{}{}{}", self.code(), text, AnsiColor::Reset.code())
}
}
#[derive(Debug, Clone)]
pub struct DiagnosticColorScheme {
pub error_colors: Vec<AnsiColor>,
pub warning_colors: Vec<AnsiColor>,
pub note_colors: Vec<AnsiColor>,
pub fixit_colors: Vec<AnsiColor>,
pub caret_colors: Vec<AnsiColor>,
pub source_line_colors: Vec<AnsiColor>,
pub filename_colors: Vec<AnsiColor>,
pub line_number_colors: Vec<AnsiColor>,
pub range_highlight_colors: Vec<AnsiColor>,
pub use_colors: bool,
}
impl DiagnosticColorScheme {
pub fn clang_style() -> Self {
Self {
error_colors: vec![AnsiColor::Bold, AnsiColor::Red],
warning_colors: vec![AnsiColor::Bold, AnsiColor::Magenta],
note_colors: vec![AnsiColor::Cyan],
fixit_colors: vec![AnsiColor::Green],
caret_colors: vec![AnsiColor::Green],
source_line_colors: vec![AnsiColor::White],
filename_colors: vec![AnsiColor::Bold],
line_number_colors: vec![AnsiColor::Blue],
range_highlight_colors: vec![AnsiColor::Green],
use_colors: true,
}
}
pub fn monochrome() -> Self {
Self {
error_colors: vec![],
warning_colors: vec![],
note_colors: vec![],
fixit_colors: vec![],
caret_colors: vec![],
source_line_colors: vec![],
filename_colors: vec![],
line_number_colors: vec![],
range_highlight_colors: vec![],
use_colors: false,
}
}
fn apply_colors(&self, colors: &[AnsiColor], text: &str) -> String {
if !self.use_colors || colors.is_empty() {
return text.to_string();
}
let start: String = colors.iter().map(|c| c.code()).collect();
format!("{}{}{}", start, text, AnsiColor::Reset.code())
}
pub fn error_open(&self) -> String {
if !self.use_colors { String::new() } else {
self.error_colors.iter().map(|c| c.code()).collect()
}
}
pub fn warning_open(&self) -> String {
if !self.use_colors { String::new() } else {
self.warning_colors.iter().map(|c| c.code()).collect()
}
}
pub fn note_open(&self) -> String {
if !self.use_colors { String::new() } else {
self.note_colors.iter().map(|c| c.code()).collect()
}
}
pub fn fixit_open(&self) -> String {
if !self.use_colors { String::new() } else {
self.fixit_colors.iter().map(|c| c.code()).collect()
}
}
pub fn caret_open(&self) -> String {
if !self.use_colors { String::new() } else {
self.caret_colors.iter().map(|c| c.code()).collect()
}
}
pub fn close(&self) -> String {
if !self.use_colors { String::new() } else {
AnsiColor::Reset.code().to_string()
}
}
pub fn colorize_error(&self, text: &str) -> String {
self.apply_colors(&self.error_colors, text)
}
pub fn colorize_warning(&self, text: &str) -> String {
self.apply_colors(&self.warning_colors, text)
}
pub fn colorize_note(&self, text: &str) -> String {
self.apply_colors(&self.note_colors, text)
}
pub fn colorize_fixit(&self, text: &str) -> String {
self.apply_colors(&self.fixit_colors, text)
}
pub fn colorize_caret(&self, text: &str) -> String {
self.apply_colors(&self.caret_colors, text)
}
pub fn colorize_filename(&self, text: &str) -> String {
self.apply_colors(&self.filename_colors, text)
}
pub fn colorize_line_number(&self, text: &str) -> String {
self.apply_colors(&self.line_number_colors, text)
}
}
impl Default for DiagnosticColorScheme {
fn default() -> Self {
Self::clang_style()
}
}
#[derive(Debug, Clone)]
pub struct CaretRange {
pub start_col: usize,
pub end_col: usize,
pub is_primary: bool,
pub message: Option<String>,
}
impl CaretRange {
pub fn new(start: usize, end: usize) -> Self {
Self { start_col: start, end_col: end, is_primary: false, message: None }
}
pub fn primary(start: usize, end: usize) -> Self {
Self { start_col: start, end_col: end, is_primary: true, message: None }
}
pub fn with_message(start: usize, end: usize, msg: &str) -> Self {
Self { start_col: start, end_col: end, is_primary: false, message: Some(msg.to_string()) }
}
}
pub struct CaretRenderer;
impl CaretRenderer {
pub fn build_caret_line(
line: &str,
ranges: &[CaretRange],
scheme: &DiagnosticColorScheme,
) -> String {
if ranges.is_empty() {
return String::new();
}
let line_len = line.len();
let mut caret_line = String::with_capacity(line_len + 10);
let mut sorted = ranges.to_vec();
sorted.sort_by_key(|r| r.start_col);
let mut col_markers: Vec<char> = vec![' '; line_len.max(1)];
for range in &sorted {
let start = range.start_col.min(line_len.saturating_sub(1));
let end = range.end_col.min(line_len);
if start >= end || start >= line_len {
continue;
}
let primary_pos = if range.is_primary { start } else { start };
for col in start..end.min(line_len) {
if col == primary_pos && range.is_primary {
col_markers[col] = '^';
} else if col_markers[col] == ' ' {
col_markers[col] = '~';
} else if col_markers[col] != '^' {
col_markers[col] = '~';
}
}
}
while !col_markers.is_empty() && col_markers.last() == Some(&' ') {
col_markers.pop();
}
let first_marker = col_markers.iter().position(|&c| c != ' ').unwrap_or(0);
for _ in 0..first_marker {
caret_line.push(' ');
}
for ch in col_markers.iter().skip(first_marker) {
caret_line.push(*ch);
}
let colored = scheme.colorize_caret(&caret_line);
colored
}
pub fn build_caret_with_messages(
line: &str,
ranges: &[CaretRange],
scheme: &DiagnosticColorScheme,
) -> Vec<String> {
let mut result = Vec::new();
let caret_line = Self::build_caret_line(line, ranges, scheme);
result.push(caret_line);
for range in ranges {
if let Some(ref msg) = range.message {
let indent = " ".repeat(range.start_col);
let msg_line = format!("{}|", indent);
result.push(msg_line);
let msg_indent = " ".repeat(range.start_col + 1);
result.push(format!("{}{}", msg_indent, scheme.colorize_note(msg)));
}
}
result
}
}
#[derive(Debug, Clone)]
pub struct SourceSnippet {
pub lines: Vec<(usize, String)>,
pub primary_line: usize,
pub ranges: Vec<CaretRange>,
}
impl SourceSnippet {
pub fn new(lines: Vec<(usize, String)>, primary_line: usize) -> Self {
Self { lines, primary_line, ranges: Vec::new() }
}
pub fn add_range(&mut self, range: CaretRange) {
self.ranges.push(range);
}
pub fn render(&self, scheme: &DiagnosticColorScheme) -> String {
let mut output = String::new();
let max_line_num = self.lines.iter().map(|(n, _)| *n).max().unwrap_or(0);
let line_num_width = max_line_num.to_string().len().max(4);
for (line_num, line_text) in &self.lines {
let num_str = format!("{:>width$} | ", line_num, width = line_num_width);
output.push_str(&scheme.colorize_line_number(&num_str));
if *line_num == self.primary_line {
output.push_str(&line_text);
} else {
output.push_str(&scheme.apply_colors(&scheme.source_line_colors, line_text));
}
output.push('\n');
if *line_num == self.primary_line && !self.ranges.is_empty() {
let indent = " ".repeat(line_num_width + 3);
output.push_str(&indent);
let caret = CaretRenderer::build_caret_line(line_text, &self.ranges, scheme);
output.push_str(&caret);
output.push('\n');
}
}
output
}
}
pub struct FullDiagnosticRenderer {
pub color_scheme: DiagnosticColorScheme,
pub show_column: bool,
pub show_source_line: bool,
pub show_carets: bool,
pub show_fixits: bool,
pub show_option_names: bool,
pub show_categories: bool,
pub context_lines: usize,
pub tab_stop: usize,
pub message_length: usize,
}
impl FullDiagnosticRenderer {
pub fn new() -> Self {
Self {
color_scheme: DiagnosticColorScheme::clang_style(),
show_column: true,
show_source_line: true,
show_carets: true,
show_fixits: true,
show_option_names: true,
show_categories: true,
context_lines: 1,
tab_stop: 8,
message_length: 120,
}
}
pub fn for_ide() -> Self {
Self {
color_scheme: DiagnosticColorScheme::monochrome(),
..Self::new()
}
}
pub fn render_diagnostic(&self, diag: &StructuredDiagnostic, lines: &[(usize, String)]) -> String {
let mut output = String::new();
let severity_str = match diag.severity {
DiagSeverity::Error | DiagSeverity::Fatal => self.color_scheme.colorize_error("error:"),
DiagSeverity::Warning => self.color_scheme.colorize_warning("warning:"),
DiagSeverity::Note => self.color_scheme.colorize_note("note:"),
DiagSeverity::Remark => self.color_scheme.colorize_note("remark:"),
DiagSeverity::Ignored => "ignored:".to_string(),
};
if diag.location.is_valid {
let file = self.color_scheme.colorize_filename(
&diag.location.file.display().to_string()
);
output.push_str(&format!("{}:{}:{}: ", file, diag.location.line, diag.location.column));
}
output.push_str(&format!("{} {}\n", severity_str, diag.message));
if self.show_categories && !diag.category.is_empty() {
output.push_str(&format!(
"{}",
self.color_scheme.colorize_note(&format!("[-W{}] ", diag.category))
));
output.push('\n');
}
if self.show_source_line && !lines.is_empty() {
let snippet = SourceSnippet::new(lines.to_vec(), diag.location.line as usize);
output.push_str(&snippet.render(&self.color_scheme));
}
if self.show_carets && !diag.ranges.is_empty() {
let line_num = diag.location.line as usize;
let primary_line = lines.iter().find(|(n, _)| *n == line_num)
.map(|(_, t)| t.clone())
.unwrap_or_default();
let caret_ranges: Vec<CaretRange> = diag.ranges.iter().map(|r| {
CaretRange::primary(r.begin.column as usize - 1, r.end.column as usize)
}).collect();
if !caret_ranges.is_empty() {
let indent = " ".repeat(5);
output.push_str(&indent);
let caret = CaretRenderer::build_caret_line(&primary_line, &caret_ranges, &self.color_scheme);
output.push_str(&caret);
output.push('\n');
}
}
if self.show_fixits && !diag.fixits.is_empty() {
for fixit in &diag.fixits {
if !fixit.code.is_empty() && !fixit.remove_range.begin.is_valid {
output.push_str(&format!(
"{}: {}\n",
self.color_scheme.colorize_note("fix-it"),
self.color_scheme.colorize_fixit(&fixit.code)
));
}
}
}
for note in &diag.notes {
if note.location.is_valid {
let file = note.location.file.display().to_string();
output.push_str(&format!(
"{}:{}:{}: {} {}\n",
self.color_scheme.colorize_filename(&file),
note.location.line,
note.location.column,
self.color_scheme.colorize_note("note:"),
note.message
));
} else {
output.push_str(&format!(
"{} {}\n",
self.color_scheme.colorize_note("note:"),
note.message
));
}
}
if self.show_option_names {
if let Some(ref flag) = diag.flag_name {
output.push_str(&format!(
"{}",
self.color_scheme.colorize_note(&format!("[-W{}] ", flag))
));
}
}
output
}
pub fn render_all<W: Write>(
&self,
writer: &mut W,
diagnostics: &[StructuredDiagnostic],
source_lines: &[(usize, String)],
) -> io::Result<()> {
for diag in diagnostics {
let rendered = self.render_diagnostic(diag, source_lines);
write!(writer, "{}", rendered)?;
if !rendered.ends_with('\n') {
writeln!(writer)?;
}
}
Ok(())
}
}
impl Default for FullDiagnosticRenderer {
fn default() -> Self {
Self::new()
}
}
pub struct DiagnosticSuggestionEngine {
pub known_names: HashSet<String>,
pub max_distance: usize,
}
impl DiagnosticSuggestionEngine {
pub fn new() -> Self {
Self { known_names: HashSet::new(), max_distance: 2 }
}
pub fn with_names(names: impl IntoIterator<Item = String>) -> Self {
Self { known_names: names.into_iter().collect(), max_distance: 2 }
}
pub fn register(&mut self, name: &str) {
self.known_names.insert(name.to_string());
}
pub fn register_all(&mut self, names: &[&str]) {
for name in names {
self.known_names.insert(name.to_string());
}
}
pub fn suggest(&self, input: &str) -> Option<String> {
let mut best: Option<(usize, String)> = None;
for name in &self.known_names {
let dist = levenshtein_distance(input, name);
if dist <= self.max_distance {
if best.as_ref().map_or(true, |(d, _)| dist < *d) {
best = Some((dist, name.clone()));
}
}
}
best.map(|(_, name)| name)
}
pub fn did_you_mean(&self, input: &str) -> Option<String> {
self.suggest(input).map(|s| format!("did you mean '{}'?", s))
}
pub fn fixit_for(&self, input: &str) -> Option<FixItHint> {
self.suggest(input).map(|replacement| FixItHint::replacement(
SourceRange::point(ClangSourceLocation::invalid()),
&replacement,
))
}
}
impl Default for DiagnosticSuggestionEngine {
fn default() -> Self {
Self::new()
}
}
fn levenshtein_distance(a: &str, b: &str) -> usize {
let a_chars: Vec<char> = a.chars().collect();
let b_chars: Vec<char> = b.chars().collect();
let alen = a_chars.len();
let blen = b_chars.len();
if alen == 0 { return blen; }
if blen == 0 { return alen; }
let mut prev: Vec<usize> = (0..=blen).collect();
let mut curr = vec![0usize; blen + 1];
for i in 1..=alen {
curr[0] = i;
for j in 1..=blen {
let cost = if a_chars[i - 1] == b_chars[j - 1] { 0 } else { 1 };
curr[j] = (prev[j] + 1)
.min(curr[j - 1] + 1)
.min(prev[j - 1] + cost);
}
std::mem::swap(&mut prev, &mut curr);
}
prev[blen]
}
pub struct DiagnosticSeverityManager {
pub file_overrides: HashMap<String, DiagSeverity>,
pub category_overrides: HashMap<String, DiagSeverity>,
pub file_category_overrides: HashMap<(String, String), DiagSeverity>,
pub globally_suppressed: HashSet<DiagID>,
}
impl DiagnosticSeverityManager {
pub fn new() -> Self {
Self {
file_overrides: HashMap::new(),
category_overrides: HashMap::new(),
file_category_overrides: HashMap::new(),
globally_suppressed: HashSet::new(),
}
}
pub fn set_file_severity(&mut self, file: &str, severity: DiagSeverity) {
self.file_overrides.insert(file.to_string(), severity);
}
pub fn set_category_severity(&mut self, category: &str, severity: DiagSeverity) {
self.category_overrides.insert(category.to_string(), severity);
}
pub fn suppress(&mut self, id: DiagID) {
self.globally_suppressed.insert(id);
}
pub fn is_suppressed(&self, id: &DiagID) -> bool {
self.globally_suppressed.contains(id)
}
pub fn effective_severity(
&self,
id: &DiagID,
file: Option<&str>,
category: Option<&str>,
default: DiagSeverity,
) -> DiagSeverity {
if self.globally_suppressed.contains(id) {
return DiagSeverity::Ignored;
}
if let (Some(f), Some(c)) = (file, category) {
if let Some(sev) = self.file_category_overrides.get(&(f.to_string(), c.to_string())) {
return *sev;
}
}
if let Some(c) = category {
if let Some(sev) = self.category_overrides.get(c) {
return *sev;
}
}
if let Some(f) = file {
if let Some(sev) = self.file_overrides.get(f) {
return *sev;
}
}
default
}
}
impl Default for DiagnosticSeverityManager {
fn default() -> Self {
Self::new()
}
}
pub struct CompleteDiagnosticPrinter<W: Write> {
pub writer: W,
pub renderer: FullDiagnosticRenderer,
pub message_catalog: &'static FullDiagnosticMessageCatalog,
pub suggestion_engine: DiagnosticSuggestionEngine,
pub severity_manager: DiagnosticSeverityManager,
pub error_count: usize,
pub warning_count: usize,
pub note_count: usize,
}
impl<W: Write> CompleteDiagnosticPrinter<W> {
pub fn new(writer: W) -> Self {
Self {
writer,
renderer: FullDiagnosticRenderer::new(),
message_catalog: &FullDiagnosticMessageCatalog,
suggestion_engine: DiagnosticSuggestionEngine::new(),
severity_manager: DiagnosticSeverityManager::new(),
error_count: 0,
warning_count: 0,
note_count: 0,
}
}
pub fn print(
&mut self,
id: DiagID,
severity: DiagSeverity,
message: &str,
location: ClangSourceLocation,
ranges: Vec<SourceRange>,
notes: Vec<DiagnosticNote>,
fixits: Vec<FixItHint>,
source_lines: &[(usize, String)],
) -> io::Result<()> {
match severity {
DiagSeverity::Error | DiagSeverity::Fatal => self.error_count += 1,
DiagSeverity::Warning => self.warning_count += 1,
DiagSeverity::Note => self.note_count += 1,
_ => {}
}
let flag_name: Option<String> = DiagID::flag_name(&id).map(|s| s.to_string());
let category_name: String = id.category().to_string();
let diag = StructuredDiagnostic {
id,
severity,
message: message.to_string(),
location,
ranges,
notes,
fixits,
flag_name,
category: category_name,
};
let rendered = self.renderer.render_diagnostic(&diag, source_lines);
write!(self.writer, "{}", rendered)?;
Ok(())
}
pub fn error(
&mut self,
id: DiagID,
location: ClangSourceLocation,
args: &[String],
source_lines: &[(usize, String)],
) -> io::Result<()> {
let template = FullDiagnosticMessageCatalog::message_for(id);
let message = Self::format_message(template, args);
self.print(id, DiagSeverity::Error, &message, location, Vec::new(), Vec::new(), Vec::new(), source_lines)
}
pub fn warning(
&mut self,
id: DiagID,
location: ClangSourceLocation,
args: &[String],
source_lines: &[(usize, String)],
) -> io::Result<()> {
let template = FullDiagnosticMessageCatalog::message_for(id);
let message = Self::format_message(template, args);
self.print(id, DiagSeverity::Warning, &message, location, Vec::new(), Vec::new(), Vec::new(), source_lines)
}
fn format_message(template: &str, args: &[String]) -> String {
let mut result = template.to_string();
for arg in args {
if let Some(pos) = result.find("{}") {
result.replace_range(pos..pos + 2, arg);
}
}
result
}
pub fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
}
impl<W: Write> fmt::Debug for CompleteDiagnosticPrinter<W> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CompleteDiagnosticPrinter")
.field("error_count", &self.error_count)
.field("warning_count", &self.warning_count)
.field("note_count", &self.note_count)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::clang::diagnostics::{
ClangSourceLocation, DiagID, DiagSeverity, DiagnosticNote, FixItHint, SourceRange,
};
#[test]
fn test_category_registry_has_wall_categories() {
let reg = DiagnosticCategoryRegistry::new();
assert!(reg.categories.contains_key("unused-variable"));
assert!(reg.categories.contains_key("sign-compare"));
assert!(reg.categories.contains_key("format"));
assert!(!reg.is_empty());
}
#[test]
fn test_category_registry_has_group_hierarchy() {
let reg = DiagnosticCategoryRegistry::new();
assert!(reg.group_to_members.contains_key("all"));
assert!(reg.group_to_members.contains_key("extra"));
assert!(reg.group_to_members.contains_key("conversion"));
}
#[test]
fn test_category_registry_default_enabled() {
let reg = DiagnosticCategoryRegistry::new();
let cat = reg.categories.get("unused-variable").unwrap();
assert!(cat.enabled_by_default);
}
#[test]
fn test_category_registry_default_disabled() {
let reg = DiagnosticCategoryRegistry::new();
let cat = reg.categories.get("pedantic").unwrap();
assert!(!cat.enabled_by_default);
}
#[test]
fn test_category_registry_len() {
let reg = DiagnosticCategoryRegistry::new();
assert!(reg.len() > 100);
}
#[test]
fn test_message_catalog_syntax_errors() {
assert_eq!(
FullDiagnosticMessageCatalog::message_for(DiagID::ErrExpectedSemiAfterExpr),
"expected ';' after expression"
);
assert_eq!(
FullDiagnosticMessageCatalog::message_for(DiagID::ErrExpectedRBrace),
"expected '}'"
);
}
#[test]
fn test_message_catalog_undeclared() {
assert_eq!(
FullDiagnosticMessageCatalog::message_for(DiagID::ErrUndeclaredIdentifier),
"use of undeclared identifier '{}'"
);
}
#[test]
fn test_message_catalog_warnings() {
assert_eq!(
FullDiagnosticMessageCatalog::message_for(DiagID::WUnusedVariable),
"unused variable '{}'"
);
assert_eq!(
FullDiagnosticMessageCatalog::message_for(DiagID::WSignCompare),
"comparison of integers of different signs: '{}' and '{}'"
);
}
#[test]
fn test_message_catalog_fatal() {
assert_eq!(
FullDiagnosticMessageCatalog::message_for(DiagID::FatalNoInputFiles),
"no input files"
);
}
#[test]
fn test_ansi_color_codes() {
assert_eq!(AnsiColor::Red.code(), "\x1b[31m");
assert_eq!(AnsiColor::Bold.code(), "\x1b[1m");
assert_eq!(AnsiColor::Reset.code(), "\x1b[0m");
}
#[test]
fn test_ansi_color_wrap() {
let wrapped = AnsiColor::Red.wrap("error");
assert!(wrapped.starts_with("\x1b[31m"));
assert!(wrapped.ends_with("\x1b[0m"));
assert!(wrapped.contains("error"));
}
#[test]
fn test_color_scheme_clang() {
let scheme = DiagnosticColorScheme::clang_style();
assert!(scheme.use_colors);
assert!(!scheme.error_colors.is_empty());
}
#[test]
fn test_color_scheme_monochrome() {
let scheme = DiagnosticColorScheme::monochrome();
assert!(!scheme.use_colors);
}
#[test]
fn test_color_scheme_colorize() {
let scheme = DiagnosticColorScheme::clang_style();
let text = scheme.colorize_error("error");
assert!(text.contains("error"));
}
#[test]
fn test_caret_line_single_range() {
let scheme = DiagnosticColorScheme::monochrome();
let line = "int x = 1 + y;";
let ranges = vec![CaretRange::primary(12, 13)];
let caret = CaretRenderer::build_caret_line(line, &ranges, &scheme);
assert!(caret.contains('^'));
}
#[test]
fn test_caret_line_multiple_ranges() {
let scheme = DiagnosticColorScheme::monochrome();
let line = "int x = a + b;";
let ranges = vec![
CaretRange::primary(8, 9),
CaretRange::new(12, 13),
];
let caret = CaretRenderer::build_caret_line(line, &ranges, &scheme);
assert!(caret.contains('^'));
assert!(caret.contains('~'));
}
#[test]
fn test_caret_line_empty_ranges() {
let scheme = DiagnosticColorScheme::monochrome();
let caret = CaretRenderer::build_caret_line("test", &[], &scheme);
assert!(caret.is_empty());
}
#[test]
fn test_source_snippet_render() {
let scheme = DiagnosticColorScheme::monochrome();
let lines = vec![
(1, "int main() {".to_string()),
(2, " return 0;".to_string()),
(3, "}".to_string()),
];
let snippet = SourceSnippet::new(lines.clone(), 2);
let rendered = snippet.render(&scheme);
assert!(rendered.contains("return 0;"));
}
#[test]
fn test_full_renderer_new() {
let renderer = FullDiagnosticRenderer::new();
assert!(renderer.show_column);
assert!(renderer.show_carets);
}
#[test]
fn test_full_renderer_for_ide() {
let renderer = FullDiagnosticRenderer::for_ide();
assert!(!renderer.color_scheme.use_colors);
}
#[test]
fn test_suggestion_engine_exact_match() {
let engine = DiagnosticSuggestionEngine::with_names(
["printf", "scanf", "malloc"].iter().map(|s| s.to_string()),
);
let suggestion = engine.suggest("printf");
assert_eq!(suggestion, Some("printf".to_string()));
}
#[test]
fn test_suggestion_engine_close_match() {
let engine = DiagnosticSuggestionEngine::with_names(
["printf", "scanf", "malloc", "free"].iter().map(|s| s.to_string()),
);
let suggestion = engine.suggest("pritnf"); assert_eq!(suggestion, Some("printf".to_string()));
}
#[test]
fn test_suggestion_engine_no_match() {
let engine = DiagnosticSuggestionEngine::with_names(
["printf", "scanf"].iter().map(|s| s.to_string()),
);
let suggestion = engine.suggest("abcdefghijkl"); assert!(suggestion.is_none());
}
#[test]
fn test_suggestion_engine_did_you_mean() {
let engine = DiagnosticSuggestionEngine::with_names(
["vector", "deque"].iter().map(|s| s.to_string()),
);
let msg = engine.did_you_mean("vectro");
assert_eq!(msg, Some("did you mean 'vector'?".to_string()));
}
#[test]
fn test_levenshtein_basic() {
assert_eq!(levenshtein_distance("kitten", "sitting"), 3);
assert_eq!(levenshtein_distance("abc", "abc"), 0);
assert_eq!(levenshtein_distance("", "abc"), 3);
assert_eq!(levenshtein_distance("abc", ""), 3);
}
#[test]
fn test_severity_manager_suppress() {
let mut mgr = DiagnosticSeverityManager::new();
mgr.suppress(DiagID::WUnusedVariable);
assert!(mgr.is_suppressed(&DiagID::WUnusedVariable));
assert!(!mgr.is_suppressed(&DiagID::WUnusedParameter));
}
#[test]
fn test_severity_manager_file_override() {
let mut mgr = DiagnosticSeverityManager::new();
mgr.set_file_severity("test.c", DiagSeverity::Warning);
let effective = mgr.effective_severity(
&DiagID::WUnusedVariable,
Some("test.c"),
None,
DiagSeverity::Ignored,
);
assert_eq!(effective, DiagSeverity::Warning);
}
#[test]
fn test_severity_manager_category_override() {
let mut mgr = DiagnosticSeverityManager::new();
mgr.set_category_severity("unused-variable", DiagSeverity::Error);
let effective = mgr.effective_severity(
&DiagID::WUnusedVariable,
None,
Some("unused-variable"),
DiagSeverity::Warning,
);
assert_eq!(effective, DiagSeverity::Error);
}
#[test]
fn test_severity_manager_default_fallback() {
let mgr = DiagnosticSeverityManager::new();
let effective = mgr.effective_severity(
&DiagID::WUnusedVariable,
None,
None,
DiagSeverity::Warning,
);
assert_eq!(effective, DiagSeverity::Warning);
}
#[test]
fn test_complete_printer_new() {
let writer = Vec::new();
let printer = CompleteDiagnosticPrinter::new(writer);
assert_eq!(printer.error_count, 0);
assert_eq!(printer.warning_count, 0);
}
#[test]
fn test_format_message_simple() {
let result = CompleteDiagnosticPrinter::<Vec<u8>>::format_message(
"hello {} world",
&["beautiful".to_string()],
);
assert_eq!(result, "hello beautiful world");
}
#[test]
fn test_format_message_multiple() {
let result = CompleteDiagnosticPrinter::<Vec<u8>>::format_message(
"expected '{}' but got '{}'",
&["int".to_string(), "float".to_string()],
);
assert_eq!(result, "expected 'int' but got 'float'");
}
#[test]
fn test_caret_range_new() {
let r = CaretRange::new(10, 15);
assert_eq!(r.start_col, 10);
assert_eq!(r.end_col, 15);
assert!(!r.is_primary);
}
#[test]
fn test_caret_range_primary() {
let r = CaretRange::primary(5, 10);
assert!(r.is_primary);
}
#[test]
fn test_caret_range_with_message() {
let r = CaretRange::with_message(3, 7, "type mismatch here");
assert_eq!(r.message, Some("type mismatch here".to_string()));
}
}