Skip to content

Commit 487f1b0

Browse files
committed
don't suggest autofixes on composite types with fields of uncertain types
1 parent 2147bb9 commit 487f1b0

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
@@ -275,7 +275,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
275275
&& !stmt.span.in_external_macro(cx.sess().source_map())
276276
&& let ctxt = stmt.span.ctxt()
277277
&& expr.span.ctxt() == ctxt
278-
&& let Some(reduced) = reduce_expression(cx, expr)
278+
&& let Some((reduced, applicability)) = reduce_expression(cx, expr)
279279
&& reduced.iter().all(|e| e.span.ctxt() == ctxt)
280280
{
281281
if let ExprKind::Index(..) = &expr.kind {
@@ -318,48 +318,72 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
318318
stmt.span,
319319
"unnecessary operation",
320320
|diag| {
321-
diag.span_suggestion(
322-
stmt.span,
323-
"statement can be reduced to",
324-
snippet,
325-
Applicability::MachineApplicable,
326-
);
321+
diag.span_suggestion(stmt.span, "statement can be reduced to", snippet, applicability);
327322
},
328323
);
329324
}
330325
}
331326
}
332327

333-
fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec<&'a Expr<'a>>> {
328+
fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(Vec<&'a Expr<'a>>, Applicability)> {
334329
if expr.span.from_expansion() {
335330
return None;
336331
}
337332
match expr.kind {
338-
ExprKind::Index(a, b, _) => Some(vec![a, b]),
333+
ExprKind::Index(a, b, _) => Some((vec![a, b], Applicability::MachineApplicable)),
339334
ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
340-
Some(vec![a, b])
335+
Some((vec![a, b], Applicability::MachineApplicable))
341336
},
342-
ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()),
337+
ExprKind::Array(elems) => {
338+
let applicability = if elems.iter().all(|elem| expr_type_is_certain(cx, elem)) {
339+
Applicability::MachineApplicable
340+
} else {
341+
// there's a risk that, if we take the elem exprs out of the context of the array,
342+
// their types might become ambiguous
343+
Applicability::MaybeIncorrect
344+
};
345+
Some((elems.iter().collect(), applicability))
346+
},
347+
ExprKind::Tup(v) => Some((v.iter().collect(), Applicability::MachineApplicable)),
343348
ExprKind::Repeat(inner, _)
344349
| ExprKind::Type(inner, _)
345350
| ExprKind::Unary(_, inner)
346351
| ExprKind::Field(inner, _)
347-
| ExprKind::AddrOf(_, _, inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
352+
| ExprKind::AddrOf(_, _, inner) => {
353+
reduce_expression(cx, inner).or_else(|| Some((vec![inner], Applicability::MachineApplicable)))
354+
},
348355
ExprKind::Cast(inner, _) if expr_type_is_certain(cx, inner) => {
349-
reduce_expression(cx, inner).or_else(|| Some(vec![inner]))
356+
reduce_expression(cx, inner).or_else(|| Some((vec![inner], Applicability::MachineApplicable)))
350357
},
351358
ExprKind::Struct(_, fields, ref base) => {
359+
let applicability = if fields.iter().all(|f| expr_type_is_certain(cx, f.expr)) {
360+
Applicability::MachineApplicable
361+
} else {
362+
// there's a risk that, if we take the field exprs out of the context of the struct constructor,
363+
// their types might become ambiguous
364+
Applicability::MaybeIncorrect
365+
};
352366
if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
353367
None
354368
} else {
355369
let base = match base {
356370
StructTailExpr::Base(base) => Some(base),
357371
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
358372
};
359-
Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
373+
Some((
374+
fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect(),
375+
applicability,
376+
))
360377
}
361378
},
362379
ExprKind::Call(callee, args) => {
380+
let applicability = if args.iter().all(|a| expr_type_is_certain(cx, a)) {
381+
Applicability::MachineApplicable
382+
} else {
383+
// there's a risk that, if we take the args out of the context of the
384+
// call/constructor, their types might become ambiguous
385+
Applicability::MaybeIncorrect
386+
};
363387
if let ExprKind::Path(ref qpath) = callee.kind {
364388
if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() {
365389
// type-dependent function call like `impl FnOnce for X`
@@ -370,7 +394,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
370394
Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
371395
if !has_drop(cx, cx.typeck_results().expr_ty(expr)) =>
372396
{
373-
Some(args.iter().collect())
397+
Some((args.iter().collect(), applicability))
374398
},
375399
_ => None,
376400
}
@@ -383,7 +407,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
383407
block.expr.as_ref().and_then(|e| {
384408
match block.rules {
385409
BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
386-
BlockCheckMode::DefaultBlock => Some(vec![&**e]),
410+
BlockCheckMode::DefaultBlock => Some((vec![&**e], Applicability::MachineApplicable)),
387411
// in case of compiler-inserted signaling blocks
388412
BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e),
389413
}
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 6 previous errors
35+

0 commit comments

Comments
 (0)