miniextendr_lint/rules/s4_method_prefix.rs
1//! MXL111: `s4_*` method name on `#[miniextendr(s4)]` impl.
2//!
3//! S4 codegen auto-prepends `s4_` to every instance method name when generating
4//! the R generic. A Rust method named `s4_foo` on an `#[miniextendr(s4)]` impl
5//! produces an R generic `s4_s4_foo`, making it unreachable via the expected
6//! `s4_foo(obj, ...)` call.
7
8use crate::crate_index::CrateIndex;
9use crate::diagnostic::Diagnostic;
10use crate::lint_code::LintCode;
11
12pub fn check(index: &CrateIndex, diagnostics: &mut Vec<Diagnostic>) {
13 for (path, data) in &index.file_data {
14 for (impl_type, methods) in &data.impl_methods {
15 for entry in methods {
16 if entry.class_system != "s4" {
17 continue;
18 }
19 // Constructors (`new`) are not auto-prefixed — they become the
20 // class constructor function, named after the type, not `s4_new`.
21 if entry.method_name == "new" {
22 continue;
23 }
24 if let Some(rest) = entry.method_name.strip_prefix("s4_") {
25 diagnostics.push(
26 Diagnostic::new(
27 LintCode::MXL111,
28 path,
29 entry.line,
30 format!(
31 "method `{impl_type}::{}` will produce R generic \
32 `s4_{}` (s4 codegen auto-prepends `s4_`); \
33 rename to `{rest}` to avoid the double prefix",
34 entry.method_name, entry.method_name,
35 ),
36 )
37 .with_help(
38 "the macro prepends `s4_` to every S4 method name; \
39 keep the Rust method name unprefixed",
40 ),
41 );
42 }
43 }
44 }
45 }
46}