miniextendr_lint/lint_code.rs
1//! Stable lint rule identifiers.
2//!
3//! Each rule has a code like `MXL008` that is grep-able and CI-friendly.
4
5use std::fmt;
6
7/// Stable lint rule identifier.
8///
9/// Display format is `MXL###`, derived directly from the variant name.
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
11pub enum LintCode {
12 // region: Source-side validation
13 /// Trait impl class system incompatible with inherent impl class system.
14 MXL008,
15 /// Multiple impl blocks for one type without labels.
16 MXL009,
17 /// Duplicate labels on impl blocks for one type.
18 MXL010,
19 // endregion
20
21 // region: P0: High Impact
22 /// Registered top-level function is not `pub`.
23 MXL106,
24 /// Parameter name is an R reserved word; codegen will produce invalid R syntax.
25 MXL110,
26 /// `s4_*` method name on `#[miniextendr(s4)]` impl — codegen auto-prepends `s4_`.
27 MXL111,
28 /// vctrs constructor returns `Self` / named type, or impl has an instance-method receiver.
29 ///
30 /// Mirror: `miniextendr-macros/src/miniextendr_impl.rs` (proc-macro hard error).
31 /// Both checks must fire on the same source; keep them in sync.
32 MXL120,
33 // endregion
34
35 // region: P1: Important
36 /// `internal` + `noexport` redundancy.
37 MXL203,
38 // endregion
39
40 // region: P2: Safety
41 /// Direct `Rf_error`/`Rf_errorcall` call in user code.
42 MXL300,
43 /// `_unchecked` FFI call outside guard context.
44 MXL301,
45 /// `into_sexp()` call inside a `vec!`/array literal — unprotected SEXP across allocations (UAF).
46 MXL302,
47 /// Two `#[miniextendr]` trait impls collapse to the same vtable symbol
48 /// (`__VTABLE_{CRATE}_{TRAIT}_FOR_{TYPE}`; the crate prefix is constant
49 /// within one crate, so the rule compares the crate-invariant suffix)
50 /// after the macro's case-folding.
51 MXL303,
52 // endregion
53}
54
55impl fmt::Display for LintCode {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 // Variant names are already `MXL###`, so Debug output works.
58 fmt::Debug::fmt(self, f)
59 }
60}
61
62impl LintCode {
63 /// Default severity for this rule.
64 pub fn default_severity(self) -> super::diagnostic::Severity {
65 use super::diagnostic::Severity;
66 match self {
67 // Source-side checks are errors (CI-blocking).
68 Self::MXL008 | Self::MXL009 | Self::MXL010 => Severity::Error,
69
70 // Codegen-breaking: reserved words produce syntactically invalid R wrappers.
71 Self::MXL110 => Severity::Error,
72
73 // Runtime-breaking: vctrs constructors returning Self produce EXTPTRSXP
74 // which vctrs::new_vctr() rejects; instance-method receivers panic at runtime.
75 Self::MXL120 => Severity::Error,
76
77 // Build-breaking: colliding trait impls emit duplicate `#[no_mangle]`
78 // vtable statics → cryptic linker error divorced from the source.
79 Self::MXL303 => Severity::Error,
80
81 // Everything else is a warning.
82 Self::MXL106
83 | Self::MXL111
84 | Self::MXL203
85 | Self::MXL300
86 | Self::MXL301
87 | Self::MXL302 => Severity::Warning,
88 }
89 }
90}