Skip to content

Commit 06f65d8

Browse files
committed
Check structs and enums for use_self
1 parent 5b23bd4 commit 06f65d8

File tree

6 files changed

+366
-4
lines changed

6 files changed

+366
-4
lines changed

clippy_config/src/conf.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,9 @@ define_Conf! {
809809
/// exported visibility, or whether they are marked as "pub".
810810
#[lints(pub_underscore_fields)]
811811
pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported,
812+
/// Whether the type itself in a struct or enum should be replaced with `Self` when encountering recursive types.
813+
#[lints(use_self)]
814+
recursive_self_in_struct: bool = true,
812815
/// Whether to lint only if it's multiline.
813816
#[lints(semicolon_inside_block)]
814817
semicolon_inside_block_ignore_singleline: bool = false,

clippy_lints/src/use_self.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,15 @@ declare_clippy_lint! {
5858
pub struct UseSelf {
5959
msrv: Msrv,
6060
stack: Vec<StackItem>,
61+
recursive_struct: bool,
6162
}
6263

6364
impl UseSelf {
6465
pub fn new(conf: &'static Conf) -> Self {
6566
Self {
6667
msrv: conf.msrv,
6768
stack: Vec::new(),
69+
recursive_struct: conf.recursive_self_in_struct,
6870
}
6971
}
7072
}
@@ -84,10 +86,10 @@ const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
8486

8587
impl<'tcx> LateLintPass<'tcx> for UseSelf {
8688
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
87-
// We push the self types of `impl`s on a stack here. Only the top type on the stack is
88-
// relevant for linting, since this is the self type of the `impl` we're currently in. To
89-
// avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
90-
// we're in an `impl` or nested item, that we don't want to lint
89+
// We push the self types of items on a stack here. Only the top type on the stack is
90+
// relevant for linting, since this is the self type of the item we're currently in. To
91+
// avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal that
92+
// we're in an item or nested item that we don't want to lint
9193
let stack_item = if let ItemKind::Impl(Impl { self_ty, generics, .. }) = item.kind
9294
&& let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind
9395
&& let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args
@@ -112,6 +114,15 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
112114
impl_id: item.owner_id.def_id,
113115
types_to_skip,
114116
}
117+
} else if let ItemKind::Struct(..) | ItemKind::Enum(..) = item.kind
118+
&& self.recursive_struct
119+
&& !item.span.from_expansion()
120+
&& !is_from_proc_macro(cx, item)
121+
{
122+
StackItem::Check {
123+
impl_id: item.owner_id.def_id,
124+
types_to_skip: FxHashSet::default(),
125+
}
115126
} else {
116127
StackItem::NoCheck
117128
};

tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
6666
msrv
6767
pass-by-value-size-limit
6868
pub-underscore-fields-behavior
69+
recursive-self-in-struct
6970
semicolon-inside-block-ignore-singleline
7071
semicolon-outside-block-ignore-multiline
7172
single-char-binding-names-threshold
@@ -161,6 +162,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
161162
msrv
162163
pass-by-value-size-limit
163164
pub-underscore-fields-behavior
165+
recursive-self-in-struct
164166
semicolon-inside-block-ignore-singleline
165167
semicolon-outside-block-ignore-multiline
166168
single-char-binding-names-threshold
@@ -256,6 +258,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
256258
msrv
257259
pass-by-value-size-limit
258260
pub-underscore-fields-behavior
261+
recursive-self-in-struct
259262
semicolon-inside-block-ignore-singleline
260263
semicolon-outside-block-ignore-multiline
261264
single-char-binding-names-threshold

tests/ui/use_self_structs.fixed

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#![warn(clippy::use_self)]
2+
#![allow(clippy::type_complexity)]
3+
4+
fn main() {}
5+
6+
struct Basic {
7+
flag: Option<Box<Self>>,
8+
//~^ use_self
9+
}
10+
11+
struct BasicSelf {
12+
okay: Option<Box<Self>>,
13+
}
14+
15+
struct Generic<'q, T: From<u8>> {
16+
t: &'q T,
17+
flag: Option<Box<Self>>,
18+
//~^ use_self
19+
}
20+
21+
struct GenericSelf<'q, T: From<u8>> {
22+
t: &'q T,
23+
okay: Option<Box<Self>>,
24+
}
25+
26+
struct MixedLifetimes<'q, T: From<u8> + 'static> {
27+
t: &'q T,
28+
okay: Option<Box<MixedLifetimes<'static, T>>>,
29+
}
30+
31+
struct ConcreteType<'q, T: From<u8>> {
32+
t: &'q T,
33+
okay: Option<Box<ConcreteType<'q, u64>>>,
34+
}
35+
36+
struct ConcreteAndGeneric<'q, T: From<u8>> {
37+
t: &'q T,
38+
flag: Option<Box<Self>>,
39+
//~^ use_self
40+
okay: Option<Box<ConcreteAndGeneric<'q, u64>>>,
41+
}
42+
43+
struct ConcreteAndGenericSelf<'q, T: From<u8>> {
44+
t: &'q T,
45+
okay_1: Option<Box<Self>>,
46+
okay_2: Option<Box<ConcreteAndGeneric<'q, u64>>>,
47+
}
48+
49+
macro_rules! recursive_struct {
50+
($name:ident) => {
51+
struct $name {
52+
okay: Option<Box<$name>>,
53+
}
54+
};
55+
}
56+
57+
recursive_struct!(X);
58+
recursive_struct!(Y);
59+
recursive_struct!(Z);
60+
61+
struct Tree {
62+
left: Option<Box<Self>>,
63+
//~^ use_self
64+
right: Option<Box<Self>>,
65+
//~^ use_self
66+
}
67+
68+
struct TreeSelf {
69+
left: Option<Box<Self>>,
70+
right: Option<Box<Self>>,
71+
}
72+
73+
struct TreeMixed {
74+
left: Option<Box<Self>>,
75+
right: Option<Box<Self>>,
76+
//~^ use_self
77+
}
78+
79+
struct Nested {
80+
flag: Option<Box<Option<Box<Self>>>>,
81+
//~^ use_self
82+
}
83+
84+
struct NestedSelf {
85+
okay: Option<Box<Option<Box<Self>>>>,
86+
}
87+
88+
struct Tuple(Option<Box<Self>>);
89+
//~^ use_self
90+
91+
struct TupleSelf(Option<Box<Self>>);
92+
93+
use std::cell::RefCell;
94+
use std::rc::{Rc, Weak};
95+
96+
struct Containers {
97+
flag: Vec<Option<Rc<RefCell<Weak<Vec<Box<Self>>>>>>>,
98+
//~^ use_self
99+
}
100+
101+
struct ContainersSelf {
102+
okay: Vec<Option<Rc<RefCell<Weak<Vec<Box<Self>>>>>>>,
103+
}
104+
105+
type Wrappers<T> = Vec<Option<Rc<RefCell<Weak<Vec<Box<T>>>>>>>;
106+
107+
struct Alias {
108+
flag: Wrappers<Self>,
109+
//~^ use_self
110+
}
111+
112+
struct AliasSelf {
113+
okay: Wrappers<Self>,
114+
}
115+
116+
struct Array<const N: usize> {
117+
flag: [Option<Box<Self>>; N],
118+
//~^ use_self
119+
}
120+
121+
struct ArraySelf<const N: usize> {
122+
okay: [Option<Box<Self>>; N],
123+
}
124+
125+
enum Enum {
126+
Nil,
127+
Cons(Box<Self>),
128+
//~^ use_self
129+
}
130+
131+
enum EnumSelf {
132+
Nil,
133+
Cons(Box<Self>),
134+
}

tests/ui/use_self_structs.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#![warn(clippy::use_self)]
2+
#![allow(clippy::type_complexity)]
3+
4+
fn main() {}
5+
6+
struct Basic {
7+
flag: Option<Box<Basic>>,
8+
//~^ use_self
9+
}
10+
11+
struct BasicSelf {
12+
okay: Option<Box<Self>>,
13+
}
14+
15+
struct Generic<'q, T: From<u8>> {
16+
t: &'q T,
17+
flag: Option<Box<Generic<'q, T>>>,
18+
//~^ use_self
19+
}
20+
21+
struct GenericSelf<'q, T: From<u8>> {
22+
t: &'q T,
23+
okay: Option<Box<Self>>,
24+
}
25+
26+
struct MixedLifetimes<'q, T: From<u8> + 'static> {
27+
t: &'q T,
28+
okay: Option<Box<MixedLifetimes<'static, T>>>,
29+
}
30+
31+
struct ConcreteType<'q, T: From<u8>> {
32+
t: &'q T,
33+
okay: Option<Box<ConcreteType<'q, u64>>>,
34+
}
35+
36+
struct ConcreteAndGeneric<'q, T: From<u8>> {
37+
t: &'q T,
38+
flag: Option<Box<ConcreteAndGeneric<'q, T>>>,
39+
//~^ use_self
40+
okay: Option<Box<ConcreteAndGeneric<'q, u64>>>,
41+
}
42+
43+
struct ConcreteAndGenericSelf<'q, T: From<u8>> {
44+
t: &'q T,
45+
okay_1: Option<Box<Self>>,
46+
okay_2: Option<Box<ConcreteAndGeneric<'q, u64>>>,
47+
}
48+
49+
macro_rules! recursive_struct {
50+
($name:ident) => {
51+
struct $name {
52+
okay: Option<Box<$name>>,
53+
}
54+
};
55+
}
56+
57+
recursive_struct!(X);
58+
recursive_struct!(Y);
59+
recursive_struct!(Z);
60+
61+
struct Tree {
62+
left: Option<Box<Tree>>,
63+
//~^ use_self
64+
right: Option<Box<Tree>>,
65+
//~^ use_self
66+
}
67+
68+
struct TreeSelf {
69+
left: Option<Box<Self>>,
70+
right: Option<Box<Self>>,
71+
}
72+
73+
struct TreeMixed {
74+
left: Option<Box<Self>>,
75+
right: Option<Box<TreeMixed>>,
76+
//~^ use_self
77+
}
78+
79+
struct Nested {
80+
flag: Option<Box<Option<Box<Nested>>>>,
81+
//~^ use_self
82+
}
83+
84+
struct NestedSelf {
85+
okay: Option<Box<Option<Box<Self>>>>,
86+
}
87+
88+
struct Tuple(Option<Box<Tuple>>);
89+
//~^ use_self
90+
91+
struct TupleSelf(Option<Box<Self>>);
92+
93+
use std::cell::RefCell;
94+
use std::rc::{Rc, Weak};
95+
96+
struct Containers {
97+
flag: Vec<Option<Rc<RefCell<Weak<Vec<Box<Containers>>>>>>>,
98+
//~^ use_self
99+
}
100+
101+
struct ContainersSelf {
102+
okay: Vec<Option<Rc<RefCell<Weak<Vec<Box<Self>>>>>>>,
103+
}
104+
105+
type Wrappers<T> = Vec<Option<Rc<RefCell<Weak<Vec<Box<T>>>>>>>;
106+
107+
struct Alias {
108+
flag: Wrappers<Alias>,
109+
//~^ use_self
110+
}
111+
112+
struct AliasSelf {
113+
okay: Wrappers<Self>,
114+
}
115+
116+
struct Array<const N: usize> {
117+
flag: [Option<Box<Array<N>>>; N],
118+
//~^ use_self
119+
}
120+
121+
struct ArraySelf<const N: usize> {
122+
okay: [Option<Box<Self>>; N],
123+
}
124+
125+
enum Enum {
126+
Nil,
127+
Cons(Box<Enum>),
128+
//~^ use_self
129+
}
130+
131+
enum EnumSelf {
132+
Nil,
133+
Cons(Box<Self>),
134+
}

0 commit comments

Comments
 (0)