fn detect_duplicate_wrapper_defs(
content: &str,
accessor_producers: &HashMap<String, String>,
) -> Result<(), String>Expand description
Detect conflicting top-level function and class/namespace definitions in the assembled R wrapper text.
This is the write-time counterpart to the compile-time
check_s7_shortcut_collisions (in miniextendr-macros): it catches name
collisions that span separate macro expansions and are therefore invisible
to any single proc-macro invocation. The two motivating cases (#991):
- An S7 fast-path shortcut
<Class>_<method>colliding with a#[derive(ExternalPtr)]sidecar accessor<Class>_get_<field>/<Class>_set_<field>– the sidecar field names live in theMX_S7_SIDECAR_PROPSslice, which the impl-block macro can’t see. - Two
#[miniextendr(s7)]impl blocks on the same type each emitting a<Class>_<method>shortcut – cross-impl-block, also invisible to a single expansion.
Without this check the second definition silently clobbers the first (last-write-wins in R), so a call dispatches to the wrong implementation.
accessor_producers maps a known sidecar-accessor name to a description of
its producing derive; when a duplicate matches one we name the derive in the
message, otherwise we report the generic “defined more than once” form.
Class and namespace bindings (new.env, R6::R6Class, S7::new_class)
occupy the same namespace as functions. Include them so a free function
cannot silently replace a class object (#1677). S4’s setClass() is not a
binding assignment: its same-named constructor function remains valid.
Guarded S3/S4/S7 generic declarations may repeat across classes, but must
not share their binding with an ordinary function or class object (#1677).
A class object defined twice would replace the first together with the
methods bound to it. The impl blocks of one Rust type are merged into one
class before this scan, and two types claiming one class name are refused
there (merge_class_blocks, #1776), so this is the last line of defence.
Every message names both producers, read from the
# Generated from Rust … header that opens each wrapper fragment.
Returns Err(message) naming the offending binding on the first collision
found; Ok(()) when names are unique apart from shared generic declarations.