Skip to content

Commit bdeefc6

Browse files
authored
Rollup merge of rust-lang#68583 - estebank:hrlt, r=oli-obk
Account for HR lifetimes when suggesting introduction of named lifetime ``` error[E0106]: missing lifetime specifier --> src/test/ui/suggestions/fn-missing-lifetime-in-item.rs:2:32 | 2 | struct S2<F: Fn(&i32, &i32) -> &i32>(F); | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or argument 2 = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html help: consider making the bound lifetime-generic with a new `'a` lifetime | 2 | struct S2<F: for<'a> Fn(&'a i32, &'a i32) -> &'a i32>(F); | ^^^^^^^ ^^^^^^^ ^^^^^^^ ^^^ help: consider introducing a named lifetime parameter | 2 | struct S2<'a, F: Fn(&'a i32, &'a i32) -> &'a i32>(F);= | ^^^ ^^^^^^^ ^^^^^^^ ^^^ ``` Follow up to rust-lang#68267. Addresses the diagnostics part of rust-lang#49287.
2 parents 4ff8fb9 + 4310b74 commit bdeefc6

33 files changed

+558
-190
lines changed

src/librustc_hir/hir.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2260,10 +2260,10 @@ impl TraitRef<'_> {
22602260

22612261
#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
22622262
pub struct PolyTraitRef<'hir> {
2263-
/// The `'a` in `<'a> Foo<&'a T>`.
2263+
/// The `'a` in `for<'a> Foo<&'a T>`.
22642264
pub bound_generic_params: &'hir [GenericParam<'hir>],
22652265

2266-
/// The `Foo<&'a T>` in `<'a> Foo<&'a T>`.
2266+
/// The `Foo<&'a T>` in `for<'a> Foo<&'a T>`.
22672267
pub trait_ref: TraitRef<'hir>,
22682268

22692269
pub span: Span,

src/librustc_resolve/diagnostics.rs

+231-62
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use syntax::ast::{self, Ident, Path};
2020
use syntax::util::lev_distance::find_best_match_for_name;
2121

2222
use crate::imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver};
23+
use crate::lifetimes::{ElisionFailureInfo, LifetimeContext};
2324
use crate::path_names_to_string;
2425
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind};
2526
use crate::{BindingError, CrateLint, HasGenericParams, LegacyScope, Module, ModuleOrUniformRoot};
@@ -48,6 +49,40 @@ crate struct ImportSuggestion {
4849
pub path: Path,
4950
}
5051

52+
crate enum MissingLifetimeSpot<'tcx> {
53+
Generics(&'tcx hir::Generics<'tcx>),
54+
HigherRanked { span: Span, span_type: ForLifetimeSpanType },
55+
}
56+
57+
crate enum ForLifetimeSpanType {
58+
BoundEmpty,
59+
BoundTail,
60+
TypeEmpty,
61+
TypeTail,
62+
}
63+
64+
impl ForLifetimeSpanType {
65+
crate fn descr(&self) -> &'static str {
66+
match self {
67+
Self::BoundEmpty | Self::BoundTail => "bound",
68+
Self::TypeEmpty | Self::TypeTail => "type",
69+
}
70+
}
71+
72+
crate fn suggestion(&self, sugg: &str) -> String {
73+
match self {
74+
Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg),
75+
Self::BoundTail | Self::TypeTail => format!(", {}", sugg),
76+
}
77+
}
78+
}
79+
80+
impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> {
81+
fn into(self) -> MissingLifetimeSpot<'tcx> {
82+
MissingLifetimeSpot::Generics(self)
83+
}
84+
}
85+
5186
/// Adjust the impl span so that just the `impl` keyword is taken by removing
5287
/// everything after `<` (`"impl<T> Iterator for A<T> {}" -> "impl"`) and
5388
/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`).
@@ -1457,72 +1492,206 @@ crate fn show_candidates(
14571492
}
14581493
}
14591494

1460-
crate fn report_missing_lifetime_specifiers(
1461-
sess: &Session,
1462-
span: Span,
1463-
count: usize,
1464-
) -> DiagnosticBuilder<'_> {
1465-
struct_span_err!(sess, span, E0106, "missing lifetime specifier{}", pluralize!(count))
1466-
}
1495+
impl<'tcx> LifetimeContext<'_, 'tcx> {
1496+
crate fn report_missing_lifetime_specifiers(
1497+
&self,
1498+
span: Span,
1499+
count: usize,
1500+
) -> DiagnosticBuilder<'tcx> {
1501+
struct_span_err!(
1502+
self.tcx.sess,
1503+
span,
1504+
E0106,
1505+
"missing lifetime specifier{}",
1506+
pluralize!(count)
1507+
)
1508+
}
14671509

1468-
crate fn add_missing_lifetime_specifiers_label(
1469-
err: &mut DiagnosticBuilder<'_>,
1470-
span: Span,
1471-
count: usize,
1472-
lifetime_names: &FxHashSet<ast::Ident>,
1473-
snippet: Option<&str>,
1474-
missing_named_lifetime_spots: &[&hir::Generics<'_>],
1475-
) {
1476-
if count > 1 {
1477-
err.span_label(span, format!("expected {} lifetime parameters", count));
1478-
} else {
1479-
let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| {
1480-
err.span_suggestion(
1481-
span,
1482-
"consider using the named lifetime",
1483-
sugg,
1484-
Applicability::MaybeIncorrect,
1485-
);
1486-
};
1487-
let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg| {
1488-
err.span_label(span, "expected named lifetime parameter");
1489-
1490-
if let Some(generics) = missing_named_lifetime_spots.iter().last() {
1491-
let mut introduce_suggestion = vec![];
1492-
introduce_suggestion.push(match &generics.params {
1493-
[] => (generics.span, "<'lifetime>".to_string()),
1494-
[param, ..] => (param.span.shrink_to_lo(), "'lifetime, ".to_string()),
1495-
});
1496-
introduce_suggestion.push((span, sugg));
1497-
err.multipart_suggestion(
1498-
"consider introducing a named lifetime parameter",
1499-
introduce_suggestion,
1500-
Applicability::MaybeIncorrect,
1501-
);
1510+
crate fn emit_undeclared_lifetime_error(&self, lifetime_ref: &hir::Lifetime) {
1511+
let mut err = struct_span_err!(
1512+
self.tcx.sess,
1513+
lifetime_ref.span,
1514+
E0261,
1515+
"use of undeclared lifetime name `{}`",
1516+
lifetime_ref
1517+
);
1518+
err.span_label(lifetime_ref.span, "undeclared lifetime");
1519+
for missing in &self.missing_named_lifetime_spots {
1520+
match missing {
1521+
MissingLifetimeSpot::Generics(generics) => {
1522+
let (span, sugg) = if let Some(param) = generics
1523+
.params
1524+
.iter()
1525+
.filter(|p| match p.kind {
1526+
hir::GenericParamKind::Type {
1527+
synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
1528+
..
1529+
} => false,
1530+
_ => true,
1531+
})
1532+
.next()
1533+
{
1534+
(param.span.shrink_to_lo(), format!("{}, ", lifetime_ref))
1535+
} else {
1536+
(generics.span, format!("<{}>", lifetime_ref))
1537+
};
1538+
err.span_suggestion(
1539+
span,
1540+
&format!("consider introducing lifetime `{}` here", lifetime_ref),
1541+
sugg,
1542+
Applicability::MaybeIncorrect,
1543+
);
1544+
}
1545+
MissingLifetimeSpot::HigherRanked { span, span_type } => {
1546+
err.span_suggestion(
1547+
*span,
1548+
&format!(
1549+
"consider making the {} lifetime-generic with a new `{}` lifetime",
1550+
span_type.descr(),
1551+
lifetime_ref
1552+
),
1553+
span_type.suggestion(&lifetime_ref.to_string()),
1554+
Applicability::MaybeIncorrect,
1555+
);
1556+
err.note(
1557+
"for more information on higher-ranked polymorphism, visit \
1558+
https://doc.rust-lang.org/nomicon/hrtb.html",
1559+
);
1560+
}
15021561
}
1503-
};
1562+
}
1563+
err.emit();
1564+
}
15041565

1505-
match (lifetime_names.len(), lifetime_names.iter().next(), snippet) {
1506-
(1, Some(name), Some("&")) => {
1507-
suggest_existing(err, format!("&{} ", name));
1508-
}
1509-
(1, Some(name), Some("'_")) => {
1510-
suggest_existing(err, name.to_string());
1511-
}
1512-
(1, Some(name), Some(snippet)) if !snippet.ends_with(">") => {
1513-
suggest_existing(err, format!("{}<{}>", snippet, name));
1514-
}
1515-
(0, _, Some("&")) => {
1516-
suggest_new(err, "&'lifetime ".to_string());
1517-
}
1518-
(0, _, Some("'_")) => {
1519-
suggest_new(err, "'lifetime".to_string());
1520-
}
1521-
(0, _, Some(snippet)) if !snippet.ends_with(">") => {
1522-
suggest_new(err, format!("{}<'lifetime>", snippet));
1566+
crate fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool {
1567+
if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res {
1568+
if [
1569+
self.tcx.lang_items().fn_once_trait(),
1570+
self.tcx.lang_items().fn_trait(),
1571+
self.tcx.lang_items().fn_mut_trait(),
1572+
]
1573+
.contains(&Some(did))
1574+
{
1575+
let (span, span_type) = match &trait_ref.bound_generic_params {
1576+
[] => (trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty),
1577+
[.., bound] => (bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail),
1578+
};
1579+
self.missing_named_lifetime_spots
1580+
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
1581+
return true;
15231582
}
1524-
_ => {
1525-
err.span_label(span, "expected lifetime parameter");
1583+
};
1584+
false
1585+
}
1586+
1587+
crate fn add_missing_lifetime_specifiers_label(
1588+
&self,
1589+
err: &mut DiagnosticBuilder<'_>,
1590+
span: Span,
1591+
count: usize,
1592+
lifetime_names: &FxHashSet<ast::Ident>,
1593+
params: &[ElisionFailureInfo],
1594+
) {
1595+
if count > 1 {
1596+
err.span_label(span, format!("expected {} lifetime parameters", count));
1597+
} else {
1598+
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
1599+
let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| {
1600+
err.span_suggestion(
1601+
span,
1602+
"consider using the named lifetime",
1603+
sugg,
1604+
Applicability::MaybeIncorrect,
1605+
);
1606+
};
1607+
let suggest_new =
1608+
|err: &mut DiagnosticBuilder<'_>, sugg: &str| {
1609+
err.span_label(span, "expected named lifetime parameter");
1610+
1611+
for missing in self.missing_named_lifetime_spots.iter().rev() {
1612+
let mut introduce_suggestion = vec![];
1613+
let msg;
1614+
let should_break;
1615+
introduce_suggestion.push(match missing {
1616+
MissingLifetimeSpot::Generics(generics) => {
1617+
msg = "consider introducing a named lifetime parameter".to_string();
1618+
should_break = true;
1619+
if let Some(param) = generics.params.iter().filter(|p| match p.kind {
1620+
hir::GenericParamKind::Type {
1621+
synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
1622+
..
1623+
} => false,
1624+
_ => true,
1625+
}).next() {
1626+
(param.span.shrink_to_lo(), "'a, ".to_string())
1627+
} else {
1628+
(generics.span, "<'a>".to_string())
1629+
}
1630+
}
1631+
MissingLifetimeSpot::HigherRanked { span, span_type } => {
1632+
msg = format!(
1633+
"consider making the {} lifetime-generic with a new `'a` lifetime",
1634+
span_type.descr(),
1635+
);
1636+
should_break = false;
1637+
err.note(
1638+
"for more information on higher-ranked polymorphism, visit \
1639+
https://doc.rust-lang.org/nomicon/hrtb.html",
1640+
);
1641+
(*span, span_type.suggestion("'a"))
1642+
}
1643+
});
1644+
for param in params {
1645+
if let Ok(snippet) =
1646+
self.tcx.sess.source_map().span_to_snippet(param.span)
1647+
{
1648+
if snippet.starts_with("&") && !snippet.starts_with("&'") {
1649+
introduce_suggestion
1650+
.push((param.span, format!("&'a {}", &snippet[1..])));
1651+
} else if snippet.starts_with("&'_ ") {
1652+
introduce_suggestion
1653+
.push((param.span, format!("&'a {}", &snippet[4..])));
1654+
}
1655+
}
1656+
}
1657+
introduce_suggestion.push((span, sugg.to_string()));
1658+
err.multipart_suggestion(
1659+
&msg,
1660+
introduce_suggestion,
1661+
Applicability::MaybeIncorrect,
1662+
);
1663+
if should_break {
1664+
break;
1665+
}
1666+
}
1667+
};
1668+
1669+
match (
1670+
lifetime_names.len(),
1671+
lifetime_names.iter().next(),
1672+
snippet.as_ref().map(|s| s.as_str()),
1673+
) {
1674+
(1, Some(name), Some("&")) => {
1675+
suggest_existing(err, format!("&{} ", name));
1676+
}
1677+
(1, Some(name), Some("'_")) => {
1678+
suggest_existing(err, name.to_string());
1679+
}
1680+
(1, Some(name), Some(snippet)) if !snippet.ends_with(">") => {
1681+
suggest_existing(err, format!("{}<{}>", snippet, name));
1682+
}
1683+
(0, _, Some("&")) => {
1684+
suggest_new(err, "&'a ");
1685+
}
1686+
(0, _, Some("'_")) => {
1687+
suggest_new(err, "'a");
1688+
}
1689+
(0, _, Some(snippet)) if !snippet.ends_with(">") => {
1690+
suggest_new(err, &format!("{}<'a>", snippet));
1691+
}
1692+
_ => {
1693+
err.span_label(span, "expected lifetime parameter");
1694+
}
15261695
}
15271696
}
15281697
}

0 commit comments

Comments
 (0)