Skip to content

Commit 6e1f7b5

Browse files
committed
Auto merge of #121587 - ShoyuVanilla:fix-issue-121267, r=TaKO8Ki
Fix bad span for explicit lifetime suggestions Fixes #121267 Current explicit lifetime suggestions are not showing correct spans for some lifetimes - e.g. elided lifetime generic parameters; This should be done correctly regarding elided lifetime kind like the following code https://github.com/rust-lang/rust/blob/43fdd4916d19f4004e23d422b5547637ad67ab21/compiler/rustc_resolve/src/late/diagnostics.rs#L3015-L3044
2 parents 6a6cd65 + c270a42 commit 6e1f7b5

File tree

11 files changed

+167
-83
lines changed

11 files changed

+167
-83
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+36-27
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxt, StashKey};
5555
use rustc_hir as hir;
5656
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
5757
use rustc_hir::def_id::{LocalDefId, LocalDefIdMap, CRATE_DEF_ID, LOCAL_CRATE};
58-
use rustc_hir::{ConstArg, GenericArg, ItemLocalMap, ParamName, TraitCandidate};
58+
use rustc_hir::{
59+
ConstArg, GenericArg, ItemLocalMap, MissingLifetimeKind, ParamName, TraitCandidate,
60+
};
5961
use rustc_index::{Idx, IndexSlice, IndexVec};
6062
use rustc_macros::extension;
6163
use rustc_middle::span_bug;
@@ -797,7 +799,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
797799
LifetimeRes::Param { .. } => {
798800
(hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit)
799801
}
800-
LifetimeRes::Fresh { param, .. } => {
802+
LifetimeRes::Fresh { param, kind, .. } => {
801803
// Late resolution delegates to us the creation of the `LocalDefId`.
802804
let _def_id = self.create_def(
803805
self.current_hir_id_owner.def_id,
@@ -808,7 +810,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
808810
);
809811
debug!(?_def_id);
810812

811-
(hir::ParamName::Fresh, hir::LifetimeParamKind::Elided)
813+
(hir::ParamName::Fresh, hir::LifetimeParamKind::Elided(kind))
812814
}
813815
LifetimeRes::Static | LifetimeRes::Error => return None,
814816
res => panic!(
@@ -1605,13 +1607,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16051607

16061608
for lifetime in captured_lifetimes_to_duplicate {
16071609
let res = self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error);
1608-
let old_def_id = match res {
1609-
LifetimeRes::Param { param: old_def_id, binder: _ } => old_def_id,
1610+
let (old_def_id, missing_kind) = match res {
1611+
LifetimeRes::Param { param: old_def_id, binder: _ } => (old_def_id, None),
16101612

1611-
LifetimeRes::Fresh { param, binder: _ } => {
1613+
LifetimeRes::Fresh { param, kind, .. } => {
16121614
debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
16131615
if let Some(old_def_id) = self.orig_opt_local_def_id(param) {
1614-
old_def_id
1616+
(old_def_id, Some(kind))
16151617
} else {
16161618
self.dcx()
16171619
.span_delayed_bug(lifetime.ident.span, "no def-id for fresh lifetime");
@@ -1651,6 +1653,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16511653
duplicated_lifetime_node_id,
16521654
duplicated_lifetime_def_id,
16531655
self.lower_ident(lifetime.ident),
1656+
missing_kind,
16541657
));
16551658

16561659
// Now make an arg that we can use for the generic params of the opaque tykind.
@@ -1668,27 +1671,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16681671
let bounds = this
16691672
.with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this));
16701673

1671-
let generic_params = this.arena.alloc_from_iter(
1672-
synthesized_lifetime_definitions.iter().map(|&(new_node_id, new_def_id, ident)| {
1673-
let hir_id = this.lower_node_id(new_node_id);
1674-
let (name, kind) = if ident.name == kw::UnderscoreLifetime {
1675-
(hir::ParamName::Fresh, hir::LifetimeParamKind::Elided)
1676-
} else {
1677-
(hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit)
1678-
};
1674+
let generic_params =
1675+
this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map(
1676+
|&(new_node_id, new_def_id, ident, missing_kind)| {
1677+
let hir_id = this.lower_node_id(new_node_id);
1678+
let (name, kind) = if ident.name == kw::UnderscoreLifetime {
1679+
(
1680+
hir::ParamName::Fresh,
1681+
hir::LifetimeParamKind::Elided(
1682+
missing_kind.unwrap_or(MissingLifetimeKind::Underscore),
1683+
),
1684+
)
1685+
} else {
1686+
(hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit)
1687+
};
16791688

1680-
hir::GenericParam {
1681-
hir_id,
1682-
def_id: new_def_id,
1683-
name,
1684-
span: ident.span,
1685-
pure_wrt_drop: false,
1686-
kind: hir::GenericParamKind::Lifetime { kind },
1687-
colon_span: None,
1688-
source: hir::GenericParamSource::Generics,
1689-
}
1690-
}),
1691-
);
1689+
hir::GenericParam {
1690+
hir_id,
1691+
def_id: new_def_id,
1692+
name,
1693+
span: ident.span,
1694+
pure_wrt_drop: false,
1695+
kind: hir::GenericParamKind::Lifetime { kind },
1696+
colon_span: None,
1697+
source: hir::GenericParamSource::Generics,
1698+
}
1699+
},
1700+
));
16921701
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
16931702

16941703
let lifetime_mapping = self.arena.alloc_slice(&synthesized_lifetime_args);

compiler/rustc_hir/src/def.rs

+2
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,8 @@ pub enum LifetimeRes {
810810
param: NodeId,
811811
/// Id of the introducing place. See `Param`.
812812
binder: NodeId,
813+
/// Kind of elided lifetime
814+
kind: hir::MissingLifetimeKind,
813815
},
814816
/// This variant is used for anonymous lifetimes that we did not resolve during
815817
/// late resolution. Those lifetimes will be inferred by typechecking.

compiler/rustc_hir/src/hir.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,18 @@ impl GenericBound<'_> {
456456

457457
pub type GenericBounds<'hir> = &'hir [GenericBound<'hir>];
458458

459+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable_Generic, Debug)]
460+
pub enum MissingLifetimeKind {
461+
/// An explicit `'_`.
462+
Underscore,
463+
/// An elided lifetime `&' ty`.
464+
Ampersand,
465+
/// An elided lifetime in brackets with written brackets.
466+
Comma,
467+
/// An elided lifetime with elided brackets.
468+
Brackets,
469+
}
470+
459471
#[derive(Copy, Clone, Debug, HashStable_Generic)]
460472
pub enum LifetimeParamKind {
461473
// Indicates that the lifetime definition was explicitly declared (e.g., in
@@ -464,7 +476,7 @@ pub enum LifetimeParamKind {
464476

465477
// Indication that the lifetime was elided (e.g., in both cases in
466478
// `fn foo(x: &u8) -> &'_ u8 { x }`).
467-
Elided,
479+
Elided(MissingLifetimeKind),
468480

469481
// Indication that the lifetime name was somehow in error.
470482
Error,
@@ -512,7 +524,7 @@ impl<'hir> GenericParam<'hir> {
512524
///
513525
/// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information.
514526
pub fn is_elided_lifetime(&self) -> bool {
515-
matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided })
527+
matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(_) })
516528
}
517529
}
518530

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1305,7 +1305,7 @@ fn compare_number_of_generics<'tcx>(
13051305
.iter()
13061306
.filter(|p| match p.kind {
13071307
hir::GenericParamKind::Lifetime {
1308-
kind: hir::LifetimeParamKind::Elided,
1308+
kind: hir::LifetimeParamKind::Elided(_),
13091309
} => {
13101310
// A fn can have an arbitrary number of extra elided lifetimes for the
13111311
// same signature.

compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs

+55-16
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnosti
1313
use rustc_hir::def_id::DefId;
1414
use rustc_hir::intravisit::{walk_ty, Visitor};
1515
use rustc_hir::{
16-
self as hir, GenericBound, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, Node,
17-
TyKind,
16+
self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime,
17+
LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind,
1818
};
1919
use rustc_middle::ty::{
2020
self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
@@ -355,20 +355,8 @@ pub fn suggest_new_region_bound(
355355
// introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
356356
if let Some(id) = scope_def_id
357357
&& let Some(generics) = tcx.hir().get_generics(id)
358-
&& let mut spans_suggs = generics
359-
.params
360-
.iter()
361-
.filter(|p| p.is_elided_lifetime())
362-
.map(|p| {
363-
if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) {
364-
// Ampersand (elided without '_)
365-
(p.span.shrink_to_hi(), format!("{name} "))
366-
} else {
367-
// Underscore (elided with '_)
368-
(p.span, name.to_string())
369-
}
370-
})
371-
.collect::<Vec<_>>()
358+
&& let mut spans_suggs =
359+
make_elided_region_spans_suggs(name, generics.params.iter())
372360
&& spans_suggs.len() > 1
373361
{
374362
let use_lt = if existing_lt_name == None {
@@ -430,6 +418,57 @@ pub fn suggest_new_region_bound(
430418
}
431419
}
432420

421+
fn make_elided_region_spans_suggs<'a>(
422+
name: &str,
423+
generic_params: impl Iterator<Item = &'a GenericParam<'a>>,
424+
) -> Vec<(Span, String)> {
425+
let mut spans_suggs = Vec::new();
426+
let mut bracket_span = None;
427+
let mut consecutive_brackets = 0;
428+
429+
let mut process_consecutive_brackets =
430+
|span: Option<Span>, spans_suggs: &mut Vec<(Span, String)>| {
431+
if span
432+
.is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span))
433+
{
434+
consecutive_brackets += 1;
435+
} else if let Some(bracket_span) = bracket_span.take() {
436+
let sugg = std::iter::once("<")
437+
.chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", "))
438+
.chain([">"])
439+
.collect();
440+
spans_suggs.push((bracket_span.shrink_to_hi(), sugg));
441+
consecutive_brackets = 0;
442+
}
443+
bracket_span = span;
444+
};
445+
446+
for p in generic_params {
447+
if let GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(kind) } = p.kind {
448+
match kind {
449+
MissingLifetimeKind::Underscore => {
450+
process_consecutive_brackets(None, &mut spans_suggs);
451+
spans_suggs.push((p.span, name.to_string()))
452+
}
453+
MissingLifetimeKind::Ampersand => {
454+
process_consecutive_brackets(None, &mut spans_suggs);
455+
spans_suggs.push((p.span.shrink_to_hi(), format!("{name} ")));
456+
}
457+
MissingLifetimeKind::Comma => {
458+
process_consecutive_brackets(None, &mut spans_suggs);
459+
spans_suggs.push((p.span.shrink_to_hi(), format!("{name}, ")));
460+
}
461+
MissingLifetimeKind::Brackets => {
462+
process_consecutive_brackets(Some(p.span), &mut spans_suggs);
463+
}
464+
}
465+
}
466+
}
467+
process_consecutive_brackets(None, &mut spans_suggs);
468+
469+
spans_suggs
470+
}
471+
433472
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
434473
pub fn get_impl_ident_and_self_ty_from_trait(
435474
tcx: TyCtxt<'tcx>,

compiler/rustc_infer/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#![feature(extend_one)]
2525
#![feature(let_chains)]
2626
#![feature(if_let_guard)]
27+
#![feature(iter_intersperse)]
2728
#![feature(iterator_try_collect)]
2829
#![feature(try_blocks)]
2930
#![feature(yeet_expr)]

compiler/rustc_resolve/src/late.rs

+21-23
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_errors::{
2222
use rustc_hir::def::Namespace::{self, *};
2323
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
2424
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
25-
use rustc_hir::{PrimTy, TraitCandidate};
25+
use rustc_hir::{MissingLifetimeKind, PrimTy, TraitCandidate};
2626
use rustc_middle::middle::resolve_bound_vars::Set1;
2727
use rustc_middle::{bug, span_bug};
2828
use rustc_session::config::{CrateType, ResolveDocLinks};
@@ -44,9 +44,7 @@ type Res = def::Res<NodeId>;
4444

4545
type IdentMap<T> = FxHashMap<Ident, T>;
4646

47-
use diagnostics::{
48-
ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime, MissingLifetimeKind,
49-
};
47+
use diagnostics::{ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime};
5048

5149
#[derive(Copy, Clone, Debug)]
5250
struct BindingInfo {
@@ -1637,22 +1635,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
16371635
fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) {
16381636
debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
16391637

1640-
let missing_lifetime = MissingLifetime {
1641-
id: lifetime.id,
1642-
span: lifetime.ident.span,
1643-
kind: if elided {
1644-
MissingLifetimeKind::Ampersand
1645-
} else {
1646-
MissingLifetimeKind::Underscore
1647-
},
1648-
count: 1,
1649-
};
1638+
let kind =
1639+
if elided { MissingLifetimeKind::Ampersand } else { MissingLifetimeKind::Underscore };
1640+
let missing_lifetime =
1641+
MissingLifetime { id: lifetime.id, span: lifetime.ident.span, kind, count: 1 };
16501642
let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
16511643
for (i, rib) in self.lifetime_ribs.iter().enumerate().rev() {
16521644
debug!(?rib.kind);
16531645
match rib.kind {
16541646
LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
1655-
let res = self.create_fresh_lifetime(lifetime.ident, binder);
1647+
let res = self.create_fresh_lifetime(lifetime.ident, binder, kind);
16561648
self.record_lifetime_res(lifetime.id, res, elision_candidate);
16571649
return;
16581650
}
@@ -1744,13 +1736,18 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
17441736
}
17451737

17461738
#[instrument(level = "debug", skip(self))]
1747-
fn create_fresh_lifetime(&mut self, ident: Ident, binder: NodeId) -> LifetimeRes {
1739+
fn create_fresh_lifetime(
1740+
&mut self,
1741+
ident: Ident,
1742+
binder: NodeId,
1743+
kind: MissingLifetimeKind,
1744+
) -> LifetimeRes {
17481745
debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
17491746
debug!(?ident.span);
17501747

17511748
// Leave the responsibility to create the `LocalDefId` to lowering.
17521749
let param = self.r.next_node_id();
1753-
let res = LifetimeRes::Fresh { param, binder };
1750+
let res = LifetimeRes::Fresh { param, binder, kind };
17541751
self.record_lifetime_param(param, res);
17551752

17561753
// Record the created lifetime parameter so lowering can pick it up and add it to HIR.
@@ -1844,14 +1841,15 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
18441841
};
18451842
let ident = Ident::new(kw::UnderscoreLifetime, elided_lifetime_span);
18461843

1844+
let kind = if segment.has_generic_args {
1845+
MissingLifetimeKind::Comma
1846+
} else {
1847+
MissingLifetimeKind::Brackets
1848+
};
18471849
let missing_lifetime = MissingLifetime {
18481850
id: node_ids.start,
18491851
span: elided_lifetime_span,
1850-
kind: if segment.has_generic_args {
1851-
MissingLifetimeKind::Comma
1852-
} else {
1853-
MissingLifetimeKind::Brackets
1854-
},
1852+
kind,
18551853
count: expected_lifetimes,
18561854
};
18571855
let mut should_lint = true;
@@ -1897,7 +1895,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
18971895
// Group all suggestions into the first record.
18981896
let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
18991897
for id in node_ids {
1900-
let res = self.create_fresh_lifetime(ident, binder);
1898+
let res = self.create_fresh_lifetime(ident, binder, kind);
19011899
self.record_lifetime_res(
19021900
id,
19031901
res,

compiler/rustc_resolve/src/late/diagnostics.rs

+1-13
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_errors::{
2424
use rustc_hir as hir;
2525
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
2626
use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
27-
use rustc_hir::PrimTy;
27+
use rustc_hir::{MissingLifetimeKind, PrimTy};
2828
use rustc_session::lint;
2929
use rustc_session::Session;
3030
use rustc_span::edit_distance::find_best_match_for_name;
@@ -109,18 +109,6 @@ pub(super) struct MissingLifetime {
109109
pub count: usize,
110110
}
111111

112-
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
113-
pub(super) enum MissingLifetimeKind {
114-
/// An explicit `'_`.
115-
Underscore,
116-
/// An elided lifetime `&' ty`.
117-
Ampersand,
118-
/// An elided lifetime in brackets with written brackets.
119-
Comma,
120-
/// An elided lifetime with elided brackets.
121-
Brackets,
122-
}
123-
124112
/// Description of the lifetimes appearing in a function parameter.
125113
/// This is used to provide a literal explanation to the elision failure.
126114
#[derive(Clone, Debug)]

0 commit comments

Comments
 (0)