]>
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 | ||
5099ac24 FG |
109 | debug!( |
110 | "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}", | |
111 | sub_is_ret_type, sup_is_ret_type | |
112 | ); | |
29967ef6 | 113 | |
3c0e092e | 114 | let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); |
29967ef6 | 115 | |
5099ac24 FG |
116 | match (sup_is_ret_type, sub_is_ret_type) { |
117 | (ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => { | |
118 | let param_span = | |
119 | if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span }; | |
120 | ||
121 | err.span_label( | |
122 | param_span, | |
123 | "this parameter and the return type are declared with different lifetimes...", | |
124 | ); | |
125 | err.span_label(ret_span, ""); | |
126 | err.span_label(span, format!("...but data{} is returned here", span_label_var1)); | |
127 | } | |
128 | ||
129 | (None, None) => { | |
130 | if ty_sup.hir_id == ty_sub.hir_id { | |
131 | err.span_label(ty_sup.span, "this type is declared with multiple lifetimes..."); | |
132 | err.span_label(ty_sub.span, ""); | |
133 | err.span_label(span, "...but data with one lifetime flows into the other here"); | |
134 | } else { | |
135 | err.span_label( | |
136 | ty_sup.span, | |
137 | "these two types are declared with different lifetimes...", | |
138 | ); | |
139 | err.span_label(ty_sub.span, ""); | |
140 | err.span_label( | |
141 | span, | |
142 | format!("...but data{} flows{} here", span_label_var1, span_label_var2), | |
143 | ); | |
144 | } | |
145 | } | |
146 | } | |
3c0e092e XL |
147 | |
148 | self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err); | |
29967ef6 | 149 | |
3c0e092e | 150 | err.emit(); |
ba9703b0 | 151 | Some(ErrorReported) |
ff7c6d11 | 152 | } |
3c0e092e XL |
153 | |
154 | fn suggest_adding_lifetime_params( | |
155 | &self, | |
156 | sub: Region<'tcx>, | |
157 | ty_sup: &Ty<'_>, | |
158 | ty_sub: &Ty<'_>, | |
159 | err: &mut DiagnosticBuilder<'_>, | |
160 | ) { | |
161 | if let ( | |
162 | hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, | |
163 | hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, | |
164 | ) = (ty_sub, ty_sup) | |
165 | { | |
166 | if lifetime_sub.name.is_elided() && lifetime_sup.name.is_elided() { | |
167 | if let Some(anon_reg) = self.tcx().is_suitable_region(sub) { | |
168 | let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id); | |
169 | if let hir::Node::Item(&hir::Item { | |
170 | kind: hir::ItemKind::Fn(_, ref generics, ..), | |
171 | .. | |
172 | }) = self.tcx().hir().get(hir_id) | |
173 | { | |
174 | let (suggestion_param_name, introduce_new) = generics | |
175 | .params | |
176 | .iter() | |
177 | .find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) | |
178 | .and_then(|p| self.tcx().sess.source_map().span_to_snippet(p.span).ok()) | |
179 | .map(|name| (name, false)) | |
180 | .unwrap_or_else(|| ("'a".to_string(), true)); | |
181 | ||
182 | let mut suggestions = vec![ | |
183 | if let hir::LifetimeName::Underscore = lifetime_sub.name { | |
184 | (lifetime_sub.span, suggestion_param_name.clone()) | |
185 | } else { | |
186 | ( | |
187 | lifetime_sub.span.shrink_to_hi(), | |
188 | suggestion_param_name.clone() + " ", | |
189 | ) | |
190 | }, | |
191 | if let hir::LifetimeName::Underscore = lifetime_sup.name { | |
192 | (lifetime_sup.span, suggestion_param_name.clone()) | |
193 | } else { | |
194 | ( | |
195 | lifetime_sup.span.shrink_to_hi(), | |
196 | suggestion_param_name.clone() + " ", | |
197 | ) | |
198 | }, | |
199 | ]; | |
200 | ||
201 | if introduce_new { | |
202 | let new_param_suggestion = match &generics.params { | |
203 | [] => (generics.span, format!("<{}>", suggestion_param_name)), | |
204 | [first, ..] => ( | |
205 | first.span.shrink_to_lo(), | |
206 | format!("{}, ", suggestion_param_name), | |
207 | ), | |
208 | }; | |
209 | ||
210 | suggestions.push(new_param_suggestion); | |
211 | } | |
212 | ||
213 | err.multipart_suggestion( | |
214 | "consider introducing a named lifetime parameter", | |
215 | suggestions, | |
216 | Applicability::MaybeIncorrect, | |
217 | ); | |
218 | err.note( | |
219 | "each elided lifetime in input position becomes a distinct lifetime", | |
220 | ); | |
221 | } | |
222 | } | |
223 | } | |
224 | } | |
225 | } | |
ff7c6d11 | 226 | } |