Emit appropriate suggestion when there's already 'static bound on the return type.
This commit is contained in:
parent
65e2539666
commit
7a89e93519
4 changed files with 104 additions and 51 deletions
|
@ -1590,7 +1590,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
|||
pub fn return_type_impl_trait(
|
||||
&self,
|
||||
scope_def_id: DefId,
|
||||
) -> Option<Ty> {
|
||||
) -> Option<Ty<'tcx>> {
|
||||
let ret_ty = self.type_of(scope_def_id);
|
||||
match ret_ty.sty {
|
||||
ty::FnDef(_, _) => {
|
||||
|
|
|
@ -15,7 +15,7 @@ use rustc::hir::def_id::DefId;
|
|||
use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
|
||||
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::ty::{self, TyCtxt, Region, RegionKind, RegionVid};
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc_errors::{Diagnostic, DiagnosticBuilder};
|
||||
use std::collections::VecDeque;
|
||||
|
@ -491,26 +491,67 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
fr_region: Option<Region<'tcx>>,
|
||||
outlived_fr_region: Option<Region<'tcx>>,
|
||||
) {
|
||||
if let (Some(f), Some(RegionKind::ReStatic)) = (fr_region, outlived_fr_region) {
|
||||
if let Some(TyS {
|
||||
sty: TyKind::Anon(did, _),
|
||||
if let (Some(f), Some(ty::RegionKind::ReStatic)) = (fr_region, outlived_fr_region) {
|
||||
if let Some(ty::TyS {
|
||||
sty: ty::TyKind::Anon(did, substs),
|
||||
..
|
||||
}) = self.return_type_impl_trait(infcx, f) {
|
||||
}) = infcx.tcx.is_suitable_region(f)
|
||||
.map(|r| r.def_id)
|
||||
.map(|id| infcx.tcx.return_type_impl_trait(id))
|
||||
.unwrap_or(None)
|
||||
{
|
||||
let has_static_predicate = {
|
||||
let predicates_of = infcx.tcx.predicates_of(*did);
|
||||
let bounds = predicates_of.instantiate(infcx.tcx, substs);
|
||||
|
||||
let mut found = false;
|
||||
for predicate in bounds.predicates {
|
||||
if let ty::Predicate::TypeOutlives(binder) = predicate {
|
||||
if let ty::OutlivesPredicate(
|
||||
_,
|
||||
RegionKind::ReStatic
|
||||
) = binder.skip_binder() {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found
|
||||
};
|
||||
|
||||
debug!("add_static_impl_trait_suggestion: has_static_predicate={:?}",
|
||||
has_static_predicate);
|
||||
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 `{}` and match `{}`",
|
||||
static_str, fr_name,
|
||||
),
|
||||
match fr_name {
|
||||
RegionName::Named(name) => format!("{} + {}", snippet, name),
|
||||
RegionName::Synthesized(_) => format!("{} + '_", snippet),
|
||||
},
|
||||
);
|
||||
if has_static_predicate {
|
||||
let span = self.get_span_of_named_region(infcx.tcx, f, &fr_name);
|
||||
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 definition of `{}` to require it \
|
||||
outlive `{}`",
|
||||
fr_name, static_str,
|
||||
),
|
||||
format!("{}: {}", snippet, static_str),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
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 `{}` and match `{}`",
|
||||
static_str, fr_name,
|
||||
),
|
||||
match fr_name {
|
||||
RegionName::Named(name) => format!("{} + {}", snippet, name),
|
||||
RegionName::Synthesized(_) => format!("{} + '_", snippet),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -538,15 +579,4 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let (_, span, _) = self.best_blame_constraint(mir, tcx, fr1, |r| r == fr2);
|
||||
span
|
||||
}
|
||||
|
||||
fn return_type_impl_trait<'cx>(
|
||||
&self,
|
||||
infcx: &'cx InferCtxt<'_, '_, 'tcx>,
|
||||
outlived_fr_region: Region<'tcx>,
|
||||
) -> Option<Ty<'cx>> {
|
||||
infcx.tcx.is_suitable_region(outlived_fr_region)
|
||||
.map(|r| r.def_id)
|
||||
.map(|id| infcx.tcx.return_type_impl_trait(id))
|
||||
.unwrap_or(None)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ use rustc::util::ppaux::with_highlight_region;
|
|||
use rustc_errors::DiagnosticBuilder;
|
||||
use syntax::ast::{Name, DUMMY_NODE_ID};
|
||||
use syntax::symbol::keywords;
|
||||
use syntax_pos::Span;
|
||||
use syntax_pos::symbol::InternedString;
|
||||
|
||||
/// Name of a region used in error reporting. Variants denote the source of the region name -
|
||||
|
@ -33,6 +34,14 @@ pub(crate) enum RegionName {
|
|||
Synthesized(InternedString),
|
||||
}
|
||||
|
||||
impl RegionName {
|
||||
fn as_interned_string(&self) -> &InternedString {
|
||||
match self {
|
||||
RegionName::Named(name) | RegionName::Synthesized(name) => name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RegionName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
|
@ -121,8 +130,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
match error_region {
|
||||
ty::ReEarlyBound(ebr) => {
|
||||
if ebr.has_name() {
|
||||
self.highlight_named_span(tcx, error_region, &ebr.name, diag);
|
||||
Some(RegionName::Named(ebr.name))
|
||||
let name = RegionName::Named(ebr.name);
|
||||
self.highlight_named_span(tcx, error_region, &name, diag);
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -134,8 +144,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
ty::ReFree(free_region) => match free_region.bound_region {
|
||||
ty::BoundRegion::BrNamed(_, name) => {
|
||||
let name = RegionName::Named(name);
|
||||
self.highlight_named_span(tcx, error_region, &name, diag);
|
||||
Some(RegionName::Named(name))
|
||||
Some(name)
|
||||
},
|
||||
|
||||
ty::BoundRegion::BrEnv => {
|
||||
|
@ -198,6 +209,26 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the span of a named region.
|
||||
pub(super) fn get_span_of_named_region(
|
||||
&self,
|
||||
tcx: TyCtxt<'_, '_, 'tcx>,
|
||||
error_region: &RegionKind,
|
||||
name: &RegionName,
|
||||
) -> Span {
|
||||
let scope = error_region.free_region_binding_scope(tcx);
|
||||
let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
|
||||
|
||||
let span = tcx.sess.source_map().def_span(tcx.hir.span(node));
|
||||
if let Some(param) = tcx.hir.get_generics(scope).and_then(|generics| {
|
||||
generics.get_named(name.as_interned_string())
|
||||
}) {
|
||||
param.span
|
||||
} else {
|
||||
span
|
||||
}
|
||||
}
|
||||
|
||||
/// Highlight a named span to provide context for error messages that
|
||||
/// mention that span, for example:
|
||||
///
|
||||
|
@ -216,23 +247,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
&self,
|
||||
tcx: TyCtxt<'_, '_, 'tcx>,
|
||||
error_region: &RegionKind,
|
||||
name: &InternedString,
|
||||
name: &RegionName,
|
||||
diag: &mut DiagnosticBuilder<'_>,
|
||||
) {
|
||||
let cm = tcx.sess.source_map();
|
||||
let span = self.get_span_of_named_region(tcx, error_region, name);
|
||||
|
||||
let scope = error_region.free_region_binding_scope(tcx);
|
||||
let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
|
||||
|
||||
let mut sp = cm.def_span(tcx.hir.span(node));
|
||||
if let Some(param) = tcx.hir
|
||||
.get_generics(scope)
|
||||
.and_then(|generics| generics.get_named(name))
|
||||
{
|
||||
sp = param.span;
|
||||
}
|
||||
|
||||
diag.span_label(sp, format!("lifetime `{}` defined here", name));
|
||||
diag.span_label(
|
||||
span,
|
||||
format!("lifetime `{}` defined here", name),
|
||||
);
|
||||
}
|
||||
|
||||
/// Find an argument that contains `fr` and label it with a fully
|
||||
|
|
|
@ -21,10 +21,10 @@ 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 definition of `'a` to require it outlive `'static`
|
||||
|
|
||||
LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static + 'a { x }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn with_bound<'a: 'static>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: unsatisfied lifetime constraints
|
||||
--> $DIR/must_outlive_least_region_or_bound.rs:29:5
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue