Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #18620 from Veykril/push-pyulxnouvxkq
fix: Parse lifetime bounds in lifetime param into TypeBoundList
Lukas Wirth 2024-12-06
parent 0b5f51a · parent f3d7415 · commit 7f39ee3
-rw-r--r--crates/hir-def/src/item_tree/pretty.rs4
-rw-r--r--crates/hir-def/src/item_tree/tests.rs6
-rw-r--r--crates/hir-ty/src/tests/traits.rs23
-rw-r--r--crates/hir/src/semantics.rs4
-rw-r--r--crates/ide-completion/src/completions/lifetime.rs36
-rw-r--r--crates/ide-completion/src/context.rs5
-rw-r--r--crates/ide-completion/src/context/analysis.rs17
-rw-r--r--crates/ide-db/src/defs.rs10
-rw-r--r--crates/intern/src/symbol/symbols.rs1
-rw-r--r--crates/parser/src/grammar/generic_params.rs15
-rw-r--r--crates/parser/test_data/parser/inline/ok/lifetime_param.rast6
-rw-r--r--crates/parser/test_data/parser/inline/ok/precise_capturing.rast12
-rw-r--r--crates/parser/test_data/parser/ok/0018_struct_type_params.rast48
-rw-r--r--crates/parser/test_data/parser/ok/0020_type_param_bounds.rast23
14 files changed, 125 insertions, 85 deletions
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index 0c5e3a3620..70bf2f13c8 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -648,9 +648,9 @@ impl Printer<'_> {
let (target, bound) = match pred {
WherePredicate::TypeBound { target, bound } => (target, bound),
WherePredicate::Lifetime { target, bound } => {
- wln!(
+ w!(
this,
- "{}: {},",
+ "{}: {}",
target.name.display(self.db.upcast(), edition),
bound.name.display(self.db.upcast(), edition)
);
diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index 5c07369f4b..0f53969d6c 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -351,7 +351,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
where
T: Copy,
T: 'a,
- T: 'b
+ T: 'b,
+ 'b: 'a
{
pub(self) field: &'a &'b T,
}
@@ -370,7 +371,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
where
T: Copy,
T: 'a,
- T: 'b
+ T: 'b,
+ 'b: 'a
{
// AstId: 9
pub(self) fn f<G>(
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 624148cab2..b62672d21e 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -1631,6 +1631,29 @@ fn test<'lifetime>(
}
#[test]
+fn lifetime_bounds() {
+ check_infer(
+ r#"
+//- minicore: sized, coerce_unsized
+trait Trait<'a>: Sized {
+ fn f(&'a self) {}
+}
+fn test<'a, 'b: 'a>(it: impl Trait<'a>){
+ it.f();
+}
+"#,
+ expect![[r#"
+ 38..42 'self': &'a Self
+ 44..46 '{}': ()
+ 69..71 'it': impl Trait<'a>
+ 88..103 '{ it.f(); }': ()
+ 94..96 'it': impl Trait<'a>
+ 94..100 'it.f()': ()
+ "#]],
+ );
+}
+
+#[test]
fn error_bound_chalk() {
check_types(
r#"
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 65470d061b..0b09cf2792 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -2026,6 +2026,10 @@ impl SemanticsScope<'_> {
)
}
+ pub fn generic_def(&self) -> Option<crate::GenericDef> {
+ self.resolver.generic_def().map(|id| id.into())
+ }
+
pub fn extern_crates(&self) -> impl Iterator<Item = (Name, Module)> + '_ {
self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id }))
}
diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs
index 9efc52428e..0692446381 100644
--- a/crates/ide-completion/src/completions/lifetime.rs
+++ b/crates/ide-completion/src/completions/lifetime.rs
@@ -8,7 +8,6 @@
//! show up for normal completions, or they won't show completions other than lifetimes depending
//! on the fixture input.
use hir::{sym, Name, ScopeDef};
-use syntax::{ast, ToSmolStr, TokenText};
use crate::{
completions::Completions,
@@ -21,33 +20,24 @@ pub(crate) fn complete_lifetime(
ctx: &CompletionContext<'_>,
lifetime_ctx: &LifetimeContext,
) {
- let (lp, lifetime) = match lifetime_ctx {
- LifetimeContext { kind: LifetimeKind::Lifetime, lifetime } => (None, lifetime),
- LifetimeContext {
- kind: LifetimeKind::LifetimeParam { is_decl: false, param },
- lifetime,
- } => (Some(param), lifetime),
- _ => return,
- };
- let param_lifetime = match (lifetime, lp.and_then(|lp| lp.lifetime())) {
- (Some(lt), Some(lp)) if lp == lt.clone() => return,
- (Some(_), Some(lp)) => Some(lp),
- _ => None,
+ let &LifetimeContext { kind: LifetimeKind::Lifetime { in_lifetime_param_bound, def }, .. } =
+ lifetime_ctx
+ else {
+ return;
};
- let param_lifetime = param_lifetime.as_ref().map(ast::Lifetime::text);
- let param_lifetime = param_lifetime.as_ref().map(TokenText::as_str);
ctx.process_all_names_raw(&mut |name, res| {
- if matches!(
- res,
- ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_))
- if param_lifetime != Some(&*name.display_no_db(ctx.edition).to_smolstr())
- ) {
+ if matches!(res, ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_))) {
acc.add_lifetime(ctx, name);
}
});
- if param_lifetime.is_none() {
- acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_static.clone()));
+ acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_static.clone()));
+ if !in_lifetime_param_bound
+ && def.is_some_and(|def| {
+ !matches!(def, hir::GenericDef::Function(_) | hir::GenericDef::Impl(_))
+ })
+ {
+ acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_underscore.clone()));
}
}
@@ -222,6 +212,8 @@ fn foo<'footime, 'lifetime: 'a$0>() {}
"#,
expect![[r#"
lt 'footime
+ lt 'lifetime
+ lt 'static
"#]],
);
}
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 5b8d1c30a2..3a66170633 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -290,15 +290,14 @@ pub(crate) struct ParamContext {
/// The state of the lifetime we are completing.
#[derive(Debug)]
pub(crate) struct LifetimeContext {
- pub(crate) lifetime: Option<ast::Lifetime>,
pub(crate) kind: LifetimeKind,
}
/// The kind of lifetime we are completing.
#[derive(Debug)]
pub(crate) enum LifetimeKind {
- LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
- Lifetime,
+ LifetimeParam,
+ Lifetime { in_lifetime_param_bound: bool, def: Option<hir::GenericDef> },
LabelRef,
LabelDef,
}
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index a4e018b180..4a678963b9 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -562,7 +562,7 @@ fn expected_type_and_name(
}
fn classify_lifetime(
- _sema: &Semantics<'_, RootDatabase>,
+ sema: &Semantics<'_, RootDatabase>,
original_file: &SyntaxNode,
lifetime: ast::Lifetime,
) -> Option<LifetimeContext> {
@@ -571,21 +571,22 @@ fn classify_lifetime(
return None;
}
+ let lifetime =
+ find_node_at_offset::<ast::Lifetime>(original_file, lifetime.syntax().text_range().start());
let kind = match_ast! {
match parent {
- ast::LifetimeParam(param) => LifetimeKind::LifetimeParam {
- is_decl: param.lifetime().as_ref() == Some(&lifetime),
- param
- },
+ ast::LifetimeParam(_) => LifetimeKind::LifetimeParam,
ast::BreakExpr(_) => LifetimeKind::LabelRef,
ast::ContinueExpr(_) => LifetimeKind::LabelRef,
ast::Label(_) => LifetimeKind::LabelDef,
- _ => LifetimeKind::Lifetime,
+ _ => {
+ let def = lifetime.as_ref().and_then(|lt| sema.scope(lt.syntax())?.generic_def());
+ LifetimeKind::Lifetime { in_lifetime_param_bound: ast::TypeBound::can_cast(parent.kind()), def }
+ },
}
};
- let lifetime = find_node_at_offset(original_file, lifetime.syntax().text_range().start());
- Some(LifetimeContext { lifetime, kind })
+ Some(LifetimeContext { kind })
}
fn classify_name(
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index fdac4dd2ef..5eec33636b 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -772,16 +772,6 @@ impl NameRefClass {
.map(GenericParam::LifetimeParam)
.map(Definition::GenericParam)
.map(NameRefClass::Definition),
- // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
- // if our lifetime is in a LifetimeParam without being the constrained lifetime
- _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
- != Some(lifetime) =>
- {
- sema.resolve_lifetime_param(lifetime)
- .map(GenericParam::LifetimeParam)
- .map(Definition::GenericParam)
- .map(NameRefClass::Definition)
- }
_ => None,
}
}
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 8f79cf2007..ebc9c14059 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -80,6 +80,7 @@ define_symbols! {
self_ = "self",
Self_ = "Self",
tick_static = "'static",
+ tick_underscore = "'_",
dollar_crate = "$crate",
MISSING_NAME = "[missing name]",
fn_ = "fn",
diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs
index 08b23cd92a..9d4fdbfaf2 100644
--- a/crates/parser/src/grammar/generic_params.rs
+++ b/crates/parser/src/grammar/generic_params.rs
@@ -56,7 +56,7 @@ fn generic_param(p: &mut Parser<'_>, m: Marker) -> bool {
fn lifetime_param(p: &mut Parser<'_>, m: Marker) {
assert!(p.at(LIFETIME_IDENT));
lifetime(p);
- if p.at(T![:]) {
+ if p.eat(T![:]) {
lifetime_bounds(p);
}
m.complete(p, LIFETIME_PARAM);
@@ -106,14 +106,19 @@ fn const_param(p: &mut Parser<'_>, m: Marker) {
}
fn lifetime_bounds(p: &mut Parser<'_>) {
- assert!(p.at(T![:]));
- p.bump(T![:]);
- while p.at(LIFETIME_IDENT) {
- lifetime(p);
+ let marker = p.start();
+ while {
+ if !matches!(p.current(), LIFETIME_IDENT | T![>] | T![,]) {
+ p.error("expected lifetime");
+ }
+
+ type_bound(p)
+ } {
if !p.eat(T![+]) {
break;
}
}
+ marker.complete(p, TYPE_BOUND_LIST);
}
// test type_param_bounds
diff --git a/crates/parser/test_data/parser/inline/ok/lifetime_param.rast b/crates/parser/test_data/parser/inline/ok/lifetime_param.rast
index c595031f35..315200aca2 100644
--- a/crates/parser/test_data/parser/inline/ok/lifetime_param.rast
+++ b/crates/parser/test_data/parser/inline/ok/lifetime_param.rast
@@ -11,8 +11,10 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
R_ANGLE ">"
PARAM_LIST
L_PAREN "("
diff --git a/crates/parser/test_data/parser/inline/ok/precise_capturing.rast b/crates/parser/test_data/parser/inline/ok/precise_capturing.rast
index f9c0a245af..5a67cc2176 100644
--- a/crates/parser/test_data/parser/inline/ok/precise_capturing.rast
+++ b/crates/parser/test_data/parser/inline/ok/precise_capturing.rast
@@ -11,8 +11,10 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'a"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'a"
COMMA ","
WHITESPACE " "
LIFETIME_PARAM
@@ -20,8 +22,10 @@ SOURCE_FILE
LIFETIME_IDENT "'b"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
COMMA ","
WHITESPACE " "
TYPE_PARAM
diff --git a/crates/parser/test_data/parser/ok/0018_struct_type_params.rast b/crates/parser/test_data/parser/ok/0018_struct_type_params.rast
index 11ebc7efb9..1e4eb15609 100644
--- a/crates/parser/test_data/parser/ok/0018_struct_type_params.rast
+++ b/crates/parser/test_data/parser/ok/0018_struct_type_params.rast
@@ -96,6 +96,7 @@ SOURCE_FILE
LIFETIME
LIFETIME_IDENT "'a"
COLON ":"
+ TYPE_BOUND_LIST
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
@@ -111,8 +112,10 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
@@ -128,10 +131,12 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
- WHITESPACE " "
- PLUS "+"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
+ WHITESPACE " "
+ PLUS "+"
WHITESPACE " "
R_ANGLE ">"
SEMICOLON ";"
@@ -148,13 +153,16 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'c"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'c"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
@@ -202,9 +210,11 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
- PLUS "+"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
+ PLUS "+"
COMMA ","
WHITESPACE " "
LIFETIME_PARAM
@@ -212,8 +222,10 @@ SOURCE_FILE
LIFETIME_IDENT "'b"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'c"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'c"
COMMA ","
R_ANGLE ">"
SEMICOLON ";"
diff --git a/crates/parser/test_data/parser/ok/0020_type_param_bounds.rast b/crates/parser/test_data/parser/ok/0020_type_param_bounds.rast
index 043a966ff9..448cf49446 100644
--- a/crates/parser/test_data/parser/ok/0020_type_param_bounds.rast
+++ b/crates/parser/test_data/parser/ok/0020_type_param_bounds.rast
@@ -237,8 +237,10 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'d"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'d"
COMMA ","
WHITESPACE " "
LIFETIME_PARAM
@@ -246,13 +248,16 @@ SOURCE_FILE
LIFETIME_IDENT "'d"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'a"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'a"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
COMMA ","
WHITESPACE " "
TYPE_PARAM