wowlab_tidy/languages/rust/rules/api/
impl_into_for_owned.rs1use 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});