Skip to content

Commit 0aad34e

Browse files
committed
Normalize projections types when checking explicit_auto_deref
1 parent 5ef3cc8 commit 0aad34e

File tree

4 files changed

+102
-29
lines changed

4 files changed

+102
-29
lines changed

clippy_lints/src/dereference.rs

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exact
33
use clippy_utils::msrvs::{self, Msrv};
44
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
55
use clippy_utils::sugg::has_enclosing_paren;
6-
use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
6+
use clippy_utils::ty::{adt_and_variant_of_res, expr_sig, is_copy, peel_mid_ty_refs, ty_sig};
77
use clippy_utils::{
88
fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage,
99
};
@@ -26,8 +26,8 @@ use rustc_lint::{LateContext, LateLintPass};
2626
use rustc_middle::mir::{Rvalue, StatementKind};
2727
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
2828
use rustc_middle::ty::{
29-
self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
30-
ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
29+
self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamEnv, ParamTy,
30+
PredicateKind, ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
3131
};
3232
use rustc_session::{declare_tool_lint, impl_lint_pass};
3333
use rustc_span::{symbol::sym, Span, Symbol};
@@ -736,7 +736,7 @@ fn walk_parents<'tcx>(
736736
..
737737
}) if span.ctxt() == ctxt => {
738738
let ty = cx.tcx.type_of(owner_id.def_id);
739-
Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx))
739+
Some(ty_auto_deref_stability(cx.tcx, cx.param_env, ty, precedence).position_for_result(cx))
740740
},
741741

742742
Node::Item(&Item {
@@ -760,18 +760,31 @@ fn walk_parents<'tcx>(
760760
let output = cx
761761
.tcx
762762
.erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output());
763-
Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx))
763+
Some(ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx))
764764
},
765765

766766
Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) {
767767
Some(Expr {
768768
hir_id,
769769
kind: ExprKind::Struct(path, ..),
770770
..
771-
}) => variant_of_res(cx, cx.qpath_res(path, *hir_id))
772-
.and_then(|variant| variant.fields.iter().find(|f| f.name == field.ident.name))
773-
.map(|field_def| {
774-
ty_auto_deref_stability(cx, cx.tcx.type_of(field_def.did), precedence).position_for_arg()
771+
}) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
772+
.and_then(|(adt, variant)| {
773+
variant
774+
.fields
775+
.iter()
776+
.find(|f| f.name == field.ident.name)
777+
.map(|f| (adt, f))
778+
})
779+
.map(|(adt, field_def)| {
780+
ty_auto_deref_stability(
781+
cx.tcx,
782+
// Use the param_env of the target type.
783+
cx.tcx.param_env(adt.did()),
784+
cx.tcx.type_of(field_def.did),
785+
precedence,
786+
)
787+
.position_for_arg()
775788
}),
776789
_ => None,
777790
},
@@ -792,7 +805,7 @@ fn walk_parents<'tcx>(
792805
let output = cx
793806
.tcx
794807
.erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output());
795-
ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)
808+
ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx)
796809
},
797810
)
798811
},
@@ -835,15 +848,20 @@ fn walk_parents<'tcx>(
835848
msrv,
836849
)
837850
} else {
838-
ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
839-
.position_for_arg()
851+
ty_auto_deref_stability(
852+
cx.tcx,
853+
// Use the param_env of the target function.
854+
sig.predicates_id().map_or(ParamEnv::empty(), |id| cx.tcx.param_env(id)),
855+
cx.tcx.erase_late_bound_regions(ty),
856+
precedence
857+
).position_for_arg()
840858
}
841859
},
842860
}
843861
})
844862
}),
845863
ExprKind::MethodCall(method, receiver, args, _) => {
846-
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
864+
let fn_id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
847865
if receiver.hir_id == child_id {
848866
// Check for calls to trait methods where the trait is implemented on a reference.
849867
// Two cases need to be handled:
@@ -852,13 +870,17 @@ fn walk_parents<'tcx>(
852870
// priority.
853871
if e.hir_id != child_id {
854872
return Some(Position::ReborrowStable(precedence))
855-
} else if let Some(trait_id) = cx.tcx.trait_of_item(id)
873+
} else if let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
856874
&& let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
857875
&& let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
858876
&& let subs = cx
859877
.typeck_results()
860878
.node_substs_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default()
861-
&& let impl_ty = if cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[0].is_ref() {
879+
&& let impl_ty = if cx.tcx.fn_sig(fn_id)
880+
.subst_identity()
881+
.skip_binder()
882+
.inputs()[0].is_ref()
883+
{
862884
// Trait methods taking `&self`
863885
sub_ty
864886
} else {
@@ -879,10 +901,13 @@ fn walk_parents<'tcx>(
879901
return Some(Position::MethodReceiver);
880902
}
881903
args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
882-
let ty = cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i + 1];
904+
let ty = cx.tcx.fn_sig(fn_id).subst_identity().input(i + 1);
883905
// `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739
884906
// `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782
885-
if e.hir_id == child_id && method.args.is_none() && let ty::Param(param_ty) = ty.kind() {
907+
if e.hir_id == child_id
908+
&& method.args.is_none()
909+
&& let ty::Param(param_ty) = ty.skip_binder().kind()
910+
{
886911
needless_borrow_impl_arg_position(
887912
cx,
888913
possible_borrowers,
@@ -895,8 +920,10 @@ fn walk_parents<'tcx>(
895920
)
896921
} else {
897922
ty_auto_deref_stability(
898-
cx,
899-
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).subst_identity().input(i + 1)),
923+
cx.tcx,
924+
// Use the param_env of the target function.
925+
cx.tcx.param_env(fn_id),
926+
cx.tcx.erase_late_bound_regions(ty),
900927
precedence,
901928
)
902929
.position_for_arg()
@@ -1378,11 +1405,18 @@ impl<'tcx> TyPosition<'tcx> {
13781405
}
13791406

13801407
// Checks whether a type is stable when switching to auto dereferencing,
1381-
fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedence: i8) -> TyPosition<'tcx> {
1408+
fn ty_auto_deref_stability<'tcx>(
1409+
tcx: TyCtxt<'tcx>,
1410+
param_env: ParamEnv<'tcx>,
1411+
ty: Ty<'tcx>,
1412+
precedence: i8,
1413+
) -> TyPosition<'tcx> {
13821414
let ty::Ref(_, mut ty, _) = *ty.kind() else {
13831415
return Position::Other(precedence).into();
13841416
};
13851417

1418+
ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty);
1419+
13861420
loop {
13871421
break match *ty.kind() {
13881422
ty::Ref(_, ref_ty, _) => {
@@ -1423,9 +1457,7 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc
14231457
| ty::Closure(..)
14241458
| ty::Never
14251459
| ty::Tuple(_)
1426-
| ty::Alias(ty::Projection, _) => {
1427-
Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds())).into()
1428-
},
1460+
| ty::Alias(ty::Projection, _) => Position::DerefStable(precedence, ty.is_sized(tcx, param_env)).into(),
14291461
};
14301462
}
14311463
}

clippy_utils/src/ty.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -894,16 +894,29 @@ impl AdtVariantInfo {
894894
}
895895

896896
/// Gets the struct or enum variant from the given `Res`
897-
pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> {
897+
pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<(AdtDef<'tcx>, &'tcx VariantDef)> {
898898
match res {
899-
Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()),
900-
Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)),
901-
Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant()),
899+
Res::Def(DefKind::Struct, id) => {
900+
let adt = cx.tcx.adt_def(id);
901+
Some((adt, adt.non_enum_variant()))
902+
},
903+
Res::Def(DefKind::Variant, id) => {
904+
let adt = cx.tcx.adt_def(cx.tcx.parent(id));
905+
Some((adt, adt.variant_with_id(id)))
906+
},
907+
Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => {
908+
let adt = cx.tcx.adt_def(cx.tcx.parent(id));
909+
Some((adt, adt.non_enum_variant()))
910+
},
902911
Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
903912
let var_id = cx.tcx.parent(id);
904-
Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id))
913+
let adt = cx.tcx.adt_def(cx.tcx.parent(var_id));
914+
Some((adt, adt.variant_with_id(var_id)))
915+
},
916+
Res::SelfCtor(id) => {
917+
let adt = cx.tcx.type_of(id).ty_adt_def().unwrap();
918+
Some((adt, adt.non_enum_variant()))
905919
},
906-
Res::SelfCtor(id) => Some(cx.tcx.type_of(id).ty_adt_def().unwrap().non_enum_variant()),
907920
_ => None,
908921
}
909922
}

tests/ui/explicit_auto_deref.fixed

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ fn main() {
269269

270270
trait WithAssoc {
271271
type Assoc: ?Sized;
272+
fn to_assoc(&self) -> &Self::Assoc {
273+
panic!()
274+
}
272275
}
273276
impl WithAssoc for String {
274277
type Assoc = str;
@@ -281,4 +284,15 @@ fn main() {
281284
// Issue #9901
282285
fn takes_ref(_: &i32) {}
283286
takes_ref(*Box::new(&0i32));
287+
288+
// Issue #10384
289+
impl<'a> WithAssoc for &'a u32 {
290+
type Assoc = dyn core::fmt::Display;
291+
fn to_assoc(&self) -> &Self::Assoc {
292+
*self
293+
}
294+
}
295+
fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc {
296+
*x
297+
}
284298
}

tests/ui/explicit_auto_deref.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ fn main() {
269269

270270
trait WithAssoc {
271271
type Assoc: ?Sized;
272+
fn to_assoc(&self) -> &Self::Assoc {
273+
panic!()
274+
}
272275
}
273276
impl WithAssoc for String {
274277
type Assoc = str;
@@ -281,4 +284,15 @@ fn main() {
281284
// Issue #9901
282285
fn takes_ref(_: &i32) {}
283286
takes_ref(*Box::new(&0i32));
287+
288+
// Issue #10384
289+
impl<'a> WithAssoc for &'a u32 {
290+
type Assoc = dyn core::fmt::Display;
291+
fn to_assoc(&self) -> &Self::Assoc {
292+
*self
293+
}
294+
}
295+
fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc {
296+
*x
297+
}
284298
}

0 commit comments

Comments
 (0)