]>
Commit | Line | Data |
---|---|---|
ff7c6d11 XL |
1 | //! Error Reporting for Anonymous Region Lifetime Errors |
2 | //! where both the regions are anonymous. | |
3 | ||
6a06907d | 4 | use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; |
e1599b0c | 5 | use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo; |
dfeec247 | 6 | use crate::infer::error_reporting::nice_region_error::NiceRegionError; |
ba9703b0 XL |
7 | use crate::infer::lexical_region_resolve::RegionResolutionError; |
8 | use crate::infer::SubregionOrigin; | |
ff7c6d11 | 9 | |
3c0e092e XL |
10 | use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; |
11 | use rustc_hir as hir; | |
12 | use rustc_hir::{GenericParamKind, Ty}; | |
13 | use rustc_middle::ty::Region; | |
60c5eb7d | 14 | |
dc9dc135 | 15 | impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { |
ff7c6d11 XL |
16 | /// Print the error message for lifetime errors when both the concerned regions are anonymous. |
17 | /// | |
18 | /// Consider a case where we have | |
19 | /// | |
20 | /// ```no_run | |
21 | /// fn foo(x: &mut Vec<&u8>, y: &u8) { | |
22 | /// x.push(y); | |
23 | /// } | |
24 | /// ``` | |
25 | /// | |
26 | /// The example gives | |
27 | /// | |
28 | /// ```text | |
29 | /// fn foo(x: &mut Vec<&u8>, y: &u8) { | |
30 | /// --- --- these references are declared with different lifetimes... | |
31 | /// x.push(y); | |
32 | /// ^ ...but data from `y` flows into `x` here | |
33 | /// ``` | |
34 | /// | |
35 | /// It has been extended for the case of structs too. | |
36 | /// | |
37 | /// Consider the example | |
38 | /// | |
39 | /// ```no_run | |
40 | /// struct Ref<'a> { x: &'a u32 } | |
41 | /// ``` | |
42 | /// | |
43 | /// ```text | |
44 | /// fn foo(mut x: Vec<Ref>, y: Ref) { | |
45 | /// --- --- these structs are declared with different lifetimes... | |
46 | /// x.push(y); | |
47 | /// ^ ...but data from `y` flows into `x` here | |
48 | /// } | |
9fa01778 | 49 | /// ``` |
ff7c6d11 XL |
50 | /// |
51 | /// It will later be extended to trait objects. | |
52 | pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorReported> { | |
74b04a01 | 53 | let (span, sub, sup) = self.regions()?; |
ff7c6d11 | 54 | |
ba9703b0 XL |
55 | if let Some(RegionResolutionError::ConcreteFailure( |
56 | SubregionOrigin::ReferenceOutlivesReferent(..), | |
57 | .., | |
58 | )) = self.error | |
59 | { | |
60 | // This error doesn't make much sense in this case. | |
61 | return None; | |
62 | } | |
63 | ||
ff7c6d11 | 64 | // Determine whether the sub and sup consist of both anonymous (elided) regions. |
9fa01778 | 65 | let anon_reg_sup = self.tcx().is_suitable_region(sup)?; |
ff7c6d11 | 66 | |
9fa01778 | 67 | let anon_reg_sub = self.tcx().is_suitable_region(sub)?; |
ff7c6d11 XL |
68 | let scope_def_id_sup = anon_reg_sup.def_id; |
69 | let bregion_sup = anon_reg_sup.boundregion; | |
70 | let scope_def_id_sub = anon_reg_sub.def_id; | |
71 | let bregion_sub = anon_reg_sub.boundregion; | |
72 | ||
6a06907d | 73 | let ty_sup = find_anon_type(self.tcx(), sup, &bregion_sup)?; |
ff7c6d11 | 74 | |
6a06907d | 75 | let ty_sub = find_anon_type(self.tcx(), sub, &bregion_sub)?; |
ff7c6d11 XL |
76 | |
77 | debug!( | |
e1599b0c | 78 | "try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}", |
dfeec247 | 79 | ty_sub, sup, bregion_sup |
ff7c6d11 XL |
80 | ); |
81 | debug!( | |
e1599b0c | 82 | "try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}", |
dfeec247 | 83 | ty_sup, sub, bregion_sub |
ff7c6d11 XL |
84 | ); |
85 | ||
86 | let (ty_sup, ty_fndecl_sup) = ty_sup; | |
87 | let (ty_sub, ty_fndecl_sub) = ty_sub; | |
88 | ||
dfeec247 XL |
89 | let AnonymousParamInfo { param: anon_param_sup, .. } = |
90 | self.find_param_with_region(sup, sup)?; | |
91 | let AnonymousParamInfo { param: anon_param_sub, .. } = | |
92 | self.find_param_with_region(sub, sub)?; | |
ff7c6d11 XL |
93 | |
94 | let sup_is_ret_type = | |
95 | self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup); | |
96 | let sub_is_ret_type = | |
97 | self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); | |
98 | ||
e1599b0c | 99 | let span_label_var1 = match anon_param_sup.pat.simple_ident() { |
48663c56 XL |
100 | Some(simple_ident) => format!(" from `{}`", simple_ident), |
101 | None => String::new(), | |
ff7c6d11 XL |
102 | }; |
103 | ||
e1599b0c | 104 | let span_label_var2 = match anon_param_sub.pat.simple_ident() { |
48663c56 XL |
105 | Some(simple_ident) => format!(" into `{}`", simple_ident), |
106 | None => String::new(), | |
ff7c6d11 XL |
107 | }; |
108 | ||
29967ef6 XL |
109 | let (span_1, span_2, main_label, span_label, future_return_type) = |
110 | match (sup_is_ret_type, sub_is_ret_type) { | |
111 | (None, None) => { | |
112 | let (main_label_1, span_label_1) = if ty_sup.hir_id == ty_sub.hir_id { | |
113 | ( | |
114 | "this type is declared with multiple lifetimes...".to_owned(), | |
115 | "...but data with one lifetime flows into the other here".to_owned(), | |
116 | ) | |
117 | } else { | |
118 | ( | |
119 | "these two types are declared with different lifetimes...".to_owned(), | |
120 | format!("...but data{} flows{} here", span_label_var1, span_label_var2), | |
121 | ) | |
122 | }; | |
123 | (ty_sup.span, ty_sub.span, main_label_1, span_label_1, None) | |
124 | } | |
125 | ||
126 | (Some(ret_span), _) => { | |
127 | let sup_future = self.future_return_type(scope_def_id_sup); | |
fc512014 | 128 | let (return_type, action) = if sup_future.is_some() { |
29967ef6 XL |
129 | ("returned future", "held across an await point") |
130 | } else { | |
131 | ("return type", "returned") | |
132 | }; | |
133 | ||
ff7c6d11 | 134 | ( |
29967ef6 XL |
135 | ty_sub.span, |
136 | ret_span, | |
137 | format!( | |
138 | "this parameter and the {} are declared with different lifetimes...", | |
139 | return_type | |
140 | ), | |
141 | format!("...but data{} is {} here", span_label_var1, action), | |
142 | sup_future, | |
ff7c6d11 | 143 | ) |
29967ef6 XL |
144 | } |
145 | (_, Some(ret_span)) => { | |
146 | let sub_future = self.future_return_type(scope_def_id_sub); | |
fc512014 | 147 | let (return_type, action) = if sub_future.is_some() { |
29967ef6 XL |
148 | ("returned future", "held across an await point") |
149 | } else { | |
150 | ("return type", "returned") | |
151 | }; | |
152 | ||
ff7c6d11 | 153 | ( |
29967ef6 XL |
154 | ty_sup.span, |
155 | ret_span, | |
156 | format!( | |
157 | "this parameter and the {} are declared with different lifetimes...", | |
158 | return_type | |
159 | ), | |
160 | format!("...but data{} is {} here", span_label_var1, action), | |
161 | sub_future, | |
ff7c6d11 | 162 | ) |
29967ef6 XL |
163 | } |
164 | }; | |
165 | ||
3c0e092e | 166 | let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); |
29967ef6 | 167 | |
3c0e092e XL |
168 | err.span_label(span_1, main_label); |
169 | err.span_label(span_2, String::new()); | |
170 | err.span_label(span, span_label); | |
171 | ||
172 | self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err); | |
29967ef6 XL |
173 | |
174 | if let Some(t) = future_return_type { | |
175 | let snip = self | |
176 | .tcx() | |
177 | .sess | |
178 | .source_map() | |
179 | .span_to_snippet(t.span) | |
180 | .ok() | |
181 | .and_then(|s| match (&t.kind, s.as_str()) { | |
182 | (rustc_hir::TyKind::Tup(&[]), "") => Some("()".to_string()), | |
183 | (_, "") => None, | |
184 | _ => Some(s), | |
185 | }) | |
3c0e092e | 186 | .unwrap_or_else(|| "{unnamed_type}".to_string()); |
29967ef6 | 187 | |
3c0e092e | 188 | err.span_label( |
29967ef6 XL |
189 | t.span, |
190 | &format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip), | |
191 | ); | |
192 | } | |
3c0e092e | 193 | err.emit(); |
ba9703b0 | 194 | Some(ErrorReported) |
ff7c6d11 | 195 | } |
3c0e092e XL |
196 | |
197 | fn suggest_adding_lifetime_params( | |
198 | &self, | |
199 | sub: Region<'tcx>, | |
200 | ty_sup: &Ty<'_>, | |
201 | ty_sub: &Ty<'_>, | |
202 | err: &mut DiagnosticBuilder<'_>, | |
203 | ) { | |
204 | if let ( | |
205 | hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, | |
206 | hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, | |
207 | ) = (ty_sub, ty_sup) | |
208 | { | |
209 | if lifetime_sub.name.is_elided() && lifetime_sup.name.is_elided() { | |
210 | if let Some(anon_reg) = self.tcx().is_suitable_region(sub) { | |
211 | let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id); | |
212 | if let hir::Node::Item(&hir::Item { | |
213 | kind: hir::ItemKind::Fn(_, ref generics, ..), | |
214 | .. | |
215 | }) = self.tcx().hir().get(hir_id) | |
216 | { | |
217 | let (suggestion_param_name, introduce_new) = generics | |
218 | .params | |
219 | .iter() | |
220 | .find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) | |
221 | .and_then(|p| self.tcx().sess.source_map().span_to_snippet(p.span).ok()) | |
222 | .map(|name| (name, false)) | |
223 | .unwrap_or_else(|| ("'a".to_string(), true)); | |
224 | ||
225 | let mut suggestions = vec![ | |
226 | if let hir::LifetimeName::Underscore = lifetime_sub.name { | |
227 | (lifetime_sub.span, suggestion_param_name.clone()) | |
228 | } else { | |
229 | ( | |
230 | lifetime_sub.span.shrink_to_hi(), | |
231 | suggestion_param_name.clone() + " ", | |
232 | ) | |
233 | }, | |
234 | if let hir::LifetimeName::Underscore = lifetime_sup.name { | |
235 | (lifetime_sup.span, suggestion_param_name.clone()) | |
236 | } else { | |
237 | ( | |
238 | lifetime_sup.span.shrink_to_hi(), | |
239 | suggestion_param_name.clone() + " ", | |
240 | ) | |
241 | }, | |
242 | ]; | |
243 | ||
244 | if introduce_new { | |
245 | let new_param_suggestion = match &generics.params { | |
246 | [] => (generics.span, format!("<{}>", suggestion_param_name)), | |
247 | [first, ..] => ( | |
248 | first.span.shrink_to_lo(), | |
249 | format!("{}, ", suggestion_param_name), | |
250 | ), | |
251 | }; | |
252 | ||
253 | suggestions.push(new_param_suggestion); | |
254 | } | |
255 | ||
256 | err.multipart_suggestion( | |
257 | "consider introducing a named lifetime parameter", | |
258 | suggestions, | |
259 | Applicability::MaybeIncorrect, | |
260 | ); | |
261 | err.note( | |
262 | "each elided lifetime in input position becomes a distinct lifetime", | |
263 | ); | |
264 | } | |
265 | } | |
266 | } | |
267 | } | |
268 | } | |
ff7c6d11 | 269 | } |