]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
Replace `&mut DiagnosticBuilder`, in signatures, with `&mut Diagnostic`.
[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
10 use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported};
11 use rustc_hir as hir;
12 use rustc_hir::{GenericParamKind, Ty};
13 use rustc_middle::ty::Region;
14
15 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
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     /// }
49     /// ```
50     ///
51     /// It will later be extended to trait objects.
52     pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorReported> {
53         let (span, sub, sup) = self.regions()?;
54
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
64         // Determine whether the sub and sup consist of both anonymous (elided) regions.
65         let anon_reg_sup = self.tcx().is_suitable_region(sup)?;
66
67         let anon_reg_sub = self.tcx().is_suitable_region(sub)?;
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
73         let ty_sup = find_anon_type(self.tcx(), sup, &bregion_sup)?;
74
75         let ty_sub = find_anon_type(self.tcx(), sub, &bregion_sub)?;
76
77         debug!(
78             "try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}",
79             ty_sub, sup, bregion_sup
80         );
81         debug!(
82             "try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}",
83             ty_sup, sub, bregion_sub
84         );
85
86         let (ty_sup, ty_fndecl_sup) = ty_sup;
87         let (ty_sub, ty_fndecl_sub) = ty_sub;
88
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)?;
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
99         let span_label_var1 = match anon_param_sup.pat.simple_ident() {
100             Some(simple_ident) => format!(" from `{}`", simple_ident),
101             None => String::new(),
102         };
103
104         let span_label_var2 = match anon_param_sub.pat.simple_ident() {
105             Some(simple_ident) => format!(" into `{}`", simple_ident),
106             None => String::new(),
107         };
108
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         );
113
114         let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
115
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         }
147
148         self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err);
149
150         err.emit();
151         Some(ErrorReported)
152     }
153
154     fn suggest_adding_lifetime_params(
155         &self,
156         sub: Region<'tcx>,
157         ty_sup: &Ty<'_>,
158         ty_sub: &Ty<'_>,
159         err: &mut Diagnostic,
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     }
226 }