Skip to content

Commit 5ebede0

Browse files
committed
Fix full projection identifier + move applicability to MaybeIncorrect
1 parent 1176b8e commit 5ebede0

7 files changed

+228
-18
lines changed

clippy_lints/src/methods/search_is_some.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ fn get_closure_suggestion<'tcx>(cx: &LateContext<'_>, search_expr: &'tcx hir::Ex
185185
closure_arg_is_double_ref,
186186
next_pos: search_expr.span.lo(),
187187
suggestion_start: String::new(),
188-
applicability: Applicability::MachineApplicable,
188+
applicability: Applicability::MaybeIncorrect,
189189
};
190190

191191
let fn_def_id = cx.tcx.hir().local_def_id(search_expr.hir_id);
@@ -252,11 +252,15 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
252252
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
253253
if let PlaceBase::Local(id) = cmt.place.base {
254254
let map = self.cx.tcx.hir();
255-
let ident_str = map.name(id).to_string();
256255
let span = map.span(cmt.hir_id);
257256
let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None);
258257
let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
259258

259+
// identifier referring to the variable currently triggered (i.e.: `fp`)
260+
let ident_str = map.name(id).to_string();
261+
// full identifier that includes projection (i.e.: `fp.field`)
262+
let ident_str_with_proj = snippet(self.cx, span, "..").to_string();
263+
260264
if cmt.place.projections.is_empty() {
261265
// handle item without any projection, that needs an explicit borrowing
262266
// i.e.: suggest `&x` instead of `x`
@@ -276,7 +280,8 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
276280
// given expression is the self argument and will be handled completely by the compiler
277281
// i.e.: `|x| x.is_something()`
278282
ExprKind::MethodCall(_, _, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => {
279-
self.suggestion_start.push_str(&format!("{}{}", start_snip, ident_str));
283+
self.suggestion_start
284+
.push_str(&format!("{}{}", start_snip, ident_str_with_proj));
280285
self.next_pos = span.hi();
281286
return;
282287
},
@@ -291,13 +296,26 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
291296
let takes_arg_by_double_ref =
292297
self.func_takes_arg_by_double_ref(parent_expr, cmt.hir_id);
293298

299+
// compiler will automatically dereference field projection, so no need
300+
// to suggest ampersand, but full identifier that includes projection is required
301+
let has_field_projection = cmt
302+
.place
303+
.projections
304+
.iter()
305+
.any(|proj| matches!(proj.kind, ProjectionKind::Field(..)));
306+
294307
// no need to bind again if the function doesn't take arg by double ref
295308
// and if the item is already a double ref
296309
let ident_sugg = if !call_args.is_empty()
297310
&& !takes_arg_by_double_ref
298-
&& self.closure_arg_is_double_ref
311+
&& (self.closure_arg_is_double_ref || has_field_projection)
299312
{
300-
format!("{}{}", start_snip, ident_str)
313+
let ident = if has_field_projection {
314+
ident_str_with_proj
315+
} else {
316+
ident_str
317+
};
318+
format!("{}{}", start_snip, ident)
301319
} else {
302320
format!("{}&{}", start_snip, ident_str)
303321
};
@@ -318,17 +336,9 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
318336
match proj.kind {
319337
// Field projection like `|v| v.foo`
320338
// no adjustment needed here, as field projections are handled by the compiler
321-
ProjectionKind::Field(idx, variant) => match cmt.place.ty_before_projection(i).kind() {
322-
ty::Adt(def, ..) => {
323-
replacement_str = format!(
324-
"{}.{}",
325-
replacement_str,
326-
def.variants[variant].fields[idx as usize].ident.name.as_str()
327-
);
328-
projections_handled = true;
329-
},
330-
ty::Tuple(_) => {
331-
replacement_str = format!("{}.{}", replacement_str, idx);
339+
ProjectionKind::Field(..) => match cmt.place.ty_before_projection(i).kind() {
340+
ty::Adt(..) | ty::Tuple(_) => {
341+
replacement_str = ident_str_with_proj.clone();
332342
projections_handled = true;
333343
},
334344
_ => (),

tests/ui/search_is_some_fixable_none.fixed

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,35 @@ mod issue7392 {
182182
let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
183183
let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
184184
}
185+
186+
fn test_string_1(s: &str) -> bool {
187+
s.is_empty()
188+
}
189+
190+
fn test_u32_1(s: &u32) -> bool {
191+
s.is_power_of_two()
192+
}
193+
194+
fn test_u32_2(s: u32) -> bool {
195+
s.is_power_of_two()
196+
}
197+
198+
fn projection_in_args_test() {
199+
// Index projections
200+
let lst = &[String::from("Hello"), String::from("world")];
201+
let v: Vec<&[String]> = vec![lst];
202+
let _ = !v.iter().any(|s| s[0].is_empty());
203+
let _ = !v.iter().any(|s| test_string_1(&s[0]));
204+
205+
// Field projections
206+
struct FieldProjection<'a> {
207+
field: &'a u32,
208+
}
209+
let field = 123456789;
210+
let instance = FieldProjection { field: &field };
211+
let v = vec![instance];
212+
let _ = !v.iter().any(|fp| fp.field.is_power_of_two());
213+
let _ = !v.iter().any(|fp| test_u32_1(fp.field));
214+
let _ = !v.iter().any(|fp| test_u32_2(*fp.field));
215+
}
185216
}

tests/ui/search_is_some_fixable_none.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,35 @@ mod issue7392 {
188188
let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
189189
let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
190190
}
191+
192+
fn test_string_1(s: &String) -> bool {
193+
s.is_empty()
194+
}
195+
196+
fn test_u32_1(s: &u32) -> bool {
197+
s.is_power_of_two()
198+
}
199+
200+
fn test_u32_2(s: u32) -> bool {
201+
s.is_power_of_two()
202+
}
203+
204+
fn projection_in_args_test() {
205+
// Index projections
206+
let lst = &[String::from("Hello"), String::from("world")];
207+
let v: Vec<&[String]> = vec![lst];
208+
let _ = v.iter().find(|s| s[0].is_empty()).is_none();
209+
let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
210+
211+
// Field projections
212+
struct FieldProjection<'a> {
213+
field: &'a u32,
214+
}
215+
let field = 123456789;
216+
let instance = FieldProjection { field: &field };
217+
let v = vec![instance];
218+
let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
219+
let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
220+
let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
221+
}
191222
}

tests/ui/search_is_some_fixable_none.stderr

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,5 +251,43 @@ error: called `is_none()` after searching an `Iterator` with `find`
251251
LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
252252
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
253253

254-
error: aborting due to 38 previous errors
254+
error: writing `&String` instead of `&str` involves a new object where a slice will do
255+
--> $DIR/search_is_some_fixable_none.rs:192:25
256+
|
257+
LL | fn test_string_1(s: &String) -> bool {
258+
| ^^^^^^^ help: change this to: `&str`
259+
|
260+
= note: `-D clippy::ptr-arg` implied by `-D warnings`
261+
262+
error: called `is_none()` after searching an `Iterator` with `find`
263+
--> $DIR/search_is_some_fixable_none.rs:208:17
264+
|
265+
LL | let _ = v.iter().find(|s| s[0].is_empty()).is_none();
266+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| s[0].is_empty())`
267+
268+
error: called `is_none()` after searching an `Iterator` with `find`
269+
--> $DIR/search_is_some_fixable_none.rs:209:17
270+
|
271+
LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
272+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| test_string_1(&s[0]))`
273+
274+
error: called `is_none()` after searching an `Iterator` with `find`
275+
--> $DIR/search_is_some_fixable_none.rs:218:17
276+
|
277+
LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
278+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| fp.field.is_power_of_two())`
279+
280+
error: called `is_none()` after searching an `Iterator` with `find`
281+
--> $DIR/search_is_some_fixable_none.rs:219:17
282+
|
283+
LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
284+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_1(fp.field))`
285+
286+
error: called `is_none()` after searching an `Iterator` with `find`
287+
--> $DIR/search_is_some_fixable_none.rs:220:17
288+
|
289+
LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
290+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))`
291+
292+
error: aborting due to 44 previous errors
255293

tests/ui/search_is_some_fixable_some.fixed

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,35 @@ mod issue7392 {
184184
let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
185185
let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
186186
}
187+
188+
fn test_string_1(s: &str) -> bool {
189+
s.is_empty()
190+
}
191+
192+
fn test_u32_1(s: &u32) -> bool {
193+
s.is_power_of_two()
194+
}
195+
196+
fn test_u32_2(s: u32) -> bool {
197+
s.is_power_of_two()
198+
}
199+
200+
fn projection_in_args_test() {
201+
// Index projections
202+
let lst = &[String::from("Hello"), String::from("world")];
203+
let v: Vec<&[String]> = vec![lst];
204+
let _ = v.iter().any(|s| s[0].is_empty());
205+
let _ = v.iter().any(|s| test_string_1(&s[0]));
206+
207+
// Field projections
208+
struct FieldProjection<'a> {
209+
field: &'a u32,
210+
}
211+
let field = 123456789;
212+
let instance = FieldProjection { field: &field };
213+
let v = vec![instance];
214+
let _ = v.iter().any(|fp| fp.field.is_power_of_two());
215+
let _ = v.iter().any(|fp| test_u32_1(fp.field));
216+
let _ = v.iter().any(|fp| test_u32_2(*fp.field));
217+
}
187218
}

tests/ui/search_is_some_fixable_some.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,35 @@ mod issue7392 {
187187
let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
188188
let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
189189
}
190+
191+
fn test_string_1(s: &String) -> bool {
192+
s.is_empty()
193+
}
194+
195+
fn test_u32_1(s: &u32) -> bool {
196+
s.is_power_of_two()
197+
}
198+
199+
fn test_u32_2(s: u32) -> bool {
200+
s.is_power_of_two()
201+
}
202+
203+
fn projection_in_args_test() {
204+
// Index projections
205+
let lst = &[String::from("Hello"), String::from("world")];
206+
let v: Vec<&[String]> = vec![lst];
207+
let _ = v.iter().find(|s| s[0].is_empty()).is_some();
208+
let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
209+
210+
// Field projections
211+
struct FieldProjection<'a> {
212+
field: &'a u32,
213+
}
214+
let field = 123456789;
215+
let instance = FieldProjection { field: &field };
216+
let v = vec![instance];
217+
let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
218+
let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
219+
let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
220+
}
190221
}

tests/ui/search_is_some_fixable_some.stderr

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,5 +234,43 @@ error: called `is_some()` after searching an `Iterator` with `find`
234234
LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
235235
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
236236

237-
error: aborting due to 38 previous errors
237+
error: writing `&String` instead of `&str` involves a new object where a slice will do
238+
--> $DIR/search_is_some_fixable_some.rs:191:25
239+
|
240+
LL | fn test_string_1(s: &String) -> bool {
241+
| ^^^^^^^ help: change this to: `&str`
242+
|
243+
= note: `-D clippy::ptr-arg` implied by `-D warnings`
244+
245+
error: called `is_some()` after searching an `Iterator` with `find`
246+
--> $DIR/search_is_some_fixable_some.rs:207:26
247+
|
248+
LL | let _ = v.iter().find(|s| s[0].is_empty()).is_some();
249+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s[0].is_empty())`
250+
251+
error: called `is_some()` after searching an `Iterator` with `find`
252+
--> $DIR/search_is_some_fixable_some.rs:208:26
253+
|
254+
LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
255+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| test_string_1(&s[0]))`
256+
257+
error: called `is_some()` after searching an `Iterator` with `find`
258+
--> $DIR/search_is_some_fixable_some.rs:217:26
259+
|
260+
LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
261+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| fp.field.is_power_of_two())`
262+
263+
error: called `is_some()` after searching an `Iterator` with `find`
264+
--> $DIR/search_is_some_fixable_some.rs:218:26
265+
|
266+
LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
267+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_1(fp.field))`
268+
269+
error: called `is_some()` after searching an `Iterator` with `find`
270+
--> $DIR/search_is_some_fixable_some.rs:219:26
271+
|
272+
LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
273+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))`
274+
275+
error: aborting due to 44 previous errors
238276

0 commit comments

Comments
 (0)