Skip to content

Commit c08c57e

Browse files
authored
Rollup merge of #104742 - WaffleLapkin:forbidden-SUPER-deref, r=compiler-errors
Make `deref_into_dyn_supertrait` lint the impl and not the usage Proposed by ``@compiler-errors`` in #89460 (comment) r? ``@crlf0710``
2 parents b43c2e7 + 20f3de5 commit c08c57e

File tree

11 files changed

+146
-95
lines changed

11 files changed

+146
-95
lines changed

compiler/rustc_lint/src/context.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1245,6 +1245,23 @@ impl<'tcx> LateContext<'tcx> {
12451245

12461246
AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap()
12471247
}
1248+
1249+
/// Returns the associated type `name` for `self_ty` as an implementation of `trait_id`.
1250+
/// Do not invoke without first verifying that the type implements the trait.
1251+
pub fn get_associated_type(
1252+
&self,
1253+
self_ty: Ty<'tcx>,
1254+
trait_id: DefId,
1255+
name: &str,
1256+
) -> Option<Ty<'tcx>> {
1257+
let tcx = self.tcx;
1258+
tcx.associated_items(trait_id)
1259+
.find_by_name_and_kind(tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
1260+
.and_then(|assoc| {
1261+
let proj = tcx.mk_projection(assoc.def_id, tcx.mk_substs_trait(self_ty, []));
1262+
tcx.try_normalize_erasing_regions(self.param_env, proj).ok()
1263+
})
1264+
}
12481265
}
12491266

12501267
impl<'tcx> abi::HasDataLayout for LateContext<'tcx> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use crate::{LateContext, LateLintPass, LintContext};
2+
3+
use rustc_errors::DelayDm;
4+
use rustc_hir as hir;
5+
use rustc_middle::{traits::util::supertraits, ty};
6+
use rustc_span::sym;
7+
8+
declare_lint! {
9+
/// The `deref_into_dyn_supertrait` lint is output whenever there is a use of the
10+
/// `Deref` implementation with a `dyn SuperTrait` type as `Output`.
11+
///
12+
/// These implementations will become shadowed when the `trait_upcasting` feature is stabilized.
13+
/// The `deref` functions will no longer be called implicitly, so there might be behavior change.
14+
///
15+
/// ### Example
16+
///
17+
/// ```rust,compile_fail
18+
/// #![deny(deref_into_dyn_supertrait)]
19+
/// #![allow(dead_code)]
20+
///
21+
/// use core::ops::Deref;
22+
///
23+
/// trait A {}
24+
/// trait B: A {}
25+
/// impl<'a> Deref for dyn 'a + B {
26+
/// type Target = dyn A;
27+
/// fn deref(&self) -> &Self::Target {
28+
/// todo!()
29+
/// }
30+
/// }
31+
///
32+
/// fn take_a(_: &dyn A) { }
33+
///
34+
/// fn take_b(b: &dyn B) {
35+
/// take_a(b);
36+
/// }
37+
/// ```
38+
///
39+
/// {{produces}}
40+
///
41+
/// ### Explanation
42+
///
43+
/// The dyn upcasting coercion feature adds new coercion rules, taking priority
44+
/// over certain other coercion rules, which will cause some behavior change.
45+
pub DEREF_INTO_DYN_SUPERTRAIT,
46+
Warn,
47+
"`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future",
48+
@future_incompatible = FutureIncompatibleInfo {
49+
reference: "issue #89460 <https://github.com/rust-lang/rust/issues/89460>",
50+
};
51+
}
52+
53+
declare_lint_pass!(DerefIntoDynSupertrait => [DEREF_INTO_DYN_SUPERTRAIT]);
54+
55+
impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait {
56+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
57+
// `Deref` is being implemented for `t`
58+
if let hir::ItemKind::Impl(impl_) = item.kind
59+
&& let Some(trait_) = &impl_.of_trait
60+
&& let t = cx.tcx.type_of(item.owner_id)
61+
&& let opt_did @ Some(did) = trait_.trait_def_id()
62+
&& opt_did == cx.tcx.lang_items().deref_trait()
63+
// `t` is `dyn t_principal`
64+
&& let ty::Dynamic(data, _, ty::Dyn) = t.kind()
65+
&& let Some(t_principal) = data.principal()
66+
// `<T as Deref>::Target` is `dyn target_principal`
67+
&& let Some(target) = cx.get_associated_type(t, did, "Target")
68+
&& let ty::Dynamic(data, _, ty::Dyn) = target.kind()
69+
&& let Some(target_principal) = data.principal()
70+
// `target_principal` is a supertrait of `t_principal`
71+
&& supertraits(cx.tcx, t_principal.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self))
72+
.any(|sup| sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(cx.tcx, x)) == target_principal)
73+
{
74+
cx.struct_span_lint(
75+
DEREF_INTO_DYN_SUPERTRAIT,
76+
cx.tcx.def_span(item.owner_id.def_id),
77+
DelayDm(|| {
78+
format!(
79+
"`{t}` implements `Deref` with supertrait `{target_principal}` as target"
80+
)
81+
}),
82+
|lint| {
83+
if let Some(target_span) = impl_.items.iter().find_map(|i| (i.ident.name == sym::Target).then_some(i.span)) {
84+
lint.span_label(target_span, "target type is set here");
85+
}
86+
87+
lint
88+
},
89+
)
90+
}
91+
}
92+
}

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ extern crate tracing;
4949
mod array_into_iter;
5050
pub mod builtin;
5151
mod context;
52+
mod deref_into_dyn_supertrait;
5253
mod early;
5354
mod enum_intrinsics_non_enums;
5455
mod errors;
@@ -87,6 +88,7 @@ use rustc_span::Span;
8788

8889
use array_into_iter::ArrayIntoIter;
8990
use builtin::*;
91+
use deref_into_dyn_supertrait::*;
9092
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
9193
use for_loops_over_fallibles::*;
9294
use hidden_unicode_codepoints::*;
@@ -192,6 +194,7 @@ macro_rules! late_lint_mod_passes {
192194
$args,
193195
[
194196
ForLoopsOverFallibles: ForLoopsOverFallibles,
197+
DerefIntoDynSupertrait: DerefIntoDynSupertrait,
195198
HardwiredLints: HardwiredLints,
196199
ImproperCTypesDeclarations: ImproperCTypesDeclarations,
197200
ImproperCTypesDefinitions: ImproperCTypesDefinitions,

compiler/rustc_lint_defs/src/builtin.rs

-46
Original file line numberDiff line numberDiff line change
@@ -3262,7 +3262,6 @@ declare_lint_pass! {
32623262
UNUSED_TUPLE_STRUCT_FIELDS,
32633263
NON_EXHAUSTIVE_OMITTED_PATTERNS,
32643264
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
3265-
DEREF_INTO_DYN_SUPERTRAIT,
32663265
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
32673266
DUPLICATE_MACRO_ATTRIBUTES,
32683267
SUSPICIOUS_AUTO_TRAIT_IMPLS,
@@ -3764,51 +3763,6 @@ declare_lint! {
37643763
"invisible directionality-changing codepoints in comment"
37653764
}
37663765

3767-
declare_lint! {
3768-
/// The `deref_into_dyn_supertrait` lint is output whenever there is a use of the
3769-
/// `Deref` implementation with a `dyn SuperTrait` type as `Output`.
3770-
///
3771-
/// These implementations will become shadowed when the `trait_upcasting` feature is stabilized.
3772-
/// The `deref` functions will no longer be called implicitly, so there might be behavior change.
3773-
///
3774-
/// ### Example
3775-
///
3776-
/// ```rust,compile_fail
3777-
/// #![deny(deref_into_dyn_supertrait)]
3778-
/// #![allow(dead_code)]
3779-
///
3780-
/// use core::ops::Deref;
3781-
///
3782-
/// trait A {}
3783-
/// trait B: A {}
3784-
/// impl<'a> Deref for dyn 'a + B {
3785-
/// type Target = dyn A;
3786-
/// fn deref(&self) -> &Self::Target {
3787-
/// todo!()
3788-
/// }
3789-
/// }
3790-
///
3791-
/// fn take_a(_: &dyn A) { }
3792-
///
3793-
/// fn take_b(b: &dyn B) {
3794-
/// take_a(b);
3795-
/// }
3796-
/// ```
3797-
///
3798-
/// {{produces}}
3799-
///
3800-
/// ### Explanation
3801-
///
3802-
/// The dyn upcasting coercion feature adds new coercion rules, taking priority
3803-
/// over certain other coercion rules, which will cause some behavior change.
3804-
pub DEREF_INTO_DYN_SUPERTRAIT,
3805-
Warn,
3806-
"`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future",
3807-
@future_incompatible = FutureIncompatibleInfo {
3808-
reference: "issue #89460 <https://github.com/rust-lang/rust/issues/89460>",
3809-
};
3810-
}
3811-
38123766
declare_lint! {
38133767
/// The `duplicate_macro_attributes` lint detects when a `#[test]`-like built-in macro
38143768
/// attribute is duplicated on an item. This lint may trigger on `bench`, `cfg_eval`, `test`

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

-12
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66
//!
77
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
88
use hir::LangItem;
9-
use rustc_errors::DelayDm;
109
use rustc_hir as hir;
1110
use rustc_infer::traits::ObligationCause;
1211
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
13-
use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
1412
use rustc_middle::ty::print::with_no_trimmed_paths;
1513
use rustc_middle::ty::{self, Ty, TypeVisitable};
1614
use rustc_target::spec::abi::Abi;
@@ -809,16 +807,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
809807
&obligation.cause,
810808
) {
811809
if deref_trait_ref.def_id() == target_trait_did {
812-
self.tcx().struct_span_lint_hir(
813-
DEREF_INTO_DYN_SUPERTRAIT,
814-
obligation.cause.body_id,
815-
obligation.cause.span,
816-
DelayDm(|| format!(
817-
"`{}` implements `Deref` with supertrait `{}` as output",
818-
source, deref_trait_ref
819-
)),
820-
|lint| lint,
821-
);
822810
return;
823811
}
824812
}

src/test/ui/traits/trait-upcasting/migrate-lint-deny.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ use core::ops::Deref;
77
// issue 89190
88
trait A {}
99
trait B: A {}
10+
1011
impl<'a> Deref for dyn 'a + B {
12+
//~^ ERROR `(dyn B + 'a)` implements `Deref` with supertrait `A` as target
13+
//~| WARN this was previously accepted by the compiler but is being phased out;
14+
1115
type Target = dyn A;
1216
fn deref(&self) -> &Self::Target {
1317
todo!()
@@ -18,8 +22,6 @@ fn take_a(_: &dyn A) {}
1822

1923
fn whoops(b: &dyn B) {
2024
take_a(b)
21-
//~^ ERROR `dyn B` implements `Deref` with supertrait `A` as output
22-
//~^^ WARN this was previously accepted by the compiler but is being phased out;
2325
}
2426

2527
fn main() {}

src/test/ui/traits/trait-upcasting/migrate-lint-deny.stderr

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
error: `dyn B` implements `Deref` with supertrait `A` as output
2-
--> $DIR/migrate-lint-deny.rs:20:12
1+
error: `(dyn B + 'a)` implements `Deref` with supertrait `A` as target
2+
--> $DIR/migrate-lint-deny.rs:11:1
33
|
4-
LL | take_a(b)
5-
| ^
4+
LL | impl<'a> Deref for dyn 'a + B {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
...
7+
LL | type Target = dyn A;
8+
| -------------------- target type is set here
69
|
710
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
811
= note: for more information, see issue #89460 <https://github.com/rust-lang/rust/issues/89460>

src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::snippet_opt;
3-
use clippy_utils::ty::{get_associated_type, implements_trait, is_copy};
3+
use clippy_utils::ty::{implements_trait, is_copy};
44
use rustc_errors::Applicability;
55
use rustc_hir::Expr;
66
use rustc_lint::LateContext;
@@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(
2525
&& let Some(method_id) = typeck.type_dependent_def_id(cloned_call.hir_id)
2626
&& cx.tcx.trait_of_item(method_id) == Some(iter_id)
2727
&& let cloned_recv_ty = typeck.expr_ty_adjusted(cloned_recv)
28-
&& let Some(iter_assoc_ty) = get_associated_type(cx, cloned_recv_ty, iter_id, "Item")
28+
&& let Some(iter_assoc_ty) = cx.get_associated_type(cloned_recv_ty, iter_id, "Item")
2929
&& matches!(*iter_assoc_ty.kind(), ty::Ref(_, ty, _) if !is_copy(cx, ty))
3030
{
3131
if needs_into_iter

src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed;
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::higher::ForLoop;
44
use clippy_utils::source::snippet_opt;
5-
use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait};
5+
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
66
use clippy_utils::{fn_def_id, get_parent_expr};
77
use rustc_errors::Applicability;
88
use rustc_hir::{def_id::DefId, Expr, ExprKind};
@@ -54,7 +54,7 @@ pub fn check_for_loop_iter(
5454
if let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator);
5555
let collection_ty = cx.typeck_results().expr_ty(collection);
5656
if implements_trait(cx, collection_ty, into_iterator_trait_id, &[]);
57-
if let Some(into_iter_item_ty) = get_associated_type(cx, collection_ty, into_iterator_trait_id, "Item");
57+
if let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item");
5858

5959
if iter_item_ty == into_iter_item_ty;
6060
if let Some(collection_snippet) = snippet_opt(cx, collection.span);

src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs

+18-9
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ use super::implicit_clone::is_clone_like;
22
use super::unnecessary_iter_cloned::{self, is_into_iter};
33
use clippy_utils::diagnostics::span_lint_and_sugg;
44
use clippy_utils::source::snippet_opt;
5-
use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
5+
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
66
use clippy_utils::visitors::find_all_ret_expressions;
7-
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
7+
use clippy_utils::{
8+
fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty,
9+
};
810
use clippy_utils::{meets_msrv, msrvs};
911
use rustc_errors::Applicability;
1012
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
@@ -18,7 +20,9 @@ use rustc_middle::ty::EarlyBinder;
1820
use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
1921
use rustc_semver::RustcVersion;
2022
use rustc_span::{sym, Symbol};
21-
use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
23+
use rustc_trait_selection::traits::{
24+
query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause,
25+
};
2226
use std::cmp::max;
2327

2428
use super::UNNECESSARY_TO_OWNED;
@@ -146,7 +150,7 @@ fn check_addr_of_expr(
146150
if_chain! {
147151
if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
148152
if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
149-
if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty);
153+
if cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty);
150154
then {
151155
if n_receiver_refs > 0 {
152156
span_lint_and_sugg(
@@ -341,13 +345,13 @@ fn get_input_traits_and_projections<'tcx>(
341345
if trait_predicate.trait_ref.self_ty() == input {
342346
trait_predicates.push(trait_predicate);
343347
}
344-
},
348+
}
345349
PredicateKind::Projection(projection_predicate) => {
346350
if projection_predicate.projection_ty.self_ty() == input {
347351
projection_predicates.push(projection_predicate);
348352
}
349-
},
350-
_ => {},
353+
}
354+
_ => {}
351355
}
352356
}
353357
(trait_predicates, projection_predicates)
@@ -462,7 +466,12 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id:
462466

463467
/// Returns true if the named method can be used to convert the receiver to its "owned"
464468
/// representation.
465-
fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool {
469+
fn is_to_owned_like<'a>(
470+
cx: &LateContext<'a>,
471+
call_expr: &Expr<'a>,
472+
method_name: Symbol,
473+
method_def_id: DefId,
474+
) -> bool {
466475
is_clone_like(cx, method_name.as_str(), method_def_id)
467476
|| is_cow_into_owned(cx, method_name, method_def_id)
468477
|| is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
@@ -490,7 +499,7 @@ fn is_to_string_on_string_like<'a>(
490499
&& let GenericArgKind::Type(ty) = generic_arg.unpack()
491500
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
492501
&& let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
493-
&& (get_associated_type(cx, ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) ||
502+
&& (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) ||
494503
implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) {
495504
true
496505
} else {

0 commit comments

Comments
 (0)