Skip to main content

wowlab_tidy/languages/rust/rules/style/
where_clauses.rs

1use ra_ap_syntax::{
2    AstNode,
3    ast::{self, HasTypeBounds},
4};
5
6use crate::{AstCtx, Example, Violation};
7
8#[rustfmt::skip]
9const EXAMPLES: &[Example] = &[
10    Example {
11        label: "function where clause",
12        code: "fn render<T>(value: T) where T: std::fmt::Display {}",
13        pass: true,
14    },
15    Example {
16        label: "inline function bound",
17        code: "fn render<T: std::fmt::Display>(value: T) {}",
18        pass: false,
19    },
20    Example {
21        label: "inline struct bound",
22        code: "struct Wrapper<T: Clone> { value: T }",
23        pass: false,
24    },
25    Example {
26        label: "lifetime bounds are exempt",
27        code: "struct Borrowed<'a: 'static> { value: &'a str }",
28        pass: true,
29    },
30    Example {
31        label: "const generics are exempt",
32        code: "struct Buffer<const N: usize> { bytes: [u8; N] }",
33        pass: true,
34    },
35    Example {
36        label: "default type params are exempt",
37        code: "struct Wrapper<T: Clone = String> { value: T }",
38        pass: true,
39    },
40];
41
42crate::ast_rule!(
43    where_clauses,
44    "Require type-parameter trait bounds to use where clauses.",
45    "Where clauses keep function and type declarations readable as constraints grow.",
46    Low,
47);
48
49fn check_where_clauses(ctx: &AstCtx<'_>) -> Vec<Violation> {
50    let bounded_params = ctx
51        .nodes::<ast::TypeParam>()
52        .filter(|param| param.default_type().is_none())
53        .filter(|param| {
54            param
55                .type_bound_list()
56                .is_some_and(|bounds| bounds.bounds().next().is_some())
57        });
58
59    bounded_params
60        .filter(|param| {
61            param.syntax().ancestors().any(|ancestor| {
62                ast::Fn::can_cast(ancestor.kind())
63                    || ast::Impl::can_cast(ancestor.kind())
64                    || ast::Struct::can_cast(ancestor.kind())
65                    || ast::Enum::can_cast(ancestor.kind())
66            })
67        })
68        .map(|param| {
69            ctx.violation(
70                &param,
71                "trait bounds in generic parameter lists must move to a where clause",
72            )
73        })
74        .collect()
75}
76
77crate::tidy_ast_test!(check_where_clauses, {
78    crate::example_tests!(EXAMPLES, check_where_clauses);
79});