Lint explicit_auto_deref on most union field accesses.

This commit is contained in:
Jason Newcomb 2023-09-15 01:48:57 -04:00
parent a68cd88860
commit 1a01132417
5 changed files with 250 additions and 131 deletions

View File

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; 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::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren; 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::{ use clippy_utils::{
expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
}; };
@ -171,9 +171,7 @@ pub struct Dereferencing<'tcx> {
#[derive(Debug)] #[derive(Debug)]
struct StateData<'tcx> { struct StateData<'tcx> {
/// Span of the top level expression first_expr: &'tcx Expr<'tcx>,
span: Span,
hir_id: HirId,
adjusted_ty: Ty<'tcx>, adjusted_ty: Ty<'tcx>,
} }
@ -199,6 +197,7 @@ enum State {
}, },
ExplicitDerefField { ExplicitDerefField {
name: Symbol, name: Symbol,
derefs_manually_drop: bool,
}, },
Reborrow { Reborrow {
mutability: Mutability, mutability: Mutability,
@ -243,7 +242,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
// Stop processing sub expressions when a macro call is seen // Stop processing sub expressions when a macro call is seen
if expr.span.from_expansion() { if expr.span.from_expansion() {
if let Some((state, data)) = self.state.take() { if let Some((state, data)) = self.state.take() {
report(cx, expr, state, data); report(cx, expr, state, data, cx.typeck_results());
} }
return; return;
} }
@ -252,7 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else {
// The whole chain of reference operations has been seen // The whole chain of reference operations has been seen
if let Some((state, data)) = self.state.take() { if let Some((state, data)) = self.state.take() {
report(cx, expr, state, data); report(cx, expr, state, data, typeck);
} }
return; return;
}; };
@ -273,14 +272,16 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
(Some(use_cx), RefOp::Deref) => { (Some(use_cx), RefOp::Deref) => {
let sub_ty = typeck.expr_ty(sub_expr); let sub_ty = typeck.expr_ty(sub_expr);
if let ExprUseNode::FieldAccess(name) = use_cx.node 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) && !ty_contains_field(sub_ty, name.name)
{ {
self.state = Some(( self.state = Some((
State::ExplicitDerefField { name: name.name }, State::ExplicitDerefField {
name: name.name,
derefs_manually_drop: is_manually_drop(sub_ty),
},
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -294,8 +295,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
self.state = Some(( self.state = Some((
State::ExplicitDeref { mutability: None }, State::ExplicitDeref { mutability: None },
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -314,8 +314,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
mutbl, mutbl,
}, },
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -346,19 +345,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
ExprUseNode::FieldAccess(_) ExprUseNode::FieldAccess(_)
if !use_cx.moved_before_use && matches!(sub_expr.kind, ExprKind::Field(..)) => if !use_cx.moved_before_use && matches!(sub_expr.kind, ExprKind::Field(..)) =>
{ {
// `ManuallyDrop` fields of a union will not automatically call // `DerefMut` will not be automatically applied to `ManuallyDrop<_>`
// `deref_mut` when accessing a field. // field expressions when the base type is a union and the parent
// Note: if the `ManuallyDrop` value is not directly a field of a // expression is also a field access.
// union then `DerefMut` will work as normal.
// //
// This means we need to not modify an expression such as `(&mut x.y).z` // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a
// if accessing `z` would require `DerefMut`. // deref through `ManuallyDrop<_>` will not compile.
let mut ty = expr_ty; !adjust_derefs_manually_drop(use_cx.adjustments, expr_ty)
!use_cx.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)
&& ty.ty_adt_def().map_or(false, |def| def.is_manually_drop())
})
}, },
ExprUseNode::Callee | ExprUseNode::FieldAccess(_) => true, ExprUseNode::Callee | ExprUseNode::FieldAccess(_) => true,
ExprUseNode::MethodArg(hir_id, _, 0) if !use_cx.moved_before_use => { ExprUseNode::MethodArg(hir_id, _, 0) if !use_cx.moved_before_use => {
@ -374,11 +367,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
.tcx .tcx
.erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target)) .erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target))
&& let ty::Ref(_, sub_ty, _) = *arg_ty.kind() && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
&& let args = cx && let args =
.typeck_results() typeck.node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default()
.node_args_opt(hir_id)
.map(|args| &args[1..])
.unwrap_or_default()
&& let impl_ty = && let impl_ty =
if cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder().inputs()[0] if cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder().inputs()[0]
.is_ref() .is_ref()
@ -453,14 +443,16 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
count: deref_count - required_refs, count: deref_count - required_refs,
msg, msg,
stability, stability,
for_field_access: match use_cx.node { for_field_access: if let ExprUseNode::FieldAccess(name) = use_cx.node
ExprUseNode::FieldAccess(name) => Some(name.name), && !use_cx.moved_before_use
_ => None, {
Some(name.name)
} else {
None
}, },
}), }),
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target),
}, },
)); ));
@ -472,8 +464,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
self.state = Some(( self.state = Some((
State::Borrow { mutability }, State::Borrow { mutability },
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target),
}, },
)); ));
@ -518,13 +509,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
(Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => { (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => {
let adjusted_ty = data.adjusted_ty; let adjusted_ty = data.adjusted_ty;
let stability = state.stability; let stability = state.stability;
report(cx, expr, State::DerefedBorrow(state), data); report(cx, expr, State::DerefedBorrow(state), data, typeck);
if stability.is_deref_stable() { if stability.is_deref_stable() {
self.state = Some(( self.state = Some((
State::Borrow { mutability }, State::Borrow { mutability },
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -534,15 +524,18 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
let adjusted_ty = data.adjusted_ty; let adjusted_ty = data.adjusted_ty;
let stability = state.stability; let stability = state.stability;
let for_field_access = state.for_field_access; 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 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(( self.state = Some((
State::ExplicitDerefField { name }, State::ExplicitDerefField {
name,
derefs_manually_drop: is_manually_drop(sub_expr_ty),
},
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -552,8 +545,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
self.state = Some(( self.state = Some((
State::ExplicitDeref { mutability: None }, State::ExplicitDeref { mutability: None },
StateData { StateData {
span: parent.span, first_expr: parent,
hir_id: parent.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -583,13 +575,28 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
(state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => { (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
self.state = state; 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),
} }
} }
@ -706,6 +713,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 // Checks whether the type for a deref call actually changed the type, not just the mutability of
// the reference. // the reference.
fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
@ -915,7 +930,13 @@ fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
} }
#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)] #[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 { match state {
State::DerefMethod { State::DerefMethod {
ty_changed_count, ty_changed_count,
@ -923,8 +944,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
mutbl, mutbl,
} => { } => {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); let (expr_str, _expr_is_macro_call) =
let ty = cx.typeck_results().expr_ty(expr); 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 (_, ref_count) = peel_mid_ty_refs(ty);
let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { 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 // a deref call changing &T -> &U requires two deref operators the first time
@ -964,7 +986,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
EXPLICIT_DEREF_METHODS, EXPLICIT_DEREF_METHODS,
data.span, data.first_expr.span,
match mutbl { match mutbl {
Mutability::Not => "explicit `deref` method call", Mutability::Not => "explicit `deref` method call",
Mutability::Mut => "explicit `deref_mut` method call", Mutability::Mut => "explicit `deref_mut` method call",
@ -976,26 +998,34 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
}, },
State::DerefedBorrow(state) => { State::DerefedBorrow(state) => {
let mut app = Applicability::MachineApplicable; 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) =
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| { snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
let (precedence, calls_field) = match get_parent_node(cx.tcx, data.hir_id) { span_lint_hir_and_then(
Some(Node::Expr(e)) => match e.kind { cx,
ExprKind::Call(callee, _) if callee.hir_id != data.hir_id => (0, false), NEEDLESS_BORROW,
ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))), data.first_expr.hir_id,
_ => (e.precedence().order(), false), data.first_expr.span,
}, state.msg,
_ => (0, false), |diag| {
}; let (precedence, calls_field) = match get_parent_node(cx.tcx, data.first_expr.hir_id) {
let sugg = if !snip_is_macro Some(Node::Expr(e)) => match e.kind {
&& (calls_field || expr.precedence().order() < precedence) ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false),
&& !has_enclosing_paren(&snip) ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))),
{ _ => (e.precedence().order(), false),
format!("({snip})") },
} else { _ => (0, false),
snip.into() };
}; let sugg = if !snip_is_macro
diag.span_suggestion(data.span, "change this to", sugg, app); && (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 } => { State::ExplicitDeref { mutability } => {
if matches!( if matches!(
@ -1013,7 +1043,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
} }
let (prefix, precedence) = if let Some(mutability) = mutability 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 { let prefix = match mutability {
Mutability::Not => "&", Mutability::Not => "&",
@ -1026,53 +1056,61 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
span_lint_hir_and_then( span_lint_hir_and_then(
cx, cx,
EXPLICIT_AUTO_DEREF, EXPLICIT_AUTO_DEREF,
data.hir_id, data.first_expr.hir_id,
data.span, data.first_expr.span,
"deref which would be done by auto-deref", "deref which would be done by auto-deref",
|diag| { |diag| {
let mut app = Applicability::MachineApplicable; 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 = let sugg =
if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) { if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
format!("{prefix}({snip})") format!("{prefix}({snip})")
} else { } else {
format!("{prefix}{snip}") format!("{prefix}{snip}")
}; };
diag.span_suggestion(data.span, "try", sugg, app); diag.span_suggestion(data.first_expr.span, "try", sugg, app);
}, },
); );
}, },
State::ExplicitDerefField { .. } => { State::ExplicitDerefField {
if matches!( derefs_manually_drop, ..
expr.kind, } => {
ExprKind::Block(..) let (snip_span, needs_parens) = if matches!(expr.kind, ExprKind::Field(..))
| ExprKind::ConstBlock(_) && (derefs_manually_drop
| ExprKind::If(..) || adjust_derefs_manually_drop(
| ExprKind::Loop(..) typeck.expr_adjustments(data.first_expr),
| ExprKind::Match(..) typeck.expr_ty(data.first_expr),
) && data.adjusted_ty.is_sized(cx.tcx, cx.param_env) )) {
{ // `DerefMut` will not be automatically applied to `ManuallyDrop<_>`
// Rustc bug: auto deref doesn't work on block expression when targeting sized types. // field expressions when the base type is a union and the parent
return; // expression is also a field access.
} //
// e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a
if let ExprKind::Field(parent_expr, _) = expr.kind // deref through `ManuallyDrop<_>` will not compile.
&& let ty::Adt(adt, _) = cx.typeck_results().expr_ty(parent_expr).kind() let parent_id = cx.tcx.hir().parent_id(expr.hir_id);
&& adt.is_union() if parent_id == data.first_expr.hir_id {
{ return;
// Auto deref does not apply on union field }
return; (cx.tcx.hir().get(parent_id).expect_expr().span, true)
} } else {
(expr.span, false)
};
span_lint_hir_and_then( span_lint_hir_and_then(
cx, cx,
EXPLICIT_AUTO_DEREF, EXPLICIT_AUTO_DEREF,
data.hir_id, data.first_expr.hir_id,
data.span, data.first_expr.span,
"deref which would be done by auto-deref", "deref which would be done by auto-deref",
|diag| { |diag| {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0; let snip = snippet_with_context(cx, snip_span, data.first_expr.span.ctxt(), "..", &mut app).0;
diag.span_suggestion(data.span, "try", snip.into_owned(), app); let sugg = if needs_parens {
format!("({snip})")
} else {
snip.into_owned()
};
diag.span_suggestion(data.first_expr.span, "try", sugg, app);
}, },
); );
}, },

View File

@ -1266,3 +1266,8 @@ pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>
Err(_) => ty, 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)
}

View File

@ -301,24 +301,47 @@ fn main() {
}; };
// Issue #11474 // Issue #11474
pub struct Variant { #[derive(Clone, Copy)]
pub anonymous: Variant0, struct Wrap<T>(T);
impl<T> core::ops::Deref for Wrap<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> core::ops::DerefMut for Wrap<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
} }
pub union Variant0 { union U<T: Copy> {
pub anonymous: std::mem::ManuallyDrop<Variant00>, u: T,
} }
pub struct Variant00 { #[derive(Clone, Copy)]
pub anonymous: Variant000, struct S8 {
} x: &'static str,
pub union Variant000 {
pub val: i32,
} }
unsafe { unsafe {
let mut p = core::mem::zeroed::<Variant>(); let mut x = U {
(*p.anonymous.anonymous).anonymous.val = 1; 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;
} }
} }

View File

@ -301,24 +301,47 @@ fn main() {
}; };
// Issue #11474 // Issue #11474
pub struct Variant { #[derive(Clone, Copy)]
pub anonymous: Variant0, struct Wrap<T>(T);
impl<T> core::ops::Deref for Wrap<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> core::ops::DerefMut for Wrap<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
} }
pub union Variant0 { union U<T: Copy> {
pub anonymous: std::mem::ManuallyDrop<Variant00>, u: T,
} }
pub struct Variant00 { #[derive(Clone, Copy)]
pub anonymous: Variant000, struct S8 {
} x: &'static str,
pub union Variant000 {
pub val: i32,
} }
unsafe { unsafe {
let mut p = core::mem::zeroed::<Variant>(); let mut x = U {
(*p.anonymous.anonymous).anonymous.val = 1; 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;
} }
} }

View File

@ -241,5 +241,35 @@ error: deref which would be done by auto-deref
LL | Some(x) => &mut *x, LL | Some(x) => &mut *x,
| ^^^^^^^ help: try: `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