]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
Merge commit 'd7b5cbf065b88830ca519adcb73fad4c0d24b1c7' into clippyup
[rust.git] / compiler / rustc_infer / src / infer / error_reporting / nice_region_error / different_lifetimes.rs
1 //! Error Reporting for Anonymous Region Lifetime Errors
2 //! where both the regions are anonymous.
3
4 use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
5 use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo;
6 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
7 use crate::infer::lexical_region_resolve::RegionResolutionError;
8 use crate::infer::SubregionOrigin;
9 use crate::infer::TyCtxt;
10
11 use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
12 use rustc_hir as hir;
13 use rustc_hir::{GenericParamKind, Ty};
14 use rustc_middle::ty::Region;
15 use rustc_span::symbol::kw;
16
17 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
18     /// Print the error message for lifetime errors when both the concerned regions are anonymous.
19     ///
20     /// Consider a case where we have
21     ///
22     /// ```compile_fail,E0623
23     /// fn foo(x: &mut Vec<&u8>, y: &u8) {
24     ///     x.push(y);
25     /// }
26     /// ```
27     ///
28     /// The example gives
29     ///
30     /// ```text
31     /// fn foo(x: &mut Vec<&u8>, y: &u8) {
32     ///                    ---      --- these references are declared with different lifetimes...
33     ///     x.push(y);
34     ///     ^ ...but data from `y` flows into `x` here
35     /// ```
36     ///
37     /// It has been extended for the case of structs too.
38     ///
39     /// Consider the example
40     ///
41     /// ```no_run
42     /// struct Ref<'a> { x: &'a u32 }
43     /// ```
44     ///
45     /// ```text
46     /// fn foo(mut x: Vec<Ref>, y: Ref) {
47     ///                   ---      --- these structs are declared with different lifetimes...
48     ///     x.push(y);
49     ///     ^ ...but data from `y` flows into `x` here
50     /// }
51     /// ```
52     ///
53     /// It will later be extended to trait objects.
54     pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorGuaranteed> {
55         let (span, sub, sup) = self.regions()?;
56
57         if let Some(RegionResolutionError::ConcreteFailure(
58             SubregionOrigin::ReferenceOutlivesReferent(..),
59             ..,
60         )) = self.error
61         {
62             // This error doesn't make much sense in this case.
63             return None;
64         }
65
66         // Determine whether the sub and sup consist of both anonymous (elided) regions.
67         let anon_reg_sup = self.tcx().is_suitable_region(sup)?;
68
69         let anon_reg_sub = self.tcx().is_suitable_region(sub)?;
70         let scope_def_id_sup = anon_reg_sup.def_id;
71         let bregion_sup = anon_reg_sup.boundregion;
72         let scope_def_id_sub = anon_reg_sub.def_id;
73         let bregion_sub = anon_reg_sub.boundregion;
74
75         let ty_sup = find_anon_type(self.tcx(), sup, &bregion_sup)?;
76
77         let ty_sub = find_anon_type(self.tcx(), sub, &bregion_sub)?;
78
79         debug!(
80             "try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}",
81             ty_sub, sup, bregion_sup
82         );
83         debug!(
84             "try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}",
85             ty_sup, sub, bregion_sub
86         );
87
88         let (ty_sup, ty_fndecl_sup) = ty_sup;
89         let (ty_sub, ty_fndecl_sub) = ty_sub;
90
91         let AnonymousParamInfo { param: anon_param_sup, .. } =
92             self.find_param_with_region(sup, sup)?;
93         let AnonymousParamInfo { param: anon_param_sub, .. } =
94             self.find_param_with_region(sub, sub)?;
95
96         let sup_is_ret_type =
97             self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup);
98         let sub_is_ret_type =
99             self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub);
100
101         let span_label_var1 = match anon_param_sup.pat.simple_ident() {
102             Some(simple_ident) => format!(" from `{}`", simple_ident),
103             None => String::new(),
104         };
105
106         let span_label_var2 = match anon_param_sub.pat.simple_ident() {
107             Some(simple_ident) => format!(" into `{}`", simple_ident),
108             None => String::new(),
109         };
110
111         debug!(
112             "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}",
113             sub_is_ret_type, sup_is_ret_type
114         );
115
116         let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
117
118         match (sup_is_ret_type, sub_is_ret_type) {
119             (ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => {
120                 let param_span =
121                     if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span };
122
123                 err.span_label(
124                     param_span,
125                     "this parameter and the return type are declared with different lifetimes...",
126                 );
127                 err.span_label(ret_span, "");
128                 err.span_label(span, format!("...but data{} is returned here", span_label_var1));
129             }
130
131             (None, None) => {
132                 if ty_sup.hir_id == ty_sub.hir_id {
133                     err.span_label(ty_sup.span, "this type is declared with multiple lifetimes...");
134                     err.span_label(ty_sub.span, "");
135                     err.span_label(span, "...but data with one lifetime flows into the other here");
136                 } else {
137                     err.span_label(
138                         ty_sup.span,
139                         "these two types are declared with different lifetimes...",
140                     );
141                     err.span_label(ty_sub.span, "");
142                     err.span_label(
143                         span,
144                         format!("...but data{} flows{} here", span_label_var1, span_label_var2),
145                     );
146                 }
147             }
148         }
149
150         if suggest_adding_lifetime_params(self.tcx(), sub, ty_sup, ty_sub, &mut err) {
151             err.note("each elided lifetime in input position becomes a distinct lifetime");
152         }
153
154         let reported = err.emit();
155         Some(reported)
156     }
157 }
158
159 pub fn suggest_adding_lifetime_params<'tcx>(
160     tcx: TyCtxt<'tcx>,
161     sub: Region<'tcx>,
162     ty_sup: &Ty<'_>,
163     ty_sub: &Ty<'_>,
164     err: &mut Diagnostic,
165 ) -> bool {
166     let (
167         hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
168         hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
169     ) = (ty_sub, ty_sup) else {
170         return false;
171     };
172
173     if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() {
174         return false;
175     };
176
177     let Some(anon_reg) = tcx.is_suitable_region(sub) else {
178         return false;
179     };
180
181     let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id);
182
183     let node = tcx.hir().get(hir_id);
184     let is_impl = matches!(&node, hir::Node::ImplItem(_));
185     let generics = match node {
186         hir::Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, ref generics, ..), .. })
187         | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. })
188         | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics,
189         _ => return false,
190     };
191
192     let suggestion_param_name = generics
193         .params
194         .iter()
195         .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
196         .map(|p| p.name.ident().name)
197         .find(|i| *i != kw::UnderscoreLifetime);
198     let introduce_new = suggestion_param_name.is_none();
199     let suggestion_param_name =
200         suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned());
201
202     debug!(?lifetime_sup.span);
203     debug!(?lifetime_sub.span);
204     let make_suggestion = |span: rustc_span::Span| {
205         if span.is_empty() {
206             (span, format!("{}, ", suggestion_param_name))
207         } else if let Ok("&") = tcx.sess.source_map().span_to_snippet(span).as_deref() {
208             (span.shrink_to_hi(), format!("{} ", suggestion_param_name))
209         } else {
210             (span, suggestion_param_name.clone())
211         }
212     };
213     let mut suggestions =
214         vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)];
215
216     if introduce_new {
217         let new_param_suggestion =
218             if let Some(first) = generics.params.iter().find(|p| !p.name.ident().span.is_empty()) {
219                 (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name))
220             } else {
221                 (generics.span, format!("<{}>", suggestion_param_name))
222             };
223
224         suggestions.push(new_param_suggestion);
225     }
226
227     let mut sugg = String::from("consider introducing a named lifetime parameter");
228     if is_impl {
229         sugg.push_str(" and update trait if needed");
230     }
231     err.multipart_suggestion(sugg.as_str(), suggestions, Applicability::MaybeIncorrect);
232
233     true
234 }