Skip to content

Commit 803e8be

Browse files
committed
fix [needless_borrow] and [explicit_auto_deref] false positive on unions
1 parent 481dc2e commit 803e8be

File tree

3 files changed

+79
-4
lines changed

3 files changed

+79
-4
lines changed

clippy_lints/src/dereference.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,19 +184,22 @@ impl Dereferencing {
184184
}
185185
}
186186

187+
#[derive(Debug)]
187188
struct StateData {
188189
/// Span of the top level expression
189190
span: Span,
190191
hir_id: HirId,
191192
position: Position,
192193
}
193194

195+
#[derive(Debug)]
194196
struct DerefedBorrow {
195197
count: usize,
196198
msg: &'static str,
197199
snip_expr: Option<HirId>,
198200
}
199201

202+
#[derive(Debug)]
200203
enum State {
201204
// Any number of deref method calls.
202205
DerefMethod {
@@ -276,11 +279,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
276279
(None, kind) => {
277280
let expr_ty = typeck.expr_ty(expr);
278281
let (position, adjustments) = walk_parents(cx, expr, self.msrv);
282+
let is_field_of_union = is_field_of_union(typeck, sub_expr);
279283

280284
match kind {
281285
RefOp::Deref => {
282286
if let Position::FieldAccess(name) = position
283287
&& !ty_contains_field(typeck.expr_ty(sub_expr), name)
288+
&& !is_field_of_union
284289
{
285290
self.state = Some((
286291
State::ExplicitDerefField { name },
@@ -355,7 +360,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
355360
let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
356361
let impl_msg = "the borrowed expression implements the required traits";
357362

358-
let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
363+
let (required_refs, msg, snip_expr) = if position.can_auto_borrow(is_field_of_union) {
359364
(1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
360365
} else if let Position::ImplArg(hir_id) = position {
361366
(0, impl_msg, Some(hir_id))
@@ -572,6 +577,17 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
572577
extract_msrv_attr!(LateContext);
573578
}
574579

580+
// deref needs to be explicit on union fields
581+
fn is_field_of_union<'tcx>(typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>) -> bool {
582+
if let ExprKind::Field(path_expr, _) = expr.kind {
583+
let ty = typeck.expr_ty_adjusted(path_expr);
584+
if let Some(adt_def) = ty.ty_adt_def() {
585+
return adt_def.is_union();
586+
}
587+
}
588+
false
589+
}
590+
575591
fn try_parse_ref_op<'tcx>(
576592
tcx: TyCtxt<'tcx>,
577593
typeck: &'tcx TypeckResults<'_>,
@@ -616,7 +632,7 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
616632
}
617633

618634
/// The position of an expression relative to it's parent.
619-
#[derive(Clone, Copy)]
635+
#[derive(Clone, Copy, Debug)]
620636
enum Position {
621637
MethodReceiver,
622638
/// The method is defined on a reference type. e.g. `impl Foo for &T`
@@ -644,8 +660,9 @@ impl Position {
644660
matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_))
645661
}
646662

647-
fn can_auto_borrow(self) -> bool {
648-
matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
663+
fn can_auto_borrow(self, is_field_of_union: bool) -> bool {
664+
// union fields cannot be auto borrowed
665+
!is_field_of_union && matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
649666
}
650667

651668
fn lint_explicit_deref(self) -> bool {

tests/ui/needless_borrow.fixed

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,32 @@ mod meets_msrv {
298298
let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
299299
}
300300
}
301+
302+
#[allow(unused)]
303+
fn issue9383() {
304+
// Should not lint because unions need explicit deref when accessing field
305+
use std::mem::ManuallyDrop;
306+
307+
union Coral {
308+
crab: ManuallyDrop<Vec<i32>>,
309+
}
310+
311+
union Ocean {
312+
coral: ManuallyDrop<Coral>,
313+
}
314+
315+
let mut ocean = Ocean {
316+
coral: ManuallyDrop::new(Coral {
317+
crab: ManuallyDrop::new(vec![1, 2, 3]),
318+
}),
319+
};
320+
321+
unsafe {
322+
ManuallyDrop::drop(&mut (&mut ocean.coral).crab);
323+
324+
(*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]);
325+
ManuallyDrop::drop(&mut (*ocean.coral).crab);
326+
327+
ManuallyDrop::drop(&mut ocean.coral);
328+
}
329+
}

tests/ui/needless_borrow.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,32 @@ mod meets_msrv {
298298
let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
299299
}
300300
}
301+
302+
#[allow(unused)]
303+
fn issue9383() {
304+
// Should not lint because unions need explicit deref when accessing field
305+
use std::mem::ManuallyDrop;
306+
307+
union Coral {
308+
crab: ManuallyDrop<Vec<i32>>,
309+
}
310+
311+
union Ocean {
312+
coral: ManuallyDrop<Coral>,
313+
}
314+
315+
let mut ocean = Ocean {
316+
coral: ManuallyDrop::new(Coral {
317+
crab: ManuallyDrop::new(vec![1, 2, 3]),
318+
}),
319+
};
320+
321+
unsafe {
322+
ManuallyDrop::drop(&mut (&mut ocean.coral).crab);
323+
324+
(*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]);
325+
ManuallyDrop::drop(&mut (*ocean.coral).crab);
326+
327+
ManuallyDrop::drop(&mut ocean.coral);
328+
}
329+
}

0 commit comments

Comments
 (0)