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