Skip to main content

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(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for LintCode { }
#[automatically_derived]
impl ::core::clone::Clone for LintCode {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for LintCode { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for LintCode {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                LintCode::MXL008 => "MXL008",
                LintCode::MXL106 => "MXL106",
                LintCode::MXL110 => "MXL110",
                LintCode::MXL111 => "MXL111",
                LintCode::MXL120 => "MXL120",
                LintCode::MXL121 => "MXL121",
                LintCode::MXL203 => "MXL203",
                LintCode::MXL204 => "MXL204",
                LintCode::MXL300 => "MXL300",
                LintCode::MXL301 => "MXL301",
                LintCode::MXL302 => "MXL302",
                LintCode::MXL304 => "MXL304",
                LintCode::MXL305 => "MXL305",
            })
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for LintCode { }
#[automatically_derived]
impl ::core::cmp::PartialEq for LintCode {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for LintCode { }Eq, #[automatically_derived]
impl ::core::hash::Hash for LintCode {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
            state)
    }
}Hash, #[automatically_derived]
impl ::core::cmp::PartialOrd for LintCode {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for LintCode {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&::core::intrinsics::discriminant_value(self),
            &::core::intrinsics::discriminant_value(other))
    }
}Ord)]
11pub enum LintCode {
12    // region: Source-side validation
13    /// Trait impl class system incompatible with inherent impl class system.
14    MXL008,
15    // endregion
16
17    // region: P0: High Impact
18    /// Registered top-level function is not `pub`.
19    MXL106,
20    /// Parameter name is an R reserved word; codegen will produce invalid R syntax.
21    MXL110,
22    /// `s4_*` method name on `#[miniextendr(s4)]` impl — codegen auto-prepends `s4_`.
23    MXL111,
24    /// vctrs constructor returns `Self` / named type, or impl has an instance-method receiver.
25    ///
26    /// Mirror: `miniextendr-macros/src/miniextendr_impl.rs` (proc-macro hard error).
27    /// Both checks must fire on the same source; keep them in sync.
28    MXL120,
29    /// A class R code can never create: no constructor in any impl block of
30    /// the type, and no `#[miniextendr]` fn or method returns it.
31    MXL121,
32    // endregion
33
34    // region: P1: Important
35    /// `internal` + `noexport` redundancy.
36    MXL203,
37    /// Doc link `[...pkg::topic...]` into an R package that is neither the
38    /// package itself, a `DESCRIPTION` dependency, nor shipped with R.
39    MXL204,
40    // endregion
41
42    // region: P2: Safety
43    /// Direct `Rf_error`/`Rf_errorcall` call in user code.
44    MXL300,
45    /// `_unchecked` FFI call outside guard context.
46    MXL301,
47    /// `into_sexp()` call inside a `vec!`/array literal — unprotected SEXP across allocations (UAF).
48    MXL302,
49    /// Call to, or declaration of, R's non-API `ATTRIB` / `SET_ATTRIB`.
50    MXL304,
51    /// `Rf_eval` / `R_forceAndCall` / `Rf_applyClosure` / `R_CheckUserInterrupt` in a
52    /// `#[miniextendr]` body: an R error or interrupt there longjmps over the body's Rust frames.
53    MXL305,
54    // endregion
55}
56
57impl fmt::Display for LintCode {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        // Variant names are already `MXL###`, so Debug output works.
60        fmt::Debug::fmt(self, f)
61    }
62}
63
64impl LintCode {
65    /// Default severity for this rule.
66    pub fn default_severity(self) -> super::diagnostic::Severity {
67        use super::diagnostic::Severity;
68        match self {
69            // Source-side checks are errors (CI-blocking).
70            Self::MXL008 => Severity::Error,
71
72            // Codegen-breaking: reserved words produce syntactically invalid R wrappers.
73            Self::MXL110 => Severity::Error,
74
75            // Runtime-breaking: vctrs constructors returning Self produce EXTPTRSXP
76            // which vctrs::new_vctr() rejects; instance-method receivers panic at runtime.
77            Self::MXL120 => Severity::Error,
78
79            // Everything else is a warning.
80            Self::MXL106
81            | Self::MXL111
82            | Self::MXL121
83            | Self::MXL203
84            | Self::MXL204
85            | Self::MXL300
86            | Self::MXL301
87            | Self::MXL302
88            | Self::MXL304
89            | Self::MXL305 => Severity::Warning,
90        }
91    }
92}