1
Fork 0

Emit appropriate suggestion when there's already 'static bound on the return type.

This commit is contained in:
David Wood 2018-09-04 18:56:14 +02:00
parent 65e2539666
commit 7a89e93519
No known key found for this signature in database
GPG key ID: 01760B4F9F53F154
4 changed files with 104 additions and 51 deletions

View file

@ -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(_, _) => {

View file

@ -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)
}
}

View file

@ -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

View file

@ -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