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