Skip to main content

wowlab_tidy/languages/rust/rules/correctness/
lossy_cast.rs

1use ra_ap_syntax::ast;
2
3use crate::{AstCtx, Example, Violation};
4
5#[rustfmt::skip]
6const EXAMPLES: &[Example] = &[
7    Example {
8        label: "cast to u8",
9        code: "fn f() { let x = 42u64 as u8; }",
10        pass: false,
11    },
12    Example {
13        label: "cast to f32",
14        code: "fn f() { let x = 1.0f64 as f32; }",
15        pass: false,
16    },
17    Example {
18        label: "cast to i16",
19        code: "fn f() { let x = 1000i32 as i16; }",
20        pass: false,
21    },
22    Example {
23        label: "cast to u32",
24        code: "fn f() { let x = 42u64 as u32; }",
25        pass: true,
26    },
27    Example {
28        label: "cast to u64",
29        code: "fn f() { let x = 42u32 as u64; }",
30        pass: true,
31    },
32    Example {
33        label: "cast to usize",
34        code: "fn f() { let x = 42u32 as usize; }",
35        pass: true,
36    },
37    Example {
38        label: "cast in test module",
39        code: "#[cfg(test)]\nmod tests {\n    fn f() { let x = 42u64 as u8; }\n}",
40        pass: true,
41    },
42];
43
44crate::ast_rule!(
45    lossy_cast,
46    "Flag `as` casts to types that lose precision (`f32`, `u8`, `u16`, `i8`, `i16`).",
47    "Casting to a smaller type (u64 as u8) silently truncates. Use try_into() to catch overflow at runtime.",
48    Medium,
49);
50
51const LOSSY_TARGETS: &[&str] = &["f32", "u8", "u16", "i8", "i16"];
52
53fn check_lossy_cast(ctx: &AstCtx<'_>) -> Vec<Violation> {
54    ctx.nodes::<ast::CastExpr>()
55        .filter(|cast| !ctx.is_in_test(cast))
56        .filter_map(|cast| {
57            let ast::Type::PathType(path) = cast.ty()? else {
58                return None;
59            };
60            let ident = path.path()?.segment()?.name_ref()?;
61
62            LOSSY_TARGETS
63                .contains(&ident.text().as_str())
64                .then(|| {
65                    ctx.violation(
66                        &cast,
67                        format!(
68                            "potentially lossy cast to `{ident}` — use explicit conversion (e.g. try_into(), try_from())"
69                        ),
70                    )
71                })
72        })
73        .collect()
74}
75
76crate::tidy_ast_test!(check_lossy_cast, {
77    crate::example_tests!(EXAMPLES, check_lossy_cast);
78});