miniextendr_lint/rules/
impl_validation.rs1use crate::crate_index::CrateIndex;
8use crate::diagnostic::Diagnostic;
9use crate::lint_code::LintCode;
10
11pub fn check(index: &CrateIndex, diagnostics: &mut Vec<Diagnostic>) {
12 for (path, data) in &index.file_data {
13 for ati in &data.attributed_trait_impls {
20 let trait_style = ati.class_system.as_deref().unwrap_or("env");
21
22 if let Some((inherent_style, _)) = data.inherent_impl_class_systems.get(&ati.type_name)
23 {
24 let inherent = if inherent_style.is_empty() {
25 "env"
26 } else {
27 inherent_style.as_str()
28 };
29
30 let compatible = trait_style == inherent || trait_style == "s3";
32
33 if !compatible {
34 diagnostics.push(Diagnostic::new(
35 LintCode::MXL008,
36 path,
37 ati.line,
38 format!(
39 "#[miniextendr] impl {} for {} uses {}-style, but the inherent \
40 impl uses {}-style. Trait and inherent impls must use the same \
41 class system (S3 traits are compatible with all inherent styles). \
42 Either change the trait impl to #[miniextendr({})] or change \
43 the inherent impl to #[miniextendr({})].",
44 ati.trait_name,
45 ati.type_name,
46 trait_style,
47 inherent,
48 inherent,
49 trait_style,
50 ),
51 ));
52 }
53 }
54 }
55
56 for (type_name, impl_blocks) in &data.impl_blocks_per_type {
58 if impl_blocks.len() <= 1 {
59 continue;
60 }
61
62 let missing_labels: Vec<_> = impl_blocks
64 .iter()
65 .filter(|(label, _)| label.is_none())
66 .map(|(_, line)| *line)
67 .collect();
68
69 if !missing_labels.is_empty() {
70 diagnostics.push(Diagnostic::new(
71 LintCode::MXL009,
72 path,
73 impl_blocks[0].1,
74 format!(
75 "type `{}` has {} impl blocks but some are missing labels. \
76 When a type has multiple #[miniextendr] impl blocks, all must have \
77 distinct labels using #[miniextendr(label = \"...\")]. \
78 Unlabeled impl blocks at lines: {}",
79 type_name,
80 impl_blocks.len(),
81 missing_labels
82 .iter()
83 .map(|l| l.to_string())
84 .collect::<Vec<_>>()
85 .join(", ")
86 ),
87 ));
88 }
89
90 let mut seen_labels: std::collections::HashMap<&str, usize> =
92 std::collections::HashMap::new();
93 for (label, line) in impl_blocks {
94 if let Some(label) = label {
95 if let Some(first_line) = seen_labels.get(label.as_str()) {
96 diagnostics.push(Diagnostic::new(
97 LintCode::MXL010,
98 path,
99 *line,
100 format!(
101 "duplicate label \"{}\" for type `{}`. \
102 First occurrence at line {}. Each impl block must have \
103 a unique label.",
104 label, type_name, first_line
105 ),
106 ));
107 } else {
108 seen_labels.insert(label.as_str(), *line);
109 }
110 }
111 }
112 }
113 }
114}