diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 34dd43cfe84..afca8850ac5 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, }; +use core::mem; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; @@ -170,9 +171,7 @@ pub struct Dereferencing<'tcx> { #[derive(Debug)] struct StateData<'tcx> { - /// Span of the top level expression - span: Span, - hir_id: HirId, + first_expr: &'tcx Expr<'tcx>, adjusted_ty: Ty<'tcx>, } @@ -198,6 +197,7 @@ enum State { }, ExplicitDerefField { name: Symbol, + derefs_manually_drop: bool, }, Reborrow { mutability: Mutability, @@ -242,7 +242,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // Stop processing sub expressions when a macro call is seen if expr.span.from_expansion() { if let Some((state, data)) = self.state.take() { - report(cx, expr, state, data); + report(cx, expr, state, data, cx.typeck_results()); } return; } @@ -251,7 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { - report(cx, expr, state, data); + report(cx, expr, state, data, typeck); } return; }; @@ -272,14 +272,16 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { (Some(use_cx), RefOp::Deref) => { let sub_ty = typeck.expr_ty(sub_expr); if let ExprUseNode::FieldAccess(name) = use_cx.node - && adjusted_ty.ty_adt_def().map_or(true, |adt| !adt.is_union()) + && !use_cx.moved_before_use && !ty_contains_field(sub_ty, name.name) { self.state = Some(( - State::ExplicitDerefField { name: name.name }, + State::ExplicitDerefField { + name: name.name, + derefs_manually_drop: is_manually_drop(sub_ty), + }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty, }, )); @@ -293,8 +295,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.state = Some(( State::ExplicitDeref { mutability: None }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty, }, )); @@ -313,8 +314,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { mutbl, }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty, }, )); @@ -342,8 +342,18 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return()) }); let can_auto_borrow = match use_cx.node { - ExprUseNode::Callee => true, - ExprUseNode::FieldAccess(_) => adjusted_ty.ty_adt_def().map_or(true, |adt| !adt.is_union()), + ExprUseNode::FieldAccess(_) + if !use_cx.moved_before_use && matches!(sub_expr.kind, ExprKind::Field(..)) => + { + // `DerefMut` will not be automatically applied to `ManuallyDrop<_>` + // field expressions when the base type is a union and the parent + // expression is also a field access. + // + // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a + // deref through `ManuallyDrop<_>` will not compile. + !adjust_derefs_manually_drop(use_cx.adjustments, expr_ty) + }, + ExprUseNode::Callee | ExprUseNode::FieldAccess(_) => true, ExprUseNode::MethodArg(hir_id, _, 0) if !use_cx.moved_before_use => { // Check for calls to trait methods where the trait is implemented // on a reference. @@ -357,11 +367,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { .tcx .erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target)) && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() - && let args = cx - .typeck_results() - .node_args_opt(hir_id) - .map(|args| &args[1..]) - .unwrap_or_default() + && let args = + typeck.node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default() && let impl_ty = if cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder().inputs()[0] .is_ref() @@ -436,14 +443,16 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { count: deref_count - required_refs, msg, stability, - for_field_access: match use_cx.node { - ExprUseNode::FieldAccess(name) => Some(name.name), - _ => None, + for_field_access: if let ExprUseNode::FieldAccess(name) = use_cx.node + && !use_cx.moved_before_use + { + Some(name.name) + } else { + None }, }), StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), }, )); @@ -455,8 +464,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.state = Some(( State::Borrow { mutability }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), }, )); @@ -501,13 +509,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => { let adjusted_ty = data.adjusted_ty; let stability = state.stability; - report(cx, expr, State::DerefedBorrow(state), data); + report(cx, expr, State::DerefedBorrow(state), data, typeck); if stability.is_deref_stable() { self.state = Some(( State::Borrow { mutability }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty, }, )); @@ -517,15 +524,18 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let adjusted_ty = data.adjusted_ty; let stability = state.stability; let for_field_access = state.for_field_access; - report(cx, expr, State::DerefedBorrow(state), data); + report(cx, expr, State::DerefedBorrow(state), data, typeck); if let Some(name) = for_field_access - && !ty_contains_field(typeck.expr_ty(sub_expr), name) + && let sub_expr_ty = typeck.expr_ty(sub_expr) + && !ty_contains_field(sub_expr_ty, name) { self.state = Some(( - State::ExplicitDerefField { name }, + State::ExplicitDerefField { + name, + derefs_manually_drop: is_manually_drop(sub_expr_ty), + }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty, }, )); @@ -535,8 +545,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.state = Some(( State::ExplicitDeref { mutability: None }, StateData { - span: parent.span, - hir_id: parent.hir_id, + first_expr: parent, adjusted_ty, }, )); @@ -566,13 +575,28 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => { self.state = state; }, - (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref) - if !ty_contains_field(typeck.expr_ty(sub_expr), name) => + ( + Some(( + State::ExplicitDerefField { + name, + derefs_manually_drop, + }, + data, + )), + RefOp::Deref, + ) if let sub_expr_ty = typeck.expr_ty(sub_expr) + && !ty_contains_field(sub_expr_ty, name) => { - self.state = Some((State::ExplicitDerefField { name }, data)); + self.state = Some(( + State::ExplicitDerefField { + name, + derefs_manually_drop: derefs_manually_drop || is_manually_drop(sub_expr_ty), + }, + data, + )); }, - (Some((state, data)), _) => report(cx, expr, state, data), + (Some((state, data)), _) => report(cx, expr, state, data, typeck), } } @@ -687,6 +711,14 @@ fn try_parse_ref_op<'tcx>( } } +// Checks if the adjustments contains a deref of `ManuallyDrop<_>` +fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool { + adjustments.iter().any(|a| { + let ty = mem::replace(&mut ty, a.target); + matches!(a.kind, Adjust::Deref(Some(ref op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty) + }) +} + // Checks whether the type for a deref call actually changed the type, not just the mutability of // the reference. fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { @@ -896,7 +928,13 @@ fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool { } #[expect(clippy::needless_pass_by_value, clippy::too_many_lines)] -fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData<'tcx>) { +fn report<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + state: State, + data: StateData<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, +) { match state { State::DerefMethod { ty_changed_count, @@ -904,8 +942,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data mutbl, } => { let mut app = Applicability::MachineApplicable; - let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); - let ty = cx.typeck_results().expr_ty(expr); + let (expr_str, _expr_is_macro_call) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); + let ty = typeck.expr_ty(expr); let (_, ref_count) = peel_mid_ty_refs(ty); let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { // a deref call changing &T -> &U requires two deref operators the first time @@ -945,7 +984,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data span_lint_and_sugg( cx, EXPLICIT_DEREF_METHODS, - data.span, + data.first_expr.span, match mutbl { Mutability::Not => "explicit `deref` method call", Mutability::Mut => "explicit `deref_mut` method call", @@ -957,26 +996,34 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data }, State::DerefedBorrow(state) => { let mut app = Applicability::MachineApplicable; - let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); - span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| { - let (precedence, calls_field) = match get_parent_node(cx.tcx, data.hir_id) { - Some(Node::Expr(e)) => match e.kind { - ExprKind::Call(callee, _) if callee.hir_id != data.hir_id => (0, false), - ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))), - _ => (e.precedence().order(), false), - }, - _ => (0, false), - }; - let sugg = if !snip_is_macro - && (calls_field || expr.precedence().order() < precedence) - && !has_enclosing_paren(&snip) - { - format!("({snip})") - } else { - snip.into() - }; - diag.span_suggestion(data.span, "change this to", sugg, app); - }); + let (snip, snip_is_macro) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); + span_lint_hir_and_then( + cx, + NEEDLESS_BORROW, + data.first_expr.hir_id, + data.first_expr.span, + state.msg, + |diag| { + let (precedence, calls_field) = match get_parent_node(cx.tcx, data.first_expr.hir_id) { + Some(Node::Expr(e)) => match e.kind { + ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false), + ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))), + _ => (e.precedence().order(), false), + }, + _ => (0, false), + }; + let sugg = if !snip_is_macro + && (calls_field || expr.precedence().order() < precedence) + && !has_enclosing_paren(&snip) + { + format!("({snip})") + } else { + snip.into() + }; + diag.span_suggestion(data.first_expr.span, "change this to", sugg, app); + }, + ); }, State::ExplicitDeref { mutability } => { if matches!( @@ -994,7 +1041,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data } let (prefix, precedence) = if let Some(mutability) = mutability - && !cx.typeck_results().expr_ty(expr).is_ref() + && !typeck.expr_ty(expr).is_ref() { let prefix = match mutability { Mutability::Not => "&", @@ -1007,53 +1054,61 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data span_lint_hir_and_then( cx, EXPLICIT_AUTO_DEREF, - data.hir_id, - data.span, + data.first_expr.hir_id, + data.first_expr.span, "deref which would be done by auto-deref", |diag| { let mut app = Applicability::MachineApplicable; - let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); + let (snip, snip_is_macro) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let sugg = if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) { format!("{prefix}({snip})") } else { format!("{prefix}{snip}") }; - diag.span_suggestion(data.span, "try", sugg, app); + diag.span_suggestion(data.first_expr.span, "try", sugg, app); }, ); }, - State::ExplicitDerefField { .. } => { - if matches!( - expr.kind, - ExprKind::Block(..) - | ExprKind::ConstBlock(_) - | ExprKind::If(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) - ) && data.adjusted_ty.is_sized(cx.tcx, cx.param_env) - { - // Rustc bug: auto deref doesn't work on block expression when targeting sized types. - return; - } - - if let ExprKind::Field(parent_expr, _) = expr.kind - && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(parent_expr).kind() - && adt.is_union() - { - // Auto deref does not apply on union field - return; - } + State::ExplicitDerefField { + derefs_manually_drop, .. + } => { + let (snip_span, needs_parens) = if matches!(expr.kind, ExprKind::Field(..)) + && (derefs_manually_drop + || adjust_derefs_manually_drop( + typeck.expr_adjustments(data.first_expr), + typeck.expr_ty(data.first_expr), + )) { + // `DerefMut` will not be automatically applied to `ManuallyDrop<_>` + // field expressions when the base type is a union and the parent + // expression is also a field access. + // + // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a + // deref through `ManuallyDrop<_>` will not compile. + let parent_id = cx.tcx.hir().parent_id(expr.hir_id); + if parent_id == data.first_expr.hir_id { + return; + } + (cx.tcx.hir().get(parent_id).expect_expr().span, true) + } else { + (expr.span, false) + }; span_lint_hir_and_then( cx, EXPLICIT_AUTO_DEREF, - data.hir_id, - data.span, + data.first_expr.hir_id, + data.first_expr.span, "deref which would be done by auto-deref", |diag| { let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0; - diag.span_suggestion(data.span, "try", snip.into_owned(), app); + let snip = snippet_with_context(cx, snip_span, data.first_expr.span.ctxt(), "..", &mut app).0; + let sugg = if needs_parens { + format!("({snip})") + } else { + snip.into_owned() + }; + diag.span_suggestion(data.first_expr.span, "try", sugg, app); }, ); }, diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 7eff93881b2..09d7749b2be 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -1266,3 +1266,8 @@ pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx> Err(_) => ty, } } + +/// Checks if the type is `core::mem::ManuallyDrop<_>` +pub fn is_manually_drop(ty: Ty<'_>) -> bool { + ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop) +} diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index 12158d0d12a..e6ca4bb66cc 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -301,24 +301,47 @@ fn main() { }; // Issue #11474 - pub struct Variant { - pub anonymous: Variant0, + #[derive(Clone, Copy)] + struct Wrap(T); + impl core::ops::Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + impl core::ops::DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - pub union Variant0 { - pub anonymous: std::mem::ManuallyDrop, + union U { + u: T, } - pub struct Variant00 { - pub anonymous: Variant000, - } - - pub union Variant000 { - pub val: i32, + #[derive(Clone, Copy)] + struct S8 { + x: &'static str, } unsafe { - let mut p = core::mem::zeroed::(); - (*p.anonymous.anonymous).anonymous.val = 1; + let mut x = U { + u: core::mem::ManuallyDrop::new(S8 { x: "" }), + }; + let _ = &mut (*x.u).x; + let _ = &mut { x.u }.x; + let _ = &mut ({ *x.u }).x; + + let mut x = U { + u: Wrap(core::mem::ManuallyDrop::new(S8 { x: "" })), + }; + let _ = &mut (*x.u).x; + let _ = &mut { x.u }.x; + let _ = &mut ({ **x.u }).x; + + let mut x = U { u: Wrap(S8 { x: "" }) }; + let _ = &mut x.u.x; + let _ = &mut { x.u }.x; + let _ = &mut ({ *x.u }).x; } } diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index dec021c1834..7531e1f87b7 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -301,24 +301,47 @@ fn main() { }; // Issue #11474 - pub struct Variant { - pub anonymous: Variant0, + #[derive(Clone, Copy)] + struct Wrap(T); + impl core::ops::Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + impl core::ops::DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - pub union Variant0 { - pub anonymous: std::mem::ManuallyDrop, + union U { + u: T, } - pub struct Variant00 { - pub anonymous: Variant000, - } - - pub union Variant000 { - pub val: i32, + #[derive(Clone, Copy)] + struct S8 { + x: &'static str, } unsafe { - let mut p = core::mem::zeroed::(); - (*p.anonymous.anonymous).anonymous.val = 1; + let mut x = U { + u: core::mem::ManuallyDrop::new(S8 { x: "" }), + }; + let _ = &mut (*x.u).x; + let _ = &mut (*{ x.u }).x; + let _ = &mut ({ *x.u }).x; + + let mut x = U { + u: Wrap(core::mem::ManuallyDrop::new(S8 { x: "" })), + }; + let _ = &mut (**x.u).x; + let _ = &mut (**{ x.u }).x; + let _ = &mut ({ **x.u }).x; + + let mut x = U { u: Wrap(S8 { x: "" }) }; + let _ = &mut (*x.u).x; + let _ = &mut (*{ x.u }).x; + let _ = &mut ({ *x.u }).x; } } diff --git a/tests/ui/explicit_auto_deref.stderr b/tests/ui/explicit_auto_deref.stderr index 3d2a7b0d9f4..cc9eeeb5042 100644 --- a/tests/ui/explicit_auto_deref.stderr +++ b/tests/ui/explicit_auto_deref.stderr @@ -241,5 +241,35 @@ error: deref which would be done by auto-deref LL | Some(x) => &mut *x, | ^^^^^^^ help: try: `x` -error: aborting due to 40 previous errors +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:332:22 + | +LL | let _ = &mut (*{ x.u }).x; + | ^^^^^^^^^^ help: try: `{ x.u }` + +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:338:22 + | +LL | let _ = &mut (**x.u).x; + | ^^^^^^^ help: try: `(*x.u)` + +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:339:22 + | +LL | let _ = &mut (**{ x.u }).x; + | ^^^^^^^^^^^ help: try: `{ x.u }` + +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:343:22 + | +LL | let _ = &mut (*x.u).x; + | ^^^^^^ help: try: `x.u` + +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:344:22 + | +LL | let _ = &mut (*{ x.u }).x; + | ^^^^^^^^^^ help: try: `{ x.u }` + +error: aborting due to 45 previous errors diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index c2c5f765abf..ff1e2dc8875 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -190,27 +190,48 @@ fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; - union Coral { - crab: ManuallyDrop>, + #[derive(Clone, Copy)] + struct Wrap(T); + impl core::ops::Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + impl core::ops::DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - union Ocean { - coral: ManuallyDrop, + union U { + u: T, } - let mut ocean = Ocean { - coral: ManuallyDrop::new(Coral { - crab: ManuallyDrop::new(vec![1, 2, 3]), - }), - }; + #[derive(Clone, Copy)] + struct Foo { + x: u32, + } unsafe { - ManuallyDrop::drop(&mut (&mut ocean.coral).crab); + let mut x = U { + u: ManuallyDrop::new(Foo { x: 0 }), + }; + let _ = &mut (&mut x.u).x; + let _ = &mut { x.u }.x; + let _ = &mut ({ &mut x.u }).x; - (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); - ManuallyDrop::drop(&mut (*ocean.coral).crab); + let mut x = U { + u: Wrap(ManuallyDrop::new(Foo { x: 0 })), + }; + let _ = &mut (&mut x.u).x; + let _ = &mut { x.u }.x; + let _ = &mut ({ &mut x.u }).x; - ManuallyDrop::drop(&mut ocean.coral); + let mut x = U { u: Wrap(Foo { x: 0 }) }; + let _ = &mut x.u.x; + let _ = &mut { x.u }.x; + let _ = &mut ({ &mut x.u }).x; } } diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 0cd6e41b8a4..597021539ac 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -190,27 +190,48 @@ fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; - union Coral { - crab: ManuallyDrop>, + #[derive(Clone, Copy)] + struct Wrap(T); + impl core::ops::Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + impl core::ops::DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - union Ocean { - coral: ManuallyDrop, + union U { + u: T, } - let mut ocean = Ocean { - coral: ManuallyDrop::new(Coral { - crab: ManuallyDrop::new(vec![1, 2, 3]), - }), - }; + #[derive(Clone, Copy)] + struct Foo { + x: u32, + } unsafe { - ManuallyDrop::drop(&mut (&mut ocean.coral).crab); + let mut x = U { + u: ManuallyDrop::new(Foo { x: 0 }), + }; + let _ = &mut (&mut x.u).x; + let _ = &mut (&mut { x.u }).x; + let _ = &mut ({ &mut x.u }).x; - (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); - ManuallyDrop::drop(&mut (*ocean.coral).crab); + let mut x = U { + u: Wrap(ManuallyDrop::new(Foo { x: 0 })), + }; + let _ = &mut (&mut x.u).x; + let _ = &mut (&mut { x.u }).x; + let _ = &mut ({ &mut x.u }).x; - ManuallyDrop::drop(&mut ocean.coral); + let mut x = U { u: Wrap(Foo { x: 0 }) }; + let _ = &mut (&mut x.u).x; + let _ = &mut (&mut { x.u }).x; + let _ = &mut ({ &mut x.u }).x; } } diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index e91b78b0a15..44552ee6abe 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -133,5 +133,29 @@ error: this expression borrows a value the compiler would automatically borrow LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` -error: aborting due to 22 previous errors +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:221:22 + | +LL | let _ = &mut (&mut { x.u }).x; + | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:228:22 + | +LL | let _ = &mut (&mut { x.u }).x; + | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:232:22 + | +LL | let _ = &mut (&mut x.u).x; + | ^^^^^^^^^^ help: change this to: `x.u` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:233:22 + | +LL | let _ = &mut (&mut { x.u }).x; + | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` + +error: aborting due to 26 previous errors