]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
Rollup merge of #107656 - jonhoo:bump-rust-installer, r=Mark-Simulacrum
[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     /// ```rust
30     /// trait Duh {}
31     ///
32     /// impl Duh for i32 {}
33     ///
34     /// trait Trait {
35     ///     type Assoc: Duh;
36     /// }
37     ///
38     /// struct Struct;
39     ///
40     /// impl<F: Duh> Trait for F {
41     ///     type Assoc = F;
42     /// }
43     ///
44     /// fn test() -> impl Trait<Assoc = impl Sized> {
45     ///     42
46     /// }
47     /// ```
48     ///
49     /// {{produces}}
50     ///
51     /// In this example, `test` declares that the associated type `Assoc` for
52     /// `impl Trait` is `impl Sized`, which does not satisfy the `Send` bound
53     /// on the associated type.
54     ///
55     /// Although the hidden type, `i32` does satisfy this bound, we do not
56     /// consider the return type to be well-formed with this lint. It can be
57     /// fixed by changing `impl Sized` into `impl Sized + Send`.
58     pub OPAQUE_HIDDEN_INFERRED_BOUND,
59     Warn,
60     "detects the use of nested `impl Trait` types in associated type bounds that are not general enough"
61 }
62
63 declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]);
64
65 impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
66     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
67         let hir::ItemKind::OpaqueTy(_) = &item.kind else { return; };
68         let def_id = item.owner_id.def_id.to_def_id();
69         let infcx = &cx.tcx.infer_ctxt().build();
70         // For every projection predicate in the opaque type's explicit bounds,
71         // check that the type that we're assigning actually satisfies the bounds
72         // of the associated type.
73         for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) {
74             // Liberate bound regions in the predicate since we
75             // don't actually care about lifetimes in this check.
76             let predicate = cx.tcx.liberate_late_bound_regions(def_id, pred.kind());
77             let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = predicate else {
78                 continue;
79             };
80             // Only check types, since those are the only things that may
81             // have opaques in them anyways.
82             let Some(proj_term) = proj.term.ty() else { continue };
83
84             let proj_ty =
85                 cx.tcx.mk_projection(proj.projection_ty.def_id, proj.projection_ty.substs);
86             // For every instance of the projection type in the bounds,
87             // replace them with the term we're assigning to the associated
88             // type in our opaque type.
89             let proj_replacer = &mut BottomUpFolder {
90                 tcx: cx.tcx,
91                 ty_op: |ty| if ty == proj_ty { proj_term } else { ty },
92                 lt_op: |lt| lt,
93                 ct_op: |ct| ct,
94             };
95             // For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`,
96             // e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait`
97             // with `impl Send: OtherTrait`.
98             for (assoc_pred, assoc_pred_span) in cx
99                 .tcx
100                 .bound_explicit_item_bounds(proj.projection_ty.def_id)
101                 .subst_iter_copied(cx.tcx, &proj.projection_ty.substs)
102             {
103                 let assoc_pred = assoc_pred.fold_with(proj_replacer);
104                 let Ok(assoc_pred) = traits::fully_normalize(infcx, traits::ObligationCause::dummy(), cx.param_env, assoc_pred) else {
105                     continue;
106                 };
107                 // If that predicate doesn't hold modulo regions (but passed during type-check),
108                 // then we must've taken advantage of the hack in `project_and_unify_types` where
109                 // we replace opaques with inference vars. Emit a warning!
110                 if !infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new(
111                     cx.tcx,
112                     traits::ObligationCause::dummy(),
113                     cx.param_env,
114                     assoc_pred,
115                 )) {
116                     // If it's a trait bound and an opaque that doesn't satisfy it,
117                     // then we can emit a suggestion to add the bound.
118                     let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
119                         (
120                             ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }),
121                             ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)),
122                         ) => Some(AddBound {
123                             suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(),
124                             trait_ref: trait_pred.print_modifiers_and_trait_path(),
125                         }),
126                         _ => None,
127                     };
128                     cx.emit_spanned_lint(
129                         OPAQUE_HIDDEN_INFERRED_BOUND,
130                         pred_span,
131                         OpaqueHiddenInferredBoundLint {
132                             ty: cx.tcx.mk_opaque(
133                                 def_id,
134                                 ty::InternalSubsts::identity_for_item(cx.tcx, def_id),
135                             ),
136                             proj_ty: proj_term,
137                             assoc_pred_span,
138                             add_bound,
139                         },
140                     );
141                 }
142             }
143         }
144     }
145 }
146
147 #[derive(LintDiagnostic)]
148 #[diag(lint_opaque_hidden_inferred_bound)]
149 struct OpaqueHiddenInferredBoundLint<'tcx> {
150     ty: Ty<'tcx>,
151     proj_ty: Ty<'tcx>,
152     #[label(specifically)]
153     assoc_pred_span: Span,
154     #[subdiagnostic]
155     add_bound: Option<AddBound<'tcx>>,
156 }
157
158 #[derive(Subdiagnostic)]
159 #[suggestion(
160     lint_opaque_hidden_inferred_bound_sugg,
161     style = "verbose",
162     applicability = "machine-applicable",
163     code = " + {trait_ref}"
164 )]
165 struct AddBound<'tcx> {
166     #[primary_span]
167     suggest_span: Span,
168     #[skip_arg]
169     trait_ref: TraitPredPrintModifiersAndPath<'tcx>,
170 }