Skip to content

Commit

Permalink
Emit appropriate suggestion when there's already 'static bound on the…
Browse files Browse the repository at this point in the history
… return type.
  • Loading branch information
davidtwco committed Sep 13, 2018
1 parent 65e2539 commit 7a89e93
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 51 deletions.
2 changes: 1 addition & 1 deletion src/librustc/ty/context.rs
Expand Up @@ -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(_, _) => {
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
},
);
}
}
}
}
Expand Down Expand Up @@ -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)
}
}
Expand Up @@ -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 -
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand All @@ -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 => {
Expand Down Expand Up @@ -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:
///
Expand All @@ -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 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;
}
let span = self.get_span_of_named_region(tcx, error_region, name);

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
Expand Down
Expand Up @@ -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
Expand Down

0 comments on commit 7a89e93

Please sign in to comment.