Skip to content

Commit f3d7415

Browse files
committed
Parse lifetime bounds in lifetime param into TypeBoundList
This mainly aids in error recovery but also makes it a bit easier to handle lifetime resolution. While doing so it also came apparent that we were not actually lowering lifetime outlives relationships within lifetime parameter declaration bounds, so this fixes that.
1 parent df7ab62 commit f3d7415

File tree

14 files changed

+125
-85
lines changed

14 files changed

+125
-85
lines changed

crates/hir-def/src/item_tree/pretty.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -648,9 +648,9 @@ impl Printer<'_> {
648648
let (target, bound) = match pred {
649649
WherePredicate::TypeBound { target, bound } => (target, bound),
650650
WherePredicate::Lifetime { target, bound } => {
651-
wln!(
651+
w!(
652652
this,
653-
"{}: {},",
653+
"{}: {}",
654654
target.name.display(self.db.upcast(), edition),
655655
bound.name.display(self.db.upcast(), edition)
656656
);

crates/hir-def/src/item_tree/tests.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
351351
where
352352
T: Copy,
353353
T: 'a,
354-
T: 'b
354+
T: 'b,
355+
'b: 'a
355356
{
356357
pub(self) field: &'a &'b T,
357358
}
@@ -370,7 +371,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
370371
where
371372
T: Copy,
372373
T: 'a,
373-
T: 'b
374+
T: 'b,
375+
'b: 'a
374376
{
375377
// AstId: 9
376378
pub(self) fn f<G>(

crates/hir-ty/src/tests/traits.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,29 @@ fn test<'lifetime>(
16301630
);
16311631
}
16321632

1633+
#[test]
1634+
fn lifetime_bounds() {
1635+
check_infer(
1636+
r#"
1637+
//- minicore: sized, coerce_unsized
1638+
trait Trait<'a>: Sized {
1639+
fn f(&'a self) {}
1640+
}
1641+
fn test<'a, 'b: 'a>(it: impl Trait<'a>){
1642+
it.f();
1643+
}
1644+
"#,
1645+
expect![[r#"
1646+
38..42 'self': &'a Self
1647+
44..46 '{}': ()
1648+
69..71 'it': impl Trait<'a>
1649+
88..103 '{ it.f(); }': ()
1650+
94..96 'it': impl Trait<'a>
1651+
94..100 'it.f()': ()
1652+
"#]],
1653+
);
1654+
}
1655+
16331656
#[test]
16341657
fn error_bound_chalk() {
16351658
check_types(

crates/hir/src/semantics.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,6 +2026,10 @@ impl SemanticsScope<'_> {
20262026
)
20272027
}
20282028

2029+
pub fn generic_def(&self) -> Option<crate::GenericDef> {
2030+
self.resolver.generic_def().map(|id| id.into())
2031+
}
2032+
20292033
pub fn extern_crates(&self) -> impl Iterator<Item = (Name, Module)> + '_ {
20302034
self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id }))
20312035
}

crates/ide-completion/src/completions/lifetime.rs

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
//! show up for normal completions, or they won't show completions other than lifetimes depending
99
//! on the fixture input.
1010
use hir::{sym, Name, ScopeDef};
11-
use syntax::{ast, ToSmolStr, TokenText};
1211

1312
use crate::{
1413
completions::Completions,
@@ -21,33 +20,24 @@ pub(crate) fn complete_lifetime(
2120
ctx: &CompletionContext<'_>,
2221
lifetime_ctx: &LifetimeContext,
2322
) {
24-
let (lp, lifetime) = match lifetime_ctx {
25-
LifetimeContext { kind: LifetimeKind::Lifetime, lifetime } => (None, lifetime),
26-
LifetimeContext {
27-
kind: LifetimeKind::LifetimeParam { is_decl: false, param },
28-
lifetime,
29-
} => (Some(param), lifetime),
30-
_ => return,
31-
};
32-
let param_lifetime = match (lifetime, lp.and_then(|lp| lp.lifetime())) {
33-
(Some(lt), Some(lp)) if lp == lt.clone() => return,
34-
(Some(_), Some(lp)) => Some(lp),
35-
_ => None,
23+
let &LifetimeContext { kind: LifetimeKind::Lifetime { in_lifetime_param_bound, def }, .. } =
24+
lifetime_ctx
25+
else {
26+
return;
3627
};
37-
let param_lifetime = param_lifetime.as_ref().map(ast::Lifetime::text);
38-
let param_lifetime = param_lifetime.as_ref().map(TokenText::as_str);
3928

4029
ctx.process_all_names_raw(&mut |name, res| {
41-
if matches!(
42-
res,
43-
ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_))
44-
if param_lifetime != Some(&*name.display_no_db(ctx.edition).to_smolstr())
45-
) {
30+
if matches!(res, ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_))) {
4631
acc.add_lifetime(ctx, name);
4732
}
4833
});
49-
if param_lifetime.is_none() {
50-
acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_static.clone()));
34+
acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_static.clone()));
35+
if !in_lifetime_param_bound
36+
&& def.is_some_and(|def| {
37+
!matches!(def, hir::GenericDef::Function(_) | hir::GenericDef::Impl(_))
38+
})
39+
{
40+
acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_underscore.clone()));
5141
}
5242
}
5343

@@ -222,6 +212,8 @@ fn foo<'footime, 'lifetime: 'a$0>() {}
222212
"#,
223213
expect![[r#"
224214
lt 'footime
215+
lt 'lifetime
216+
lt 'static
225217
"#]],
226218
);
227219
}

crates/ide-completion/src/context.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,15 +290,14 @@ pub(crate) struct ParamContext {
290290
/// The state of the lifetime we are completing.
291291
#[derive(Debug)]
292292
pub(crate) struct LifetimeContext {
293-
pub(crate) lifetime: Option<ast::Lifetime>,
294293
pub(crate) kind: LifetimeKind,
295294
}
296295

297296
/// The kind of lifetime we are completing.
298297
#[derive(Debug)]
299298
pub(crate) enum LifetimeKind {
300-
LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
301-
Lifetime,
299+
LifetimeParam,
300+
Lifetime { in_lifetime_param_bound: bool, def: Option<hir::GenericDef> },
302301
LabelRef,
303302
LabelDef,
304303
}

crates/ide-completion/src/context/analysis.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ fn expected_type_and_name(
562562
}
563563

564564
fn classify_lifetime(
565-
_sema: &Semantics<'_, RootDatabase>,
565+
sema: &Semantics<'_, RootDatabase>,
566566
original_file: &SyntaxNode,
567567
lifetime: ast::Lifetime,
568568
) -> Option<LifetimeContext> {
@@ -571,21 +571,22 @@ fn classify_lifetime(
571571
return None;
572572
}
573573

574+
let lifetime =
575+
find_node_at_offset::<ast::Lifetime>(original_file, lifetime.syntax().text_range().start());
574576
let kind = match_ast! {
575577
match parent {
576-
ast::LifetimeParam(param) => LifetimeKind::LifetimeParam {
577-
is_decl: param.lifetime().as_ref() == Some(&lifetime),
578-
param
579-
},
578+
ast::LifetimeParam(_) => LifetimeKind::LifetimeParam,
580579
ast::BreakExpr(_) => LifetimeKind::LabelRef,
581580
ast::ContinueExpr(_) => LifetimeKind::LabelRef,
582581
ast::Label(_) => LifetimeKind::LabelDef,
583-
_ => LifetimeKind::Lifetime,
582+
_ => {
583+
let def = lifetime.as_ref().and_then(|lt| sema.scope(lt.syntax())?.generic_def());
584+
LifetimeKind::Lifetime { in_lifetime_param_bound: ast::TypeBound::can_cast(parent.kind()), def }
585+
},
584586
}
585587
};
586-
let lifetime = find_node_at_offset(original_file, lifetime.syntax().text_range().start());
587588

588-
Some(LifetimeContext { lifetime, kind })
589+
Some(LifetimeContext { kind })
589590
}
590591

591592
fn classify_name(

crates/ide-db/src/defs.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -772,16 +772,6 @@ impl NameRefClass {
772772
.map(GenericParam::LifetimeParam)
773773
.map(Definition::GenericParam)
774774
.map(NameRefClass::Definition),
775-
// lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
776-
// if our lifetime is in a LifetimeParam without being the constrained lifetime
777-
_ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
778-
!= Some(lifetime) =>
779-
{
780-
sema.resolve_lifetime_param(lifetime)
781-
.map(GenericParam::LifetimeParam)
782-
.map(Definition::GenericParam)
783-
.map(NameRefClass::Definition)
784-
}
785775
_ => None,
786776
}
787777
}

crates/intern/src/symbol/symbols.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ define_symbols! {
8080
self_ = "self",
8181
Self_ = "Self",
8282
tick_static = "'static",
83+
tick_underscore = "'_",
8384
dollar_crate = "$crate",
8485
MISSING_NAME = "[missing name]",
8586
fn_ = "fn",

crates/parser/src/grammar/generic_params.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn generic_param(p: &mut Parser<'_>, m: Marker) -> bool {
5656
fn lifetime_param(p: &mut Parser<'_>, m: Marker) {
5757
assert!(p.at(LIFETIME_IDENT));
5858
lifetime(p);
59-
if p.at(T![:]) {
59+
if p.eat(T![:]) {
6060
lifetime_bounds(p);
6161
}
6262
m.complete(p, LIFETIME_PARAM);
@@ -106,14 +106,19 @@ fn const_param(p: &mut Parser<'_>, m: Marker) {
106106
}
107107

108108
fn lifetime_bounds(p: &mut Parser<'_>) {
109-
assert!(p.at(T![:]));
110-
p.bump(T![:]);
111-
while p.at(LIFETIME_IDENT) {
112-
lifetime(p);
109+
let marker = p.start();
110+
while {
111+
if !matches!(p.current(), LIFETIME_IDENT | T![>] | T![,]) {
112+
p.error("expected lifetime");
113+
}
114+
115+
type_bound(p)
116+
} {
113117
if !p.eat(T![+]) {
114118
break;
115119
}
116120
}
121+
marker.complete(p, TYPE_BOUND_LIST);
117122
}
118123

119124
// test type_param_bounds

0 commit comments

Comments
 (0)