]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
Rollup merge of #103915 - chenyukang:yukang/fix-103874, r=lcnr
[rust.git] / compiler / rustc_lint / src / opaque_hidden_inferred_bound.rs
1 use rustc_hir as hir;
2 use rustc_infer::infer::TyCtxtInferExt;
3 use rustc_macros::{LintDiagnostic, Subdiagnostic};
4 use rustc_middle::ty::{
5     self, fold::BottomUpFolder, print::TraitPredPrintModifiersAndPath, Ty, TypeFoldable,
6 };
7 use rustc_span::Span;
8 use rustc_trait_selection::traits;
9 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
10
11 use crate::{LateContext, LateLintPass, LintContext};
12
13 declare_lint! {
14     /// The `opaque_hidden_inferred_bound` lint detects cases in which nested
15     /// `impl Trait` in associated type bounds are not written generally enough
16     /// to satisfy the bounds of the associated type.
17     ///
18     /// ### Explanation
19     ///
20     /// This functionality was removed in #97346, but then rolled back in #99860
21     /// because it caused regressions.
22     ///
23     /// We plan on reintroducing this as a hard error, but in the mean time,
24     /// this lint serves to warn and suggest fixes for any use-cases which rely
25     /// on this behavior.
26     ///
27     /// ### Example
28     ///
29     /// ```
30     /// trait Trait {
31     ///     type Assoc: Send;
32     /// }
33     ///
34     /// struct Struct;
35     ///
36     /// impl Trait for Struct {
37     ///     type Assoc = i32;
38     /// }
39     ///
40     /// fn test() -> impl Trait<Assoc = impl Sized> {
41     ///     Struct
42     /// }
43     /// ```
44     ///
45     /// {{produces}}
46     ///
47     /// In this example, `test` declares that the associated type `Assoc` for
48     /// `impl Trait` is `impl Sized`, which does not satisfy the `Send` bound
49     /// on the associated type.
50     ///
51     /// Although the hidden type, `i32` does satisfy this bound, we do not
52     /// consider the return type to be well-formed with this lint. It can be
53     /// fixed by changing `impl Sized` into `impl Sized + Send`.
54     pub OPAQUE_HIDDEN_INFERRED_BOUND,
55     Warn,
56     "detects the use of nested `impl Trait` types in associated type bounds that are not general enough"
57 }
58
59 declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]);
60
61 impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
62     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
63         let hir::ItemKind::OpaqueTy(_) = &item.kind else { return; };
64         let def_id = item.owner_id.def_id.to_def_id();
65         let infcx = &cx.tcx.infer_ctxt().build();
66         // For every projection predicate in the opaque type's explicit bounds,
67         // check that the type that we're assigning actually satisfies the bounds
68         // of the associated type.
69         for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) {
70             // Liberate bound regions in the predicate since we
71             // don't actually care about lifetimes in this check.
72             let predicate = cx.tcx.liberate_late_bound_regions(def_id, pred.kind());
73             let ty::PredicateKind::Projection(proj) = predicate else {
74                 continue;
75             };
76             // Only check types, since those are the only things that may
77             // have opaques in them anyways.
78             let Some(proj_term) = proj.term.ty() else { continue };
79
80             let proj_ty =
81                 cx.tcx.mk_projection(proj.projection_ty.item_def_id, proj.projection_ty.substs);
82             // For every instance of the projection type in the bounds,
83             // replace them with the term we're assigning to the associated
84             // type in our opaque type.
85             let proj_replacer = &mut BottomUpFolder {
86                 tcx: cx.tcx,
87                 ty_op: |ty| if ty == proj_ty { proj_term } else { ty },
88                 lt_op: |lt| lt,
89                 ct_op: |ct| ct,
90             };
91             // For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`,
92             // e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait`
93             // with `impl Send: OtherTrait`.
94             for (assoc_pred, assoc_pred_span) in cx
95                 .tcx
96                 .bound_explicit_item_bounds(proj.projection_ty.item_def_id)
97                 .subst_iter_copied(cx.tcx, &proj.projection_ty.substs)
98             {
99                 let assoc_pred = assoc_pred.fold_with(proj_replacer);
100                 let Ok(assoc_pred) = traits::fully_normalize(infcx, traits::ObligationCause::dummy(), cx.param_env, assoc_pred) else {
101                     continue;
102                 };
103                 // If that predicate doesn't hold modulo regions (but passed during type-check),
104                 // then we must've taken advantage of the hack in `project_and_unify_types` where
105                 // we replace opaques with inference vars. Emit a warning!
106                 if !infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new(
107                     traits::ObligationCause::dummy(),
108                     cx.param_env,
109                     assoc_pred,
110                 )) {
111                     // If it's a trait bound and an opaque that doesn't satisfy it,
112                     // then we can emit a suggestion to add the bound.
113                     let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
114                         (ty::Opaque(def_id, _), ty::PredicateKind::Trait(trait_pred)) => {
115                             Some(AddBound {
116                                 suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(),
117                                 trait_ref: trait_pred.print_modifiers_and_trait_path(),
118                             })
119                         }
120                         _ => None,
121                     };
122                     cx.emit_spanned_lint(
123                         OPAQUE_HIDDEN_INFERRED_BOUND,
124                         pred_span,
125                         OpaqueHiddenInferredBoundLint {
126                             ty: cx.tcx.mk_opaque(
127                                 def_id,
128                                 ty::InternalSubsts::identity_for_item(cx.tcx, def_id),
129                             ),
130                             proj_ty: proj_term,
131                             assoc_pred_span,
132                             add_bound,
133                         },
134                     );
135                 }
136             }
137         }
138     }
139 }
140
141 #[derive(LintDiagnostic)]
142 #[diag(lint_opaque_hidden_inferred_bound)]
143 struct OpaqueHiddenInferredBoundLint<'tcx> {
144     ty: Ty<'tcx>,
145     proj_ty: Ty<'tcx>,
146     #[label(specifically)]
147     assoc_pred_span: Span,
148     #[subdiagnostic]
149     add_bound: Option<AddBound<'tcx>>,
150 }
151
152 #[derive(Subdiagnostic)]
153 #[suggestion(
154     lint_opaque_hidden_inferred_bound_sugg,
155     style = "verbose",
156     applicability = "machine-applicable",
157     code = " + {trait_ref}"
158 )]
159 struct AddBound<'tcx> {
160     #[primary_span]
161     suggest_span: Span,
162     #[skip_arg]
163     trait_ref: TraitPredPrintModifiersAndPath<'tcx>,
164 }