1//! MXL121: a class R code can never create.
2//!
3//! A type's objects come from its constructor, from the functions that
4//! return one, or from pointers the crate builds itself. The constructor is
5//! the method marked `#[miniextendr(<system>(constructor))]`, else the
6//! receiver-less method returning bare `Self` the macro infers, in any of the
7//! type's inherent `#[miniextendr]` impl blocks. A factory is any
8//! `#[miniextendr]` fn or method returning `T`, `Self` or `ExternalPtr<T>`,
9//! bare or inside `Result` / `Option` (`returned_types` in the index). A
10//! hand-built pointer is an `ExternalPtr::new(T { .. })` / `ExternalPtr::new(T(..))`,
11//! also through a local bound earlier in the same function body
12//! (`let x = T { .. }`, `let x = T(..)`, `let x: T = ..`), or an
13//! `ExternalPtr<T>` written as a type or turbofish, anywhere in the crate,
14//! `#[miniextendr]` or not (`hand_built_types`): a wrapper whose hand-written
15//! `IntoR` builds the pointer is a factory too. Without any of these, every
16//! method of the class needs an object R code has no way to get.
17//!
18//! The type's own methods taking `self` don't count: they need an object
19//! already. Nor do parameters: the caller has the object. vctrs classes are
20//! skipped: the package builds their vectors in R from the payload, not from
21//! a `T`. `ExternalPtr::new(value)` of a value from a call whose type isn't
22//! written (`let record = make_record(inner); ExternalPtr::new(record)`), or
23//! of a value from another function, is invisible; writing the type
24//! (`let record: T = ..`) or `// mxl::allow(MXL121)` covers it. So is code
25//! inside macro calls (`vec![..]`), which `syn` does not parse.
2627use std::collections::{BTreeMap, HashSet};
28use std::path::PathBuf;
2930use crate::crate_index::{CrateIndex, InherentImpl};
31use crate::diagnostic::Diagnostic;
32use crate::lint_code::LintCode;
3334pub fn check(index: &CrateIndex, diagnostics: &mut Vec<Diagnostic>) {
35let mut returned: HashSet<&str> = HashSet::new();
36let mut marked: HashSet<&str> = HashSet::new();
37let mut blocks: BTreeMap<&str, Vec<(&PathBuf, &InherentImpl)>> = BTreeMap::new();
38for (path, data) in &index.file_data {
39 returned.extend(data.returned_types.iter().map(String::as_str));
40 returned.extend(data.hand_built_types.iter().map(String::as_str));
41for (type_name, methods) in &data.impl_methods {
42if methods.iter().any(|m| m.has_constructor_attr) {
43 marked.insert(type_name);
44 }
45 }
46for block in &data.inherent_impls {
47 blocks
48 .entry(&block.type_name)
49 .or_default()
50 .push((path, block));
51 }
52 }
5354for (type_name, blocks) in blocks {
55if returned.contains(type_name)
56 || marked.contains(type_name)
57 || blocks
58 .iter()
59 .any(|(_, b)| b.class_system == "vctrs" || b.allows_mxl121)
60 {
61continue;
62 }
63let Some((path, first)) = blocks.iter().min_by_key(|(path, b)| (*path, b.line)) else {
64continue;
65 };
66 diagnostics.push(
67 Diagnostic::new(
68 LintCode::MXL121,
69 path.as_path(),
70 first.line,
71::alloc::__export::must_use({
::alloc::fmt::format(format_args!("R code can never create a `{0}`: no impl block of `{0}` has a constructor, no `#[miniextendr]` function or method returns one, and the crate builds no `ExternalPtr<{0}>`",
type_name))
})format!(
72"R code can never create a `{type_name}`: no impl block of `{type_name}` \
73 has a constructor, no `#[miniextendr]` function or method returns one, \
74 and the crate builds no `ExternalPtr<{type_name}>`"
75),
76 )
77 .with_help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("mark a constructor with `#[miniextendr(<system>(constructor))]`, or add a `#[miniextendr]` function returning `{0}` or `ExternalPtr<{0}>`; where the crate builds the pointer from a value from a call whose type isn\'t written, write it (`let value: {0} = ...`) or put `// mxl::allow(MXL121)` on the impl",
type_name))
})format!(
78"mark a constructor with `#[miniextendr(<system>(constructor))]`, or add a \
79 `#[miniextendr]` function returning `{type_name}` or `ExternalPtr<{type_name}>`; \
80 where the crate builds the pointer from a value from a call whose type \
81 isn't written, write it (`let value: {type_name} = ...`) or put \
82 `// mxl::allow(MXL121)` on the impl"
83)),
84 );
85 }
86}