Skip to content

Commit fe28c1b

Browse files
committed
don't suggest autofixes on composite types with fields of uncertain types
1 parent e629869 commit fe28c1b

File tree

3 files changed

+109
-16
lines changed

3 files changed

+109
-16
lines changed

clippy_lints/src/no_effect.rs

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
274274
&& !stmt.span.in_external_macro(cx.sess().source_map())
275275
&& let ctxt = stmt.span.ctxt()
276276
&& expr.range_span().unwrap_or(expr.span).ctxt() == ctxt
277-
&& let Some(reduced) = reduce_expression(cx, expr)
277+
&& let Some((reduced, applicability)) = reduce_expression(cx, expr)
278278
&& reduced.iter().all(|e| e.span.ctxt() == ctxt)
279279
{
280280
if let ExprKind::Index(..) = &expr.kind {
@@ -317,48 +317,72 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
317317
stmt.span,
318318
"unnecessary operation",
319319
|diag| {
320-
diag.span_suggestion(
321-
stmt.span,
322-
"statement can be reduced to",
323-
snippet,
324-
Applicability::MachineApplicable,
325-
);
320+
diag.span_suggestion(stmt.span, "statement can be reduced to", snippet, applicability);
326321
},
327322
);
328323
}
329324
}
330325
}
331326

332-
fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec<&'a Expr<'a>>> {
327+
fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(Vec<&'a Expr<'a>>, Applicability)> {
333328
if expr.range_span().unwrap_or(expr.span).from_expansion() {
334329
return None;
335330
}
336331
match expr.kind {
337-
ExprKind::Index(a, b, _) => Some(vec![a, b]),
332+
ExprKind::Index(a, b, _) => Some((vec![a, b], Applicability::MachineApplicable)),
338333
ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
339-
Some(vec![a, b])
334+
Some((vec![a, b], Applicability::MachineApplicable))
340335
},
341-
ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()),
336+
ExprKind::Array(elems) => {
337+
let applicability = if elems.iter().all(|elem| expr_type_is_certain(cx, elem)) {
338+
Applicability::MachineApplicable
339+
} else {
340+
// there's a risk that, if we take the elem exprs out of the context of the array,
341+
// their types might become ambiguous
342+
Applicability::MaybeIncorrect
343+
};
344+
Some((elems.iter().collect(), applicability))
345+
},
346+
ExprKind::Tup(v) => Some((v.iter().collect(), Applicability::MachineApplicable)),
342347
ExprKind::Repeat(inner, _)
343348
| ExprKind::Type(inner, _)
344349
| ExprKind::Unary(_, inner)
345350
| ExprKind::Field(inner, _)
346-
| ExprKind::AddrOf(_, _, inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
351+
| ExprKind::AddrOf(_, _, inner) => {
352+
reduce_expression(cx, inner).or_else(|| Some((vec![inner], Applicability::MachineApplicable)))
353+
},
347354
ExprKind::Cast(inner, _) if expr_type_is_certain(cx, inner) => {
348-
reduce_expression(cx, inner).or_else(|| Some(vec![inner]))
355+
reduce_expression(cx, inner).or_else(|| Some((vec![inner], Applicability::MachineApplicable)))
349356
},
350357
ExprKind::Struct(_, fields, ref base) => {
358+
let applicability = if fields.iter().all(|f| expr_type_is_certain(cx, f.expr)) {
359+
Applicability::MachineApplicable
360+
} else {
361+
// there's a risk that, if we take the field exprs out of the context of the struct constructor,
362+
// their types might become ambiguous
363+
Applicability::MaybeIncorrect
364+
};
351365
if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
352366
None
353367
} else {
354368
let base = match base {
355369
StructTailExpr::Base(base) => Some(base),
356370
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
357371
};
358-
Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
372+
Some((
373+
fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect(),
374+
applicability,
375+
))
359376
}
360377
},
361378
ExprKind::Call(callee, args) => {
379+
let applicability = if args.iter().all(|a| expr_type_is_certain(cx, a)) {
380+
Applicability::MachineApplicable
381+
} else {
382+
// there's a risk that, if we take the args out of the context of the
383+
// call/constructor, their types might become ambiguous
384+
Applicability::MaybeIncorrect
385+
};
362386
if let ExprKind::Path(ref qpath) = callee.kind {
363387
if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() {
364388
// type-dependent function call like `impl FnOnce for X`
@@ -369,7 +393,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
369393
Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
370394
if !has_drop(cx, cx.typeck_results().expr_ty(expr)) =>
371395
{
372-
Some(args.iter().collect())
396+
Some((args.iter().collect(), applicability))
373397
},
374398
_ => None,
375399
}
@@ -382,7 +406,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
382406
block.expr.as_ref().and_then(|e| {
383407
match block.rules {
384408
BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
385-
BlockCheckMode::DefaultBlock => Some(vec![&**e]),
409+
BlockCheckMode::DefaultBlock => Some((vec![&**e], Applicability::MachineApplicable)),
386410
// in case of compiler-inserted signaling blocks
387411
BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e),
388412
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//@no-rustfix
2+
#![expect(unused)]
3+
#![warn(clippy::unnecessary_operation)]
4+
5+
// don't lint if any of the fields has an ambiguous type when used by themselves
6+
fn issue15381_original() {
7+
struct DescriptorSet {
8+
slots: Vec<u32>,
9+
}
10+
11+
// the repro
12+
DescriptorSet { slots: Vec::new() };
13+
//~^ unnecessary_operation
14+
}
15+
16+
fn issue15381() {
17+
enum E {
18+
Foo { f: Vec<u32> },
19+
Bar(Vec<u32>),
20+
}
21+
E::Foo { f: Vec::new() };
22+
//~^ unnecessary_operation
23+
E::Bar(Vec::new());
24+
//~^ unnecessary_operation
25+
26+
struct Tuple(Vec<u32>);
27+
Tuple(Vec::new());
28+
//~^ unnecessary_operation
29+
30+
// the type of the second slice gets inferred based on it needing to be the same to that of the
31+
// first one, but that doesn't happen when they're outside the array
32+
[[1, 2, 3].as_slice(), [].as_slice()];
33+
//~^ unnecessary_operation
34+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: unnecessary operation
2+
--> tests/ui/unnecessary_operation_unfixable.rs:12:5
3+
|
4+
LL | DescriptorSet { slots: Vec::new() };
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();`
6+
|
7+
= note: `-D clippy::unnecessary-operation` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_operation)]`
9+
10+
error: unnecessary operation
11+
--> tests/ui/unnecessary_operation_unfixable.rs:21:5
12+
|
13+
LL | E::Foo { f: Vec::new() };
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();`
15+
16+
error: unnecessary operation
17+
--> tests/ui/unnecessary_operation_unfixable.rs:23:5
18+
|
19+
LL | E::Bar(Vec::new());
20+
| ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();`
21+
22+
error: unnecessary operation
23+
--> tests/ui/unnecessary_operation_unfixable.rs:27:5
24+
|
25+
LL | Tuple(Vec::new());
26+
| ^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();`
27+
28+
error: unnecessary operation
29+
--> tests/ui/unnecessary_operation_unfixable.rs:32:5
30+
|
31+
LL | [[1, 2, 3].as_slice(), [].as_slice()];
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `[1, 2, 3].as_slice(); [].as_slice();`
33+
34+
error: aborting due to 5 previous errors
35+

0 commit comments

Comments
 (0)