From a2502794fbd124fb761ad9e3e9614bfbe85527a7 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 14:51:49 +0200 Subject: [PATCH 1/7] add failing test --- tests/ui/unnecessary_operation.rs | 9 +++++++++ tests/ui/unnecessary_operation.stderr | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index a3e6c6288ada..3400347c36a1 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -163,3 +163,12 @@ fn issue15173_original(handler: impl FnOnce() -> MsU + Clone + 'static) { None }) as Box Option>; } + +// don't lint if any of the fields has an ambiguous type when used by themselves +fn issue15381() { + struct DescriptorSet { + slots: Vec, + } + + DescriptorSet { slots: Vec::new() }; +} diff --git a/tests/ui/unnecessary_operation.stderr b/tests/ui/unnecessary_operation.stderr index 3439ba2e33e5..2f0d4b56550f 100644 --- a/tests/ui/unnecessary_operation.stderr +++ b/tests/ui/unnecessary_operation.stderr @@ -127,5 +127,11 @@ error: unnecessary operation LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` -error: aborting due to 20 previous errors +error: unnecessary operation + --> tests/ui/unnecessary_operation.rs:173:5 + | +LL | DescriptorSet { slots: Vec::new() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();` + +error: aborting due to 21 previous errors From ae7061dbf424aec9a3cf7c689c74fba7ff9dd95d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 14:39:52 +0200 Subject: [PATCH 2/7] don't allow fields whose types are uncertain --- clippy_lints/src/no_effect.rs | 4 +++- tests/ui/unnecessary_operation.fixed | 12 ++++++++++++ tests/ui/unnecessary_operation.stderr | 23 +---------------------- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 0d6666eed455..4afdbe1aff03 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -349,7 +349,9 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option { - if has_drop(cx, cx.typeck_results().expr_ty(expr)) { + if fields.iter().any(|f| !expr_type_is_certain(cx, &f.expr)) + || has_drop(cx, cx.typeck_results().expr_ty(expr)) + { None } else { let base = match base { diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index db5409bc491e..bf50291b1af7 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -157,3 +157,15 @@ fn issue15173_original(handler: impl FnOnce() -> MsU + Clone + 'static) { None }) as Box Option>; } + +fn issue15381() { + struct Resources; + struct DescriptorSet { + slots: Vec, + } + + fn main() { + Vec::new(); + //~^ unnecessary_operation + } +} diff --git a/tests/ui/unnecessary_operation.stderr b/tests/ui/unnecessary_operation.stderr index 2f0d4b56550f..1e29291ef652 100644 --- a/tests/ui/unnecessary_operation.stderr +++ b/tests/ui/unnecessary_operation.stderr @@ -67,12 +67,6 @@ error: unnecessary operation LL | ..get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:93:5 - | -LL | 5..get_number(); - | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5; get_number();` - error: unnecessary operation --> tests/ui/unnecessary_operation.rs:95:5 | @@ -112,26 +106,11 @@ LL | | get_number() LL | | }; | |______^ help: statement can be reduced to: `get_number();` -error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:109:5 - | -LL | / FooString { -LL | | -LL | | s: String::from("blah"), -LL | | }; - | |______^ help: statement can be reduced to: `String::from("blah");` - error: unnecessary operation --> tests/ui/unnecessary_operation.rs:150:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` -error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:173:5 - | -LL | DescriptorSet { slots: Vec::new() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();` - -error: aborting due to 21 previous errors +error: aborting due to 18 previous errors From cfd36ecc1c9c38f5e581f5a4967424b88b86ba46 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 14:51:27 +0200 Subject: [PATCH 3/7] special-case ranges --- clippy_lints/src/no_effect.rs | 18 ++++++++++++++++++ tests/ui/unnecessary_operation.stderr | 8 +++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 4afdbe1aff03..296052c59855 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::higher::Range; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{expr_type_is_certain, has_drop}; use clippy_utils::{ @@ -348,6 +349,23 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option { reduce_expression(cx, inner).or_else(|| Some(vec![inner])) }, + // In the normal `Struct` case, we bail out if any of the fields has an uncertain type. + // But for two-sided ranges, we know that if the type of one of the sides is certain, then so is the other + // one's. So we only check that, more relaxed pre-condition. + // + // Note that that condition true in general for any struct with a generic present in two fields, but + // generalizing the check to those would be cumbersome. + ExprKind::Struct(..) + if let Some(range) = Range::hir(expr) + && let Some(start) = range.start + && let Some(end) = range.end => + { + if [start, end].into_iter().any(|e| expr_type_is_certain(cx, e)) { + Some(vec![start, end]) + } else { + None + } + }, ExprKind::Struct(_, fields, ref base) => { if fields.iter().any(|f| !expr_type_is_certain(cx, &f.expr)) || has_drop(cx, cx.typeck_results().expr_ty(expr)) diff --git a/tests/ui/unnecessary_operation.stderr b/tests/ui/unnecessary_operation.stderr index 1e29291ef652..cfb26b830260 100644 --- a/tests/ui/unnecessary_operation.stderr +++ b/tests/ui/unnecessary_operation.stderr @@ -67,6 +67,12 @@ error: unnecessary operation LL | ..get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` +error: unnecessary operation + --> tests/ui/unnecessary_operation.rs:93:5 + | +LL | 5..get_number(); + | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5; get_number();` + error: unnecessary operation --> tests/ui/unnecessary_operation.rs:95:5 | @@ -112,5 +118,5 @@ error: unnecessary operation LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors From 5bc99b7f621f7ca0ddbbd2fdae028e9a9545b351 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 23:09:33 +0200 Subject: [PATCH 4/7] reduce `applicability` when finding exprs with uncertain types this makes the test case `unfixable`, so move it to a separate file --- clippy_lints/src/no_effect.rs | 31 ++++++++++--------- tests/ui/unnecessary_operation.fixed | 12 ------- tests/ui/unnecessary_operation.rs | 9 ------ tests/ui/unnecessary_operation.stderr | 11 ++++++- tests/ui/unnecessary_operation_unfixable.rs | 13 ++++++++ .../ui/unnecessary_operation_unfixable.stderr | 11 +++++++ 6 files changed, 51 insertions(+), 36 deletions(-) create mode 100644 tests/ui/unnecessary_operation_unfixable.rs create mode 100644 tests/ui/unnecessary_operation_unfixable.stderr diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 296052c59855..c50a9b547d79 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -272,11 +272,12 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { + let mut applicability = Applicability::MachineApplicable; if let StmtKind::Semi(expr) = stmt.kind && !stmt.span.in_external_macro(cx.sess().source_map()) && let ctxt = stmt.span.ctxt() && expr.span.ctxt() == ctxt - && let Some(reduced) = reduce_expression(cx, expr) + && let Some(reduced) = reduce_expression(cx, expr, &mut applicability) && reduced.iter().all(|e| e.span.ctxt() == ctxt) { if let ExprKind::Index(..) = &expr.kind { @@ -319,19 +320,18 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { stmt.span, "unnecessary operation", |diag| { - diag.span_suggestion( - stmt.span, - "statement can be reduced to", - snippet, - Applicability::MachineApplicable, - ); + diag.span_suggestion(stmt.span, "statement can be reduced to", snippet, applicability); }, ); } } } -fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option>> { +fn reduce_expression<'a>( + cx: &LateContext<'_>, + expr: &'a Expr<'a>, + applicability: &mut Applicability, +) -> Option>> { if expr.span.from_expansion() { return None; } @@ -345,9 +345,9 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option reduce_expression(cx, inner).or_else(|| Some(vec![inner])), + | ExprKind::AddrOf(_, _, inner) => reduce_expression(cx, inner, applicability).or_else(|| Some(vec![inner])), ExprKind::Cast(inner, _) if expr_type_is_certain(cx, inner) => { - reduce_expression(cx, inner).or_else(|| Some(vec![inner])) + reduce_expression(cx, inner, applicability).or_else(|| Some(vec![inner])) }, // In the normal `Struct` case, we bail out if any of the fields has an uncertain type. // But for two-sided ranges, we know that if the type of one of the sides is certain, then so is the other @@ -367,9 +367,12 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option { - if fields.iter().any(|f| !expr_type_is_certain(cx, &f.expr)) - || has_drop(cx, cx.typeck_results().expr_ty(expr)) - { + if fields.iter().any(|f| !expr_type_is_certain(cx, f.expr)) { + // there's a risk that if we take the field exprs out of the context of the struct constructor, + // their types might become ambiguous + *applicability = Applicability::MaybeIncorrect; + } + if has_drop(cx, cx.typeck_results().expr_ty(expr)) { None } else { let base = match base { @@ -405,7 +408,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option None, BlockCheckMode::DefaultBlock => Some(vec![&**e]), // in case of compiler-inserted signaling blocks - BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e), + BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e, applicability), } }) } else { diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index bf50291b1af7..db5409bc491e 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -157,15 +157,3 @@ fn issue15173_original(handler: impl FnOnce() -> MsU + Clone + 'static) { None }) as Box Option>; } - -fn issue15381() { - struct Resources; - struct DescriptorSet { - slots: Vec, - } - - fn main() { - Vec::new(); - //~^ unnecessary_operation - } -} diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index 3400347c36a1..a3e6c6288ada 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -163,12 +163,3 @@ fn issue15173_original(handler: impl FnOnce() -> MsU + Clone + 'static) { None }) as Box Option>; } - -// don't lint if any of the fields has an ambiguous type when used by themselves -fn issue15381() { - struct DescriptorSet { - slots: Vec, - } - - DescriptorSet { slots: Vec::new() }; -} diff --git a/tests/ui/unnecessary_operation.stderr b/tests/ui/unnecessary_operation.stderr index cfb26b830260..3439ba2e33e5 100644 --- a/tests/ui/unnecessary_operation.stderr +++ b/tests/ui/unnecessary_operation.stderr @@ -112,11 +112,20 @@ LL | | get_number() LL | | }; | |______^ help: statement can be reduced to: `get_number();` +error: unnecessary operation + --> tests/ui/unnecessary_operation.rs:109:5 + | +LL | / FooString { +LL | | +LL | | s: String::from("blah"), +LL | | }; + | |______^ help: statement can be reduced to: `String::from("blah");` + error: unnecessary operation --> tests/ui/unnecessary_operation.rs:150:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors diff --git a/tests/ui/unnecessary_operation_unfixable.rs b/tests/ui/unnecessary_operation_unfixable.rs new file mode 100644 index 000000000000..ce9723846642 --- /dev/null +++ b/tests/ui/unnecessary_operation_unfixable.rs @@ -0,0 +1,13 @@ +//@no-rustfix +#![expect(unused)] +#![warn(clippy::unnecessary_operation)] + +// don't lint if any of the fields has an ambiguous type when used by themselves +fn issue15381() { + struct DescriptorSet { + slots: Vec, + } + + DescriptorSet { slots: Vec::new() }; + //~^ unnecessary_operation +} diff --git a/tests/ui/unnecessary_operation_unfixable.stderr b/tests/ui/unnecessary_operation_unfixable.stderr new file mode 100644 index 000000000000..2d748f166649 --- /dev/null +++ b/tests/ui/unnecessary_operation_unfixable.stderr @@ -0,0 +1,11 @@ +error: unnecessary operation + --> tests/ui/unnecessary_operation_unfixable.rs:11:5 + | +LL | DescriptorSet { slots: Vec::new() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();` + | + = note: `-D clippy::unnecessary-operation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_operation)]` + +error: aborting due to 1 previous error + From 3a052f34e199a2838af4b870d8a0317a6065ab92 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 23:24:45 +0200 Subject: [PATCH 5/7] do the same for `Call`s --- clippy_lints/src/no_effect.rs | 5 +++++ tests/ui/unnecessary_operation_unfixable.rs | 15 +++++++++++++ .../ui/unnecessary_operation_unfixable.stderr | 22 +++++++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index c50a9b547d79..4cd9998d0683 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -383,6 +383,11 @@ fn reduce_expression<'a>( } }, ExprKind::Call(callee, args) => { + if args.iter().any(|a| !expr_type_is_certain(cx, a)) { + // there's a risk that if we take the args out of the context of the + // call/constructor, their types might become ambiguous + *applicability = Applicability::MaybeIncorrect; + } if let ExprKind::Path(ref qpath) = callee.kind { if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() { // type-dependent function call like `impl FnOnce for X` diff --git a/tests/ui/unnecessary_operation_unfixable.rs b/tests/ui/unnecessary_operation_unfixable.rs index ce9723846642..74541d426c02 100644 --- a/tests/ui/unnecessary_operation_unfixable.rs +++ b/tests/ui/unnecessary_operation_unfixable.rs @@ -8,6 +8,21 @@ fn issue15381() { slots: Vec, } + // the repro DescriptorSet { slots: Vec::new() }; //~^ unnecessary_operation + + // other cases + enum E { + Foo { f: Vec }, + Bar(Vec), + } + E::Foo { f: Vec::new() }; + //~^ unnecessary_operation + E::Bar(Vec::new()); + //~^ unnecessary_operation + + struct Tuple(Vec); + Tuple(Vec::new()); + //~^ unnecessary_operation } diff --git a/tests/ui/unnecessary_operation_unfixable.stderr b/tests/ui/unnecessary_operation_unfixable.stderr index 2d748f166649..59a9948e0c09 100644 --- a/tests/ui/unnecessary_operation_unfixable.stderr +++ b/tests/ui/unnecessary_operation_unfixable.stderr @@ -1,5 +1,5 @@ error: unnecessary operation - --> tests/ui/unnecessary_operation_unfixable.rs:11:5 + --> tests/ui/unnecessary_operation_unfixable.rs:12:5 | LL | DescriptorSet { slots: Vec::new() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();` @@ -7,5 +7,23 @@ LL | DescriptorSet { slots: Vec::new() }; = note: `-D clippy::unnecessary-operation` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_operation)]` -error: aborting due to 1 previous error +error: unnecessary operation + --> tests/ui/unnecessary_operation_unfixable.rs:20:5 + | +LL | E::Foo { f: Vec::new() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();` + +error: unnecessary operation + --> tests/ui/unnecessary_operation_unfixable.rs:22:5 + | +LL | E::Bar(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();` + +error: unnecessary operation + --> tests/ui/unnecessary_operation_unfixable.rs:26:5 + | +LL | Tuple(Vec::new()); + | ^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();` + +error: aborting due to 4 previous errors From b013db905001a3282470fc5171104448d906b08c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 23:25:14 +0200 Subject: [PATCH 6/7] do the same for `Array`s --- clippy_lints/src/no_effect.rs | 10 +++++++++- tests/ui/unnecessary_operation_unfixable.rs | 5 +++++ tests/ui/unnecessary_operation_unfixable.stderr | 8 +++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 4cd9998d0683..591b8ffc0379 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -340,7 +340,15 @@ fn reduce_expression<'a>( ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => { Some(vec![a, b]) }, - ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()), + ExprKind::Array(elems) => { + if elems.iter().any(|elem| !expr_type_is_certain(cx, elem)) { + // there's a risk that if we take the elem exprs out of the context of the array, + // their types might become ambiguous + *applicability = Applicability::MaybeIncorrect; + } + Some(elems.iter().collect()) + }, + ExprKind::Tup(v) => Some(v.iter().collect()), ExprKind::Repeat(inner, _) | ExprKind::Type(inner, _) | ExprKind::Unary(_, inner) diff --git a/tests/ui/unnecessary_operation_unfixable.rs b/tests/ui/unnecessary_operation_unfixable.rs index 74541d426c02..6ded3a593eee 100644 --- a/tests/ui/unnecessary_operation_unfixable.rs +++ b/tests/ui/unnecessary_operation_unfixable.rs @@ -25,4 +25,9 @@ fn issue15381() { struct Tuple(Vec); Tuple(Vec::new()); //~^ unnecessary_operation + + // the type of the second slice gets inferred based on it needing to be the same to that of the + // first one, but that doesn't happen when they're outside the array + [[1, 2, 3].as_slice(), [].as_slice()]; + //~^ unnecessary_operation } diff --git a/tests/ui/unnecessary_operation_unfixable.stderr b/tests/ui/unnecessary_operation_unfixable.stderr index 59a9948e0c09..c6513297ec73 100644 --- a/tests/ui/unnecessary_operation_unfixable.stderr +++ b/tests/ui/unnecessary_operation_unfixable.stderr @@ -25,5 +25,11 @@ error: unnecessary operation LL | Tuple(Vec::new()); | ^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `Vec::new();` -error: aborting due to 4 previous errors +error: unnecessary operation + --> tests/ui/unnecessary_operation_unfixable.rs:31:5 + | +LL | [[1, 2, 3].as_slice(), [].as_slice()]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `[1, 2, 3].as_slice(); [].as_slice();` + +error: aborting due to 5 previous errors From e783aea36fc4139a794d3a9b8187decb2de82121 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 22 Aug 2025 15:49:55 +0200 Subject: [PATCH 7/7] if range has a field with uncertain type, only reduce applicability --- clippy_lints/src/no_effect.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 591b8ffc0379..457d98c79fea 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -368,11 +368,12 @@ fn reduce_expression<'a>( && let Some(start) = range.start && let Some(end) = range.end => { - if [start, end].into_iter().any(|e| expr_type_is_certain(cx, e)) { - Some(vec![start, end]) - } else { - None + if ![start, end].into_iter().any(|e| expr_type_is_certain(cx, e)) { + // there's a risk that if we take the field exprs out of the context of the range constructor, + // their types might become ambiguous + *applicability = Applicability::MaybeIncorrect; } + Some(vec![start, end]) }, ExprKind::Struct(_, fields, ref base) => { if fields.iter().any(|f| !expr_type_is_certain(cx, f.expr)) {