]> git.lizzy.rs Git - rust.git/blob - src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs
d881a529cad3846509ba26948d9a1b9b8e2c59ca
[rust.git] / src / librustc_infer / infer / error_reporting / nice_region_error / static_impl_trait.rs
1 //! Error Reporting for static impl Traits.
2
3 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
4 use crate::infer::lexical_region_resolve::RegionResolutionError;
5 use rustc_errors::{struct_span_err, Applicability, ErrorReported};
6 use rustc_hir::{GenericBound, ItemKind, Lifetime, LifetimeName, TyKind};
7 use rustc_middle::ty::RegionKind;
8
9 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
10     /// Print the error message for lifetime errors when the return type is a static impl Trait.
11     pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> {
12         debug!("try_report_static_impl_trait(error={:?})", self.error);
13         if let Some(ref error) = self.error {
14             if let RegionResolutionError::SubSupConflict(
15                 _,
16                 var_origin,
17                 sub_origin,
18                 sub_r,
19                 sup_origin,
20                 sup_r,
21             ) = error
22             {
23                 debug!(
24                     "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
25                     var_origin, sub_origin, sub_r, sup_origin, sup_r
26                 );
27                 let anon_reg_sup = self.tcx().is_suitable_region(sup_r)?;
28                 debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
29                 let fn_returns =
30                     self.tcx().return_type_impl_or_dyn_traits(anon_reg_sup.def_id.expect_local());
31                 if fn_returns.is_empty() {
32                     return None;
33                 }
34                 debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
35                 if **sub_r == RegionKind::ReStatic {
36                     let sp = var_origin.span();
37                     let return_sp = sub_origin.span();
38                     let param_info = self.find_param_with_region(sup_r, sub_r)?;
39                     let (lifetime_name, lifetime) = if sup_r.has_name() {
40                         (sup_r.to_string(), format!("lifetime `{}`", sup_r))
41                     } else {
42                         ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
43                     };
44                     let mut err = struct_span_err!(
45                         self.tcx().sess,
46                         sp,
47                         E0759,
48                         "cannot infer an appropriate lifetime"
49                     );
50                     err.span_label(
51                         param_info.param_ty_span,
52                         &format!("this data with {}...", lifetime),
53                     );
54                     debug!("try_report_static_impl_trait: param_info={:?}", param_info);
55
56                     // We try to make the output have fewer overlapping spans if possible.
57                     if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span()))
58                         && sup_origin.span() != return_sp
59                     {
60                         // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs`
61
62                         // Customize the spans and labels depending on their relative order so
63                         // that split sentences flow correctly.
64                         if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() {
65                             // Avoid the following:
66                             //
67                             // error: cannot infer an appropriate lifetime
68                             //   --> $DIR/must_outlive_least_region_or_bound.rs:18:50
69                             //    |
70                             // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
71                             //    |           ----                      ---------^-
72                             //
73                             // and instead show:
74                             //
75                             // error: cannot infer an appropriate lifetime
76                             //   --> $DIR/must_outlive_least_region_or_bound.rs:18:50
77                             //    |
78                             // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
79                             //    |           ----                               ^
80                             err.span_label(
81                                 sup_origin.span(),
82                                 "...is captured here, requiring it to live as long as `'static`",
83                             );
84                         } else {
85                             err.span_label(sup_origin.span(), "...is captured here...");
86                             if return_sp < sup_origin.span() {
87                                 err.span_note(
88                                     return_sp,
89                                     "...and is required to live as long as `'static` here",
90                                 );
91                             } else {
92                                 err.span_label(
93                                     return_sp,
94                                     "...and is required to live as long as `'static` here",
95                                 );
96                             }
97                         }
98                     } else {
99                         err.span_label(
100                             return_sp,
101                             "...is captured and required to live as long as `'static` here",
102                         );
103                     }
104
105                     // FIXME: account for the need of parens in `&(dyn Trait + '_)`
106                     let consider = "consider changing the";
107                     let declare = "to declare that the";
108                     let arg = match param_info.param.pat.simple_ident() {
109                         Some(simple_ident) => format!("argument `{}`", simple_ident),
110                         None => "the argument".to_string(),
111                     };
112                     let explicit =
113                         format!("you can add an explicit `{}` lifetime bound", lifetime_name);
114                     let explicit_static =
115                         format!("explicit `'static` bound to the lifetime of {}", arg);
116                     let captures = format!("captures data from {}", arg);
117                     let add_static_bound =
118                         "alternatively, add an explicit `'static` bound to this reference";
119                     let plus_lt = format!(" + {}", lifetime_name);
120                     for fn_return in fn_returns {
121                         if fn_return.span.desugaring_kind().is_some() {
122                             // Skip `async` desugaring `impl Future`.
123                             continue;
124                         }
125                         match fn_return.kind {
126                             TyKind::OpaqueDef(item_id, _) => {
127                                 let item = self.tcx().hir().item(item_id.id);
128                                 let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind {
129                                     opaque
130                                 } else {
131                                     err.emit();
132                                     return Some(ErrorReported);
133                                 };
134
135                                 if let Some(span) = opaque
136                                     .bounds
137                                     .iter()
138                                     .filter_map(|arg| match arg {
139                                         GenericBound::Outlives(Lifetime {
140                                             name: LifetimeName::Static,
141                                             span,
142                                             ..
143                                         }) => Some(*span),
144                                         _ => None,
145                                     })
146                                     .next()
147                                 {
148                                     err.span_suggestion_verbose(
149                                         span,
150                                         &format!("{} `impl Trait`'s {}", consider, explicit_static),
151                                         lifetime_name.clone(),
152                                         Applicability::MaybeIncorrect,
153                                     );
154                                     err.span_suggestion_verbose(
155                                         param_info.param_ty_span,
156                                         add_static_bound,
157                                         param_info.param_ty.to_string(),
158                                         Applicability::MaybeIncorrect,
159                                     );
160                                 } else if let Some(_) = opaque
161                                     .bounds
162                                     .iter()
163                                     .filter_map(|arg| match arg {
164                                         GenericBound::Outlives(Lifetime { name, span, .. })
165                                             if name.ident().to_string() == lifetime_name =>
166                                         {
167                                             Some(*span)
168                                         }
169                                         _ => None,
170                                     })
171                                     .next()
172                                 {
173                                 } else {
174                                     err.span_suggestion_verbose(
175                                         fn_return.span.shrink_to_hi(),
176                                         &format!(
177                                             "{declare} `impl Trait` {captures}, {explicit}",
178                                             declare = declare,
179                                             captures = captures,
180                                             explicit = explicit,
181                                         ),
182                                         plus_lt.clone(),
183                                         Applicability::MaybeIncorrect,
184                                     );
185                                 }
186                             }
187                             TyKind::TraitObject(_, lt) => match lt.name {
188                                 LifetimeName::ImplicitObjectLifetimeDefault => {
189                                     err.span_suggestion_verbose(
190                                         fn_return.span.shrink_to_hi(),
191                                         &format!(
192                                             "{declare} trait object {captures}, {explicit}",
193                                             declare = declare,
194                                             captures = captures,
195                                             explicit = explicit,
196                                         ),
197                                         plus_lt.clone(),
198                                         Applicability::MaybeIncorrect,
199                                     );
200                                 }
201                                 name if name.ident().to_string() != lifetime_name => {
202                                     // With this check we avoid suggesting redundant bounds. This
203                                     // would happen if there are nested impl/dyn traits and only
204                                     // one of them has the bound we'd suggest already there, like
205                                     // in `impl Foo<X = dyn Bar> + '_`.
206                                     err.span_suggestion_verbose(
207                                         lt.span,
208                                         &format!("{} trait object's {}", consider, explicit_static),
209                                         lifetime_name.clone(),
210                                         Applicability::MaybeIncorrect,
211                                     );
212                                     err.span_suggestion_verbose(
213                                         param_info.param_ty_span,
214                                         add_static_bound,
215                                         param_info.param_ty.to_string(),
216                                         Applicability::MaybeIncorrect,
217                                     );
218                                 }
219                                 _ => {}
220                             },
221                             _ => {}
222                         }
223                     }
224                     err.emit();
225                     return Some(ErrorReported);
226                 }
227             }
228         }
229         None
230     }
231 }