wowlab_tidy/languages/rust/rules/complexity/
multiple_inherent_impl.rs1use std::collections::HashSet;
4
5#[cfg(test)]
6use googletest::prelude::*;
7use ra_ap_syntax::ast;
8
9use super::support;
10use crate::{AstCtx, Example, Violation};
11
12#[rustfmt::skip]
13const EXAMPLES: &[Example] = &[
14 Example {
15 label: "duplicate impl blocks",
16 code: "struct Foo;\nimpl Foo { fn a() {} }\nimpl Foo { fn b() {} }",
17 pass: false,
18 },
19 Example {
20 label: "different types",
21 code: "struct Foo;\nstruct Bar;\nimpl Foo {}\nimpl Bar {}",
22 pass: true,
23 },
24 Example {
25 label: "trait impl not counted",
26 code: "struct Foo;\nimpl Foo {}\nimpl std::fmt::Display for Foo { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { Ok(()) } }",
27 pass: true,
28 },
29 Example {
30 label: "duplicate impl in test module",
31 code: "#[cfg(test)]\nmod tests {\n struct Foo;\n impl Foo { fn a() {} }\n impl Foo { fn b() {} }\n}",
32 pass: true,
33 },
34];
35
36crate::ast_rule!(
37 multiple_inherent_impl,
38 "Flag multiple `impl Foo` blocks for the same type in one file.",
39 "Split impl blocks for the same type scatter related methods. Keep them in one block for discoverability.",
40);
41
42fn check_multiple_inherent_impl(ctx: &AstCtx<'_>) -> Vec<Violation> {
43 let mut seen = HashSet::new();
44 let mut violations = Vec::new();
45
46 for item_impl in ctx
47 .nodes::<ast::Impl>()
48 .filter(|item_impl| !ctx.is_in_test(item_impl) && item_impl.trait_().is_none())
49 {
50 let Some(self_type) = item_impl.self_ty() else {
51 continue;
52 };
53 let Some(name) = support::self_type_name(&self_type) else {
54 continue;
55 };
56
57 if seen.contains(&name) {
58 violations.push(ctx.violation(
59 &self_type,
60 format!("multiple inherent impl blocks for `{name}` in the same file — merge them"),
61 ));
62 } else {
63 seen.insert(name);
64 }
65 }
66
67 violations
68}
69
70crate::tidy_ast_test!(check_multiple_inherent_impl, {
71 crate::example_tests!(EXAMPLES, check_multiple_inherent_impl);
72
73 #[gtest]
74 fn three_impls_flags_second_and_third() -> Result<()> {
75 let v = run("struct Foo;\nimpl Foo {}\nimpl Foo {}\nimpl Foo {}");
76 verify_eq!(v.len(), 2)?;
77
78 Ok(())
79 }
80});