diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index 2d8f782f748e..bf3b5e2a17c6 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -17,14 +17,17 @@ use rustc::infer::InferCtxt; use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind}; use rustc::ty::{TyCtxt, Ty, TyS, TyKind, Region, RegionKind, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; -use rustc_errors::Diagnostic; +use rustc_errors::{Diagnostic, DiagnosticBuilder}; use std::collections::VecDeque; use std::fmt; +use syntax::symbol::keywords; use syntax_pos::Span; mod region_name; mod var_name; +use self::region_name::RegionName; + /// Constraints that are considered interesting can be categorized to /// determine why they are interesting. Order of variants indicates /// sort order of the category, thereby influencing diagnostic output. @@ -473,27 +476,44 @@ impl<'tcx> RegionInferenceContext<'tcx> { }, } + self.add_static_impl_trait_suggestion( + infcx, &mut diag, fr_name, fr_region, outlived_fr_region + ); + + diag.buffer(errors_buffer); + } + + fn add_static_impl_trait_suggestion( + &self, + infcx: &InferCtxt<'_, '_, 'tcx>, + diag: &mut DiagnosticBuilder<'_>, + fr_name: RegionName, + fr_region: Option>, + outlived_fr_region: Option>, + ) { if let (Some(f), Some(RegionKind::ReStatic)) = (fr_region, outlived_fr_region) { if let Some(TyS { sty: TyKind::Anon(did, _), .. }) = self.return_type_impl_trait(infcx, f) { + let static_str = keywords::StaticLifetime.name(); let span = infcx.tcx.def_span(*did); if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) { diag.span_suggestion( span, &format!( "you can add a constraint to the return type to make it last \ - less than `'static` and match {}", - fr_name, + less than `{}` and match `{}`", + static_str, fr_name, ), - format!("{} + {}", snippet, fr_name), + match fr_name { + RegionName::Named(name) => format!("{} + {}", snippet, name), + RegionName::Synthesized(_) => format!("{} + '_", snippet), + }, ); } } } - - diag.buffer(errors_buffer); } // Finds some region R such that `fr1: R` and `R` is live at diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs index 5ae123bdc18d..136f660fe8da 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::fmt::{self, Display}; use borrow_check::nll::region_infer::RegionInferenceContext; use borrow_check::nll::universal_regions::DefiningTy; use borrow_check::nll::ToRegionVid; @@ -23,6 +24,24 @@ use syntax::ast::{Name, DUMMY_NODE_ID}; use syntax::symbol::keywords; use syntax_pos::symbol::InternedString; +/// Name of a region used in error reporting. Variants denote the source of the region name - +/// whether it was synthesized for the error message and therefore should not be used in +/// suggestions; or whether it was found from the region. +#[derive(Debug)] +pub(crate) enum RegionName { + Named(InternedString), + Synthesized(InternedString), +} + +impl Display for RegionName { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + RegionName::Named(name) | RegionName::Synthesized(name) => + write!(f, "{}", name), + } + } +} + impl<'tcx> RegionInferenceContext<'tcx> { /// Maps from an internal MIR region vid to something that we can /// report to the user. In some cases, the region vids will map @@ -57,7 +76,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr: RegionVid, counter: &mut usize, diag: &mut DiagnosticBuilder, - ) -> InternedString { + ) -> RegionName { debug!("give_region_a_name(fr={:?}, counter={})", fr, counter); assert!(self.universal_regions.is_universal_region(fr)); @@ -95,7 +114,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr: RegionVid, counter: &mut usize, diag: &mut DiagnosticBuilder<'_>, - ) -> Option { + ) -> Option { let error_region = self.to_error_region(fr)?; debug!("give_region_a_name: error_region = {:?}", error_region); @@ -103,19 +122,21 @@ impl<'tcx> RegionInferenceContext<'tcx> { ty::ReEarlyBound(ebr) => { if ebr.has_name() { self.highlight_named_span(tcx, error_region, &ebr.name, diag); - Some(ebr.name) + Some(RegionName::Named(ebr.name)) } else { None } } - ty::ReStatic => Some(keywords::StaticLifetime.name().as_interned_str()), + ty::ReStatic => Some(RegionName::Named( + keywords::StaticLifetime.name().as_interned_str() + )), ty::ReFree(free_region) => match free_region.bound_region { ty::BoundRegion::BrNamed(_, name) => { self.highlight_named_span(tcx, error_region, &name, diag); - Some(name) - } + Some(RegionName::Named(name)) + }, ty::BoundRegion::BrEnv => { let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).expect("non-local mir"); @@ -132,7 +153,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { let region_name = self.synthesize_region_name(counter); diag.span_label( args_span, - format!("lifetime `{}` represents this closure's body", region_name), + format!( + "lifetime `{}` represents this closure's body", + region_name + ), ); let closure_kind_ty = substs.closure_kind_ty(def_id, tcx); @@ -227,7 +251,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr: RegionVid, counter: &mut usize, diag: &mut DiagnosticBuilder<'_>, - ) -> Option { + ) -> Option { let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs(); let argument_index = self.get_argument_index_for_region(infcx.tcx, fr)?; @@ -259,7 +283,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { argument_index: usize, counter: &mut usize, diag: &mut DiagnosticBuilder<'_>, - ) -> Option { + ) -> Option { let mir_node_id = infcx.tcx.hir.as_local_node_id(mir_def_id)?; let fn_decl = infcx.tcx.hir.fn_decl(mir_node_id)?; let argument_hir_ty: &hir::Ty = &fn_decl.inputs[argument_index]; @@ -306,7 +330,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { argument_ty: Ty<'tcx>, counter: &mut usize, diag: &mut DiagnosticBuilder<'_>, - ) -> Option { + ) -> Option { let type_name = with_highlight_region(needle_fr, *counter, || { infcx.extract_type_name(&argument_ty) }); @@ -361,7 +385,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { argument_hir_ty: &hir::Ty, counter: &mut usize, diag: &mut DiagnosticBuilder<'_>, - ) -> Option { + ) -> Option { let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty)> = &mut Vec::new(); search_stack.push((argument_ty, argument_hir_ty)); @@ -457,7 +481,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { counter: &mut usize, diag: &mut DiagnosticBuilder<'_>, search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>, - ) -> Option { + ) -> Option { // Did the user give explicit arguments? (e.g., `Foo<..>`) let args = last_segment.args.as_ref()?; let lifetime = self.try_match_adt_and_generic_args(substs, needle_fr, args, search_stack)?; @@ -467,7 +491,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { | hir::LifetimeName::Underscore => { let region_name = self.synthesize_region_name(counter); let ampersand_span = lifetime.span; - diag.span_label(ampersand_span, format!("let's call this `{}`", region_name)); + diag.span_label( + ampersand_span, + format!("let's call this `{}`", region_name) + ); return Some(region_name); } @@ -544,7 +571,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr: RegionVid, counter: &mut usize, diag: &mut DiagnosticBuilder<'_>, - ) -> Option { + ) -> Option { let upvar_index = self.get_upvar_index_for_region(tcx, fr)?; let (upvar_name, upvar_span) = self.get_upvar_name_and_span_for_region(tcx, mir, upvar_index); @@ -573,7 +600,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr: RegionVid, counter: &mut usize, diag: &mut DiagnosticBuilder<'_>, - ) -> Option { + ) -> Option { let tcx = infcx.tcx; let return_ty = self.universal_regions.unnormalized_output_ty; @@ -622,10 +649,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Create a synthetic region named `'1`, incrementing the /// counter. - fn synthesize_region_name(&self, counter: &mut usize) -> InternedString { + fn synthesize_region_name(&self, counter: &mut usize) -> RegionName { let c = *counter; *counter += 1; - Name::intern(&format!("'{:?}", c)).as_interned_str() + RegionName::Synthesized(Name::intern(&format!("'{:?}", c)).as_interned_str()) } } diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr index e372bd33aa2d..e95fc7663710 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr @@ -11,7 +11,7 @@ error: unsatisfied lifetime constraints | LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` -help: you can add a constraint to the return type to make it last less than `'static` and match 'a +help: you can add a constraint to the return type to make it last less than `'static` and match `'a` | LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ error: unsatisfied lifetime constraints | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` -help: you can add a constraint to the return type to make it last less than `'static` and match 'a +help: you can add a constraint to the return type to make it last less than `'static` and match `'a` | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static + 'a { x } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^