rulewright 0.1.1

Executable engineering standards for Rust workspaces
Documentation
use ra_ap_syntax::ast;

use crate::{AstCtx, Example, Violation};

#[rustfmt::skip]
const EXAMPLES: &[Example] = &[
    Example {
        label: "impl Into directly",
        code: "struct Foo;\nimpl Into<String> for Foo { fn into(self) -> String { String::new() } }",
        pass: false,
    },
    Example {
        label: "impl From instead",
        code: "struct Foo;\nimpl From<Foo> for String { fn from(_: Foo) -> String { String::new() } }",
        pass: true,
    },
    Example {
        label: "impl Into in test",
        code: "#[cfg(test)]\nmod tests {\n    struct Foo;\n    impl Into<String> for Foo { fn into(self) -> String { String::new() } }\n}",
        pass: true,
    },
];

crate::ast_rule!(
    impl_into_for_owned,
    "Flag `impl Into<T> for X` — implement `From<X> for T` instead (gives Into for free).",
    "Implementing From<X> for T gives you Into<T> for X for free. Implementing Into directly is redundant and non-standard.",
    Medium,
);

fn check_impl_into_for_owned(ctx: &AstCtx<'_>) -> Vec<Violation> {
    ctx.nodes::<ast::Impl>()
        .filter(|item| !ctx.is_in_test(item))
        .filter_map(|item| {
            let ast::Type::PathType(path) = item.trait_()? else {
                return None;
            };
            let name = path.path()?.segment()?.name_ref()?;

            (name.text() == "Into").then(|| {
                ctx.violation(
                    &name,
                    "implement `From` instead of `Into` — From gives you Into for free",
                )
            })
        })
        .collect()
}

crate::rulewright_ast_test!(check_impl_into_for_owned, {
    crate::example_tests!(EXAMPLES, check_impl_into_for_owned);
});