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