Skip to content

Commit

Permalink
Fix incorrect suggestion for undeclared hrtb lifetimes in where clauses.
Browse files Browse the repository at this point in the history
fixes #122714
  • Loading branch information
surechen committed Apr 28, 2024
1 parent 91d5e4a commit 82f9833
Show file tree
Hide file tree
Showing 24 changed files with 406 additions and 105 deletions.
98 changes: 86 additions & 12 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::ty::fast_reject::SimplifiedType;
use crate::{errors, path_names_to_string};
use crate::{Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, Segment};
use ast::HasNodeId;
use rustc_hir::def::Namespace::{self, *};

use rustc_ast::ptr::P;
Expand All @@ -17,6 +18,7 @@ use rustc_ast::{
};
use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan,
SuggestionStyle,
Expand All @@ -31,7 +33,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::edition::Edition;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
use rustc_span::{Span, DUMMY_SP};

use rustc_middle::ty;

Expand Down Expand Up @@ -2711,8 +2713,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
self.suggest_introducing_lifetime(
&mut err,
Some(lifetime_ref.ident.name.as_str()),
|err, _, span, message, suggestion| {
err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect);
|err, _, span, message, suggestion, span_suggs| {
err.multipart_suggestion_verbose(
message,
std::iter::once((span, suggestion)).chain(span_suggs.iter().cloned()).collect(),
Applicability::MaybeIncorrect,
);
true
},
);
Expand All @@ -2723,13 +2729,20 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
&self,
err: &mut Diag<'_>,
name: Option<&str>,
suggest: impl Fn(&mut Diag<'_>, bool, Span, Cow<'static, str>, String) -> bool,
suggest: impl Fn(
&mut Diag<'_>,
bool,
Span,
Cow<'static, str>,
String,
&Vec<(Span, String)>,
) -> bool,
) {
let mut suggest_note = true;
for rib in self.lifetime_ribs.iter().rev() {
let mut should_continue = true;
match rib.kind {
LifetimeRibKind::Generics { binder: _, span, kind } => {
LifetimeRibKind::Generics { binder: node_id, span, kind } => {
// Avoid suggesting placing lifetime parameters on constant items unless the relevant
// feature is enabled. Suggest the parent item as a possible location if applicable.
if let LifetimeBinderKind::ConstItem = kind
Expand Down Expand Up @@ -2758,11 +2771,58 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
| LifetimeBinderKind::PolyTrait
| LifetimeBinderKind::WhereBound
);

let mut rm_inner_binders: FxIndexSet<Span> = Default::default();
let (span, sugg) = if span.is_empty() {
let mut binder_idents: FxIndexSet<Ident> = Default::default();
binder_idents.insert(if let Some(name) = name {
Ident::from_str(name)
} else {
Ident::from_str("'a")
});

// We need to special treat binders in following situation:
// Change `T: for<'a> Trait<T> + 'b` to `for<'a, 'b> T: Trait<T> + 'b`
// T: for<'a> Trait<T> + 'b
// ^^^^^^^ remove existing inner binder `for<'a>`
// for<'a, 'b> T: Trait<T> + 'b
// ^^^^^^^^^^^ suggest outer binder `for<'a, 'b>`
if let LifetimeBinderKind::WhereBound = kind
&& let Some(ast::WherePredicate::BoundPredicate(
ast::WhereBoundPredicate { bounded_ty, bounds, .. },
)) = self.diag_metadata.current_where_predicate
&& bounded_ty.node_id() == node_id
{
for bound in bounds {
if let ast::GenericBound::Trait(poly_trait_ref, _) = bound
&& let span = poly_trait_ref
.span
.with_hi(poly_trait_ref.trait_ref.path.span.lo())
&& !span.is_empty()
{
rm_inner_binders.insert(span);
poly_trait_ref.bound_generic_params.iter().for_each(|v| {
binder_idents.insert(v.ident);
});
}
}
}

let len = binder_idents.len();
let binders_sugg = binder_idents.into_iter().enumerate().fold(
"".to_string(),
|mut binders, (i, x)| {
binders += x.as_str();
if i != len - 1 {
binders += ", ";
}
binders
},
);
let sugg = format!(
"{}<{}>{}",
if higher_ranked { "for" } else { "" },
name.unwrap_or("'a"),
format!("{}", binders_sugg),
if higher_ranked { " " } else { "" },
);
(span, sugg)
Expand All @@ -2777,24 +2837,39 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
let sugg = format!("{}, ", name.unwrap_or("'a"));
(span, sugg)
};

if higher_ranked {
let message = Cow::from(format!(
"consider making the {} lifetime-generic with a new `{}` lifetime",
kind.descr(),
name.unwrap_or("'a"),
));
should_continue = suggest(err, true, span, message, sugg);
should_continue = suggest(
err,
true,
span,
message,
sugg,
&if rm_inner_binders.len() > 0 {
rm_inner_binders
.into_iter()
.map(|v| (v, "".to_string()))
.collect::<Vec<_>>()
} else {
vec![]
},
);
err.note_once(
"for more information on higher-ranked polymorphism, visit \
https://doc.rust-lang.org/nomicon/hrtb.html",
);
} else if let Some(name) = name {
let message =
Cow::from(format!("consider introducing lifetime `{name}` here"));
should_continue = suggest(err, false, span, message, sugg);
should_continue = suggest(err, false, span, message, sugg, &vec![]);
} else {
let message = Cow::from("consider introducing a named lifetime parameter");
should_continue = suggest(err, false, span, message, sugg);
should_continue = suggest(err, false, span, message, sugg, &vec![]);
}
}
LifetimeRibKind::Item | LifetimeRibKind::ConstParamTy => break,
Expand Down Expand Up @@ -3030,7 +3105,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
self.suggest_introducing_lifetime(
err,
None,
|err, higher_ranked, span, message, intro_sugg| {
|err, higher_ranked, span, message, intro_sugg, _| {
err.multipart_suggestion_verbose(
message,
std::iter::once((span, intro_sugg))
Expand Down Expand Up @@ -3158,7 +3233,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
self.suggest_introducing_lifetime(
err,
None,
|err, higher_ranked, span, message, intro_sugg| {
|err, higher_ranked, span, message, intro_sugg, _| {
err.multipart_suggestion_verbose(
message,
std::iter::once((span, intro_sugg))
Expand Down Expand Up @@ -3306,7 +3381,6 @@ fn mk_where_bound_predicate(
poly_trait_ref: &ast::PolyTraitRef,
ty: &Ty,
) -> Option<ast::WhereBoundPredicate> {
use rustc_span::DUMMY_SP;
let modified_segments = {
let mut segments = path.segments.clone();
let [preceding @ .., second_last, last] = segments.as_mut_slice() else {
Expand Down
9 changes: 6 additions & 3 deletions tests/ui/associated-inherent-types/issue-109299.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ error[E0261]: use of undeclared lifetime name `'d`
--> $DIR/issue-109299.rs:6:12
|
LL | impl Lexer<'d> {
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'d` here: `<'d>`
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'d` here
|
LL | impl<'d> Lexer<'d> {
| ++++

error: lifetime may not live long enough
--> $DIR/issue-109299.rs:10:1
Expand Down
17 changes: 11 additions & 6 deletions tests/ui/borrowck/generic_const_early_param.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,24 @@ LL | struct DataWrapper<'static> {
error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/generic_const_early_param.rs:6:12
|
LL | struct DataWrapper<'static> {
| - help: consider introducing lifetime `'a` here: `'a,`
LL |
LL | data: &'a [u8; Self::SIZE],
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | struct DataWrapper<'a, 'static> {
| +++

error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/generic_const_early_param.rs:11:18
|
LL | impl DataWrapper<'a> {
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'a` here: `<'a>`
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | impl<'a> DataWrapper<'a> {
| ++++

warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/generic_const_early_param.rs:1:12
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/unresolved_lifetimes_error.rs:5:13
|
LL | fn foo() -> [(); {
| - help: consider introducing lifetime `'a` here: `<'a>`
LL | let a: &'a ();
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | fn foo<'a>() -> [(); {
| ++++

error: aborting due to 1 previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/ice-unexpected-inference-var-122549.rs:11:34
|
LL | struct ConstChunksExact<'rem, T: 'a, const N: usize> {}
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'a` here: `'a,`
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | struct ConstChunksExact<'a, 'rem, T: 'a, const N: usize> {}
| +++

error[E0046]: not all trait items implemented, missing: `const_chunks_exact`
--> $DIR/ice-unexpected-inference-var-122549.rs:9:1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/ice-type-mismatch-when-copying-112824.rs:5:21
|
LL | pub struct Opcode2(&'a S);
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'a` here: `<'a>`
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | pub struct Opcode2<'a>(&'a S);
| ++++

error[E0412]: cannot find type `S` in this scope
--> $DIR/ice-type-mismatch-when-copying-112824.rs:5:24
Expand Down
16 changes: 11 additions & 5 deletions tests/ui/error-codes/E0261.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/E0261.rs:1:12
|
LL | fn foo(x: &'a str) { }
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'a` here: `<'a>`
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | fn foo<'a>(x: &'a str) { }
| ++++

error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/E0261.rs:5:9
|
LL | struct Foo {
| - help: consider introducing lifetime `'a` here: `<'a>`
LL | x: &'a str,
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | struct Foo<'a> {
| ++++

error: aborting due to 2 previous errors

Expand Down
9 changes: 6 additions & 3 deletions tests/ui/generics/generic-extern-lifetime.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/generic-extern-lifetime.rs:6:26
|
LL | pub fn life2<'b>(x: &'a i32, y: &'b i32);
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'a` here: `'a,`
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | pub fn life2<'a, 'b>(x: &'a i32, y: &'b i32);
| +++

error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/generic-extern-lifetime.rs:8:37
Expand Down
28 changes: 28 additions & 0 deletions tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#![allow(dead_code)]

trait Trait1<T>
where T: for<'a> Trait1<T> + 'b { } //~ ERROR use of undeclared lifetime name `'b`

trait Trait2<T>
where
T: B<'b> + for<'a> A<'a>, //~ ERROR use of undeclared lifetime name `'b`
{
}

trait Trait3<T>
where
T: B<'b> + for<'a> A<'a> + 'c {}
//~^ ERROR use of undeclared lifetime name `'b`
//~| ERROR use of undeclared lifetime name `'c`

trait Trait4<T>
where
T: for<'a> A<'a> + 'x + for<'b> B<'b>, //~ ERROR use of undeclared lifetime name `'x`
{
}

trait A<'a> {}
trait B<'a> {}


fn main() {}
Loading

0 comments on commit 82f9833

Please sign in to comment.