Skip to main content

wowlab_tidy/languages/rust/rules/api/
impl_into_for_owned.rs

1use ra_ap_syntax::ast;
2
3use crate::{AstCtx, Example, Violation};
4
5#[rustfmt::skip]
6const EXAMPLES: &[Example] = &[
7    Example {
8        label: "impl Into directly",
9        code: "struct Foo;\nimpl Into<String> for Foo { fn into(self) -> String { String::new() } }",
10        pass: false,
11    },
12    Example {
13        label: "impl From instead",
14        code: "struct Foo;\nimpl From<Foo> for String { fn from(_: Foo) -> String { String::new() } }",
15        pass: true,
16    },
17    Example {
18        label: "impl Into in test",
19        code: "#[cfg(test)]\nmod tests {\n    struct Foo;\n    impl Into<String> for Foo { fn into(self) -> String { String::new() } }\n}",
20        pass: true,
21    },
22];
23
24crate::ast_rule!(
25    impl_into_for_owned,
26    "Flag `impl Into<T> for X` — implement `From<X> for T` instead (gives Into for free).",
27    "Implementing From<X> for T gives you Into<T> for X for free. Implementing Into directly is redundant and non-standard.",
28    Medium,
29);
30
31fn check_impl_into_for_owned(ctx: &AstCtx<'_>) -> Vec<Violation> {
32    ctx.nodes::<ast::Impl>()
33        .filter(|item| !ctx.is_in_test(item))
34        .filter_map(|item| {
35            let ast::Type::PathType(path) = item.trait_()? else {
36                return None;
37            };
38            let name = path.path()?.segment()?.name_ref()?;
39
40            (name.text() == "Into").then(|| {
41                ctx.violation(
42                    &name,
43                    "implement `From` instead of `Into` — From gives you Into for free",
44                )
45            })
46        })
47        .collect()
48}
49
50crate::tidy_ast_test!(check_impl_into_for_owned, {
51    crate::example_tests!(EXAMPLES, check_impl_into_for_owned);
52});