Skip to content

Commit 9f7fb41

Browse files
committed
fix: multipart suggestions for derivable_impls
1 parent 8298da7 commit 9f7fb41

File tree

4 files changed

+343
-62
lines changed

4 files changed

+343
-62
lines changed

clippy_lints/src/derivable_impls.rs

+23-25
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,15 @@ fn check_struct<'tcx>(
132132

133133
if should_emit {
134134
let struct_span = cx.tcx.def_span(adt_def.did());
135+
let suggestions = vec![
136+
(item.span, String::new()), // Remove the manual implementation
137+
(struct_span.shrink_to_lo(), "#[derive(Default)]\n".to_string()), // Add the derive attribute
138+
];
139+
135140
span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
136-
diag.span_suggestion_hidden(
137-
item.span,
138-
"remove the manual implementation...",
139-
String::new(),
140-
Applicability::MachineApplicable,
141-
);
142-
diag.span_suggestion(
143-
struct_span.shrink_to_lo(),
144-
"...and instead derive it",
145-
"#[derive(Default)]\n".to_string(),
141+
diag.multipart_suggestion(
142+
"replace the manual implementation with a derive attribute",
143+
suggestions,
146144
Applicability::MachineApplicable,
147145
);
148146
});
@@ -161,23 +159,23 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex
161159
let indent_enum = indent_of(cx, enum_span).unwrap_or(0);
162160
let variant_span = cx.tcx.def_span(variant_def.def_id);
163161
let indent_variant = indent_of(cx, variant_span).unwrap_or(0);
164-
span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
165-
diag.span_suggestion_hidden(
166-
item.span,
167-
"remove the manual implementation...",
168-
String::new(),
169-
Applicability::MachineApplicable,
170-
);
171-
diag.span_suggestion(
162+
163+
let suggestions = vec![
164+
(item.span, String::new()), // Remove the manual implementation
165+
(
172166
enum_span.shrink_to_lo(),
173-
"...and instead derive it...",
174-
format!("#[derive(Default)]\n{indent}", indent = " ".repeat(indent_enum),),
175-
Applicability::MachineApplicable,
176-
);
177-
diag.span_suggestion(
167+
format!("#[derive(Default)]\n{}", " ".repeat(indent_enum)),
168+
), // Add the derive attribute
169+
(
178170
variant_span.shrink_to_lo(),
179-
"...and mark the default variant",
180-
format!("#[default]\n{indent}", indent = " ".repeat(indent_variant),),
171+
format!("#[default]\n{}", " ".repeat(indent_variant)),
172+
), // Mark the default variant
173+
];
174+
175+
span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
176+
diag.multipart_suggestion(
177+
"replace the manual implementation with a derive attribute and mark the default variant",
178+
suggestions,
181179
Applicability::MachineApplicable,
182180
);
183181
});

tests/ui/derivable_impls.fixed

+295
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
#![allow(dead_code)]
2+
3+
use std::collections::HashMap;
4+
5+
#[derive(Default)]
6+
struct FooDefault<'a> {
7+
a: bool,
8+
b: i32,
9+
c: u64,
10+
d: Vec<i32>,
11+
e: FooND1,
12+
f: FooND2,
13+
g: HashMap<i32, i32>,
14+
h: (i32, Vec<i32>),
15+
i: [Vec<i32>; 3],
16+
j: [i32; 5],
17+
k: Option<i32>,
18+
l: &'a [i32],
19+
}
20+
21+
22+
#[derive(Default)]
23+
struct TupleDefault(bool, i32, u64);
24+
25+
26+
struct FooND1 {
27+
a: bool,
28+
}
29+
30+
impl std::default::Default for FooND1 {
31+
fn default() -> Self {
32+
Self { a: true }
33+
}
34+
}
35+
36+
struct FooND2 {
37+
a: i32,
38+
}
39+
40+
impl std::default::Default for FooND2 {
41+
fn default() -> Self {
42+
Self { a: 5 }
43+
}
44+
}
45+
46+
struct FooNDNew {
47+
a: bool,
48+
}
49+
50+
impl FooNDNew {
51+
fn new() -> Self {
52+
Self { a: true }
53+
}
54+
}
55+
56+
impl Default for FooNDNew {
57+
fn default() -> Self {
58+
Self::new()
59+
}
60+
}
61+
62+
struct FooNDVec(Vec<i32>);
63+
64+
impl Default for FooNDVec {
65+
fn default() -> Self {
66+
Self(vec![5, 12])
67+
}
68+
}
69+
70+
#[derive(Default)]
71+
struct StrDefault<'a>(&'a str);
72+
73+
74+
#[derive(Default)]
75+
struct AlreadyDerived(i32, bool);
76+
77+
macro_rules! mac {
78+
() => {
79+
0
80+
};
81+
($e:expr) => {
82+
struct X(u32);
83+
impl Default for X {
84+
fn default() -> Self {
85+
Self($e)
86+
}
87+
}
88+
};
89+
}
90+
91+
mac!(0);
92+
93+
#[derive(Default)]
94+
struct Y(u32);
95+
96+
struct RustIssue26925<T> {
97+
a: Option<T>,
98+
}
99+
100+
// We should watch out for cases where a manual impl is needed because a
101+
// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925).
102+
// For example, a struct with Option<T> does not require T: Default, but a derive adds
103+
// that type bound anyways. So until #26925 get fixed we should disable lint
104+
// for the following case
105+
impl<T> Default for RustIssue26925<T> {
106+
fn default() -> Self {
107+
Self { a: None }
108+
}
109+
}
110+
111+
struct SpecializedImpl<A, B> {
112+
a: A,
113+
b: B,
114+
}
115+
116+
impl<T: Default> Default for SpecializedImpl<T, T> {
117+
fn default() -> Self {
118+
Self {
119+
a: T::default(),
120+
b: T::default(),
121+
}
122+
}
123+
}
124+
125+
#[derive(Default)]
126+
struct WithoutSelfCurly {
127+
a: bool,
128+
}
129+
130+
131+
#[derive(Default)]
132+
struct WithoutSelfParan(bool);
133+
134+
135+
// https://github.com/rust-lang/rust-clippy/issues/7655
136+
137+
pub struct SpecializedImpl2<T> {
138+
v: Vec<T>,
139+
}
140+
141+
impl Default for SpecializedImpl2<String> {
142+
fn default() -> Self {
143+
Self { v: Vec::new() }
144+
}
145+
}
146+
147+
// https://github.com/rust-lang/rust-clippy/issues/7654
148+
149+
pub struct Color {
150+
pub r: u8,
151+
pub g: u8,
152+
pub b: u8,
153+
}
154+
155+
/// `#000000`
156+
impl Default for Color {
157+
fn default() -> Self {
158+
Color { r: 0, g: 0, b: 0 }
159+
}
160+
}
161+
162+
pub struct Color2 {
163+
pub r: u8,
164+
pub g: u8,
165+
pub b: u8,
166+
}
167+
168+
impl Default for Color2 {
169+
/// `#000000`
170+
fn default() -> Self {
171+
Self { r: 0, g: 0, b: 0 }
172+
}
173+
}
174+
175+
#[derive(Default)]
176+
pub struct RepeatDefault1 {
177+
a: [i8; 32],
178+
}
179+
180+
181+
pub struct RepeatDefault2 {
182+
a: [i8; 33],
183+
}
184+
185+
impl Default for RepeatDefault2 {
186+
fn default() -> Self {
187+
RepeatDefault2 { a: [0; 33] }
188+
}
189+
}
190+
191+
// https://github.com/rust-lang/rust-clippy/issues/7753
192+
193+
pub enum IntOrString {
194+
Int(i32),
195+
String(String),
196+
}
197+
198+
impl Default for IntOrString {
199+
fn default() -> Self {
200+
IntOrString::Int(0)
201+
}
202+
}
203+
204+
#[derive(Default)]
205+
pub enum SimpleEnum {
206+
Foo,
207+
#[default]
208+
Bar,
209+
}
210+
211+
212+
pub enum NonExhaustiveEnum {
213+
Foo,
214+
#[non_exhaustive]
215+
Bar,
216+
}
217+
218+
impl Default for NonExhaustiveEnum {
219+
fn default() -> Self {
220+
NonExhaustiveEnum::Bar
221+
}
222+
}
223+
224+
// https://github.com/rust-lang/rust-clippy/issues/10396
225+
226+
#[derive(Default)]
227+
struct DefaultType;
228+
229+
struct GenericType<T = DefaultType> {
230+
t: T,
231+
}
232+
233+
impl Default for GenericType {
234+
fn default() -> Self {
235+
Self { t: Default::default() }
236+
}
237+
}
238+
239+
struct InnerGenericType<T> {
240+
t: T,
241+
}
242+
243+
impl Default for InnerGenericType<DefaultType> {
244+
fn default() -> Self {
245+
Self { t: Default::default() }
246+
}
247+
}
248+
249+
struct OtherGenericType<T = DefaultType> {
250+
inner: InnerGenericType<T>,
251+
}
252+
253+
impl Default for OtherGenericType {
254+
fn default() -> Self {
255+
Self {
256+
inner: Default::default(),
257+
}
258+
}
259+
}
260+
261+
mod issue10158 {
262+
pub trait T {}
263+
264+
#[derive(Default)]
265+
pub struct S {}
266+
impl T for S {}
267+
268+
pub struct Outer {
269+
pub inner: Box<dyn T>,
270+
}
271+
272+
impl Default for Outer {
273+
fn default() -> Self {
274+
Outer {
275+
// Box::<S>::default() adjusts to Box<dyn T>
276+
inner: Box::<S>::default(),
277+
}
278+
}
279+
}
280+
}
281+
282+
mod issue11368 {
283+
pub struct A {
284+
a: u32,
285+
}
286+
287+
impl Default for A {
288+
#[track_caller]
289+
fn default() -> Self {
290+
Self { a: 0 }
291+
}
292+
}
293+
}
294+
295+
fn main() {}

tests/ui/derivable_impls.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#![allow(dead_code)]
22

3-
//@no-rustfix: need to change the suggestion to a multipart suggestion
4-
53
use std::collections::HashMap;
64

75
struct FooDefault<'a> {

0 commit comments

Comments
 (0)