mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 07:14:28 +00:00
Lint explicit_auto_deref
on most union field accesses.
This commit is contained in:
parent
a68cd88860
commit
1a01132417
@ -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);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user