Merge pull request #18620 from Veykril/push-pyulxnouvxkq

fix: Parse lifetime bounds in lifetime param into TypeBoundList
This commit is contained in:
Lukas Wirth 2024-12-05 17:06:02 +00:00 committed by GitHub
commit 7f39ee3fce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 125 additions and 85 deletions

View file

@ -648,9 +648,9 @@ impl Printer<'_> {
let (target, bound) = match pred { let (target, bound) = match pred {
WherePredicate::TypeBound { target, bound } => (target, bound), WherePredicate::TypeBound { target, bound } => (target, bound),
WherePredicate::Lifetime { target, bound } => { WherePredicate::Lifetime { target, bound } => {
wln!( w!(
this, this,
"{}: {},", "{}: {}",
target.name.display(self.db.upcast(), edition), target.name.display(self.db.upcast(), edition),
bound.name.display(self.db.upcast(), edition) bound.name.display(self.db.upcast(), edition)
); );

View file

@ -351,7 +351,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
where where
T: Copy, T: Copy,
T: 'a, T: 'a,
T: 'b T: 'b,
'b: 'a
{ {
pub(self) field: &'a &'b T, pub(self) field: &'a &'b T,
} }
@ -370,7 +371,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
where where
T: Copy, T: Copy,
T: 'a, T: 'a,
T: 'b T: 'b,
'b: 'a
{ {
// AstId: 9 // AstId: 9
pub(self) fn f<G>( pub(self) fn f<G>(

View file

@ -1630,6 +1630,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] #[test]
fn error_bound_chalk() { fn error_bound_chalk() {
check_types( check_types(

View file

@ -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)> + '_ { pub fn extern_crates(&self) -> impl Iterator<Item = (Name, Module)> + '_ {
self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id })) self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id }))
} }

View file

@ -8,7 +8,6 @@
//! show up for normal completions, or they won't show completions other than lifetimes depending //! show up for normal completions, or they won't show completions other than lifetimes depending
//! on the fixture input. //! on the fixture input.
use hir::{sym, Name, ScopeDef}; use hir::{sym, Name, ScopeDef};
use syntax::{ast, ToSmolStr, TokenText};
use crate::{ use crate::{
completions::Completions, completions::Completions,
@ -21,33 +20,24 @@ pub(crate) fn complete_lifetime(
ctx: &CompletionContext<'_>, ctx: &CompletionContext<'_>,
lifetime_ctx: &LifetimeContext, lifetime_ctx: &LifetimeContext,
) { ) {
let (lp, lifetime) = match lifetime_ctx { let &LifetimeContext { kind: LifetimeKind::Lifetime { in_lifetime_param_bound, def }, .. } =
LifetimeContext { kind: LifetimeKind::Lifetime, lifetime } => (None, lifetime), lifetime_ctx
LifetimeContext { else {
kind: LifetimeKind::LifetimeParam { is_decl: false, param }, return;
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 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| { ctx.process_all_names_raw(&mut |name, res| {
if matches!( if matches!(res, ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_))) {
res,
ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_))
if param_lifetime != Some(&*name.display_no_db(ctx.edition).to_smolstr())
) {
acc.add_lifetime(ctx, name); 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#" expect![[r#"
lt 'footime lt 'footime
lt 'lifetime
lt 'static
"#]], "#]],
); );
} }

View file

@ -290,15 +290,14 @@ pub(crate) struct ParamContext {
/// The state of the lifetime we are completing. /// The state of the lifetime we are completing.
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct LifetimeContext { pub(crate) struct LifetimeContext {
pub(crate) lifetime: Option<ast::Lifetime>,
pub(crate) kind: LifetimeKind, pub(crate) kind: LifetimeKind,
} }
/// The kind of lifetime we are completing. /// The kind of lifetime we are completing.
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum LifetimeKind { pub(crate) enum LifetimeKind {
LifetimeParam { is_decl: bool, param: ast::LifetimeParam }, LifetimeParam,
Lifetime, Lifetime { in_lifetime_param_bound: bool, def: Option<hir::GenericDef> },
LabelRef, LabelRef,
LabelDef, LabelDef,
} }

View file

@ -562,7 +562,7 @@ fn expected_type_and_name(
} }
fn classify_lifetime( fn classify_lifetime(
_sema: &Semantics<'_, RootDatabase>, sema: &Semantics<'_, RootDatabase>,
original_file: &SyntaxNode, original_file: &SyntaxNode,
lifetime: ast::Lifetime, lifetime: ast::Lifetime,
) -> Option<LifetimeContext> { ) -> Option<LifetimeContext> {
@ -571,21 +571,22 @@ fn classify_lifetime(
return None; return None;
} }
let lifetime =
find_node_at_offset::<ast::Lifetime>(original_file, lifetime.syntax().text_range().start());
let kind = match_ast! { let kind = match_ast! {
match parent { match parent {
ast::LifetimeParam(param) => LifetimeKind::LifetimeParam { ast::LifetimeParam(_) => LifetimeKind::LifetimeParam,
is_decl: param.lifetime().as_ref() == Some(&lifetime),
param
},
ast::BreakExpr(_) => LifetimeKind::LabelRef, ast::BreakExpr(_) => LifetimeKind::LabelRef,
ast::ContinueExpr(_) => LifetimeKind::LabelRef, ast::ContinueExpr(_) => LifetimeKind::LabelRef,
ast::Label(_) => LifetimeKind::LabelDef, 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( fn classify_name(

View file

@ -772,16 +772,6 @@ impl NameRefClass {
.map(GenericParam::LifetimeParam) .map(GenericParam::LifetimeParam)
.map(Definition::GenericParam) .map(Definition::GenericParam)
.map(NameRefClass::Definition), .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, _ => None,
} }
} }

View file

@ -80,6 +80,7 @@ define_symbols! {
self_ = "self", self_ = "self",
Self_ = "Self", Self_ = "Self",
tick_static = "'static", tick_static = "'static",
tick_underscore = "'_",
dollar_crate = "$crate", dollar_crate = "$crate",
MISSING_NAME = "[missing name]", MISSING_NAME = "[missing name]",
fn_ = "fn", fn_ = "fn",

View file

@ -56,7 +56,7 @@ fn generic_param(p: &mut Parser<'_>, m: Marker) -> bool {
fn lifetime_param(p: &mut Parser<'_>, m: Marker) { fn lifetime_param(p: &mut Parser<'_>, m: Marker) {
assert!(p.at(LIFETIME_IDENT)); assert!(p.at(LIFETIME_IDENT));
lifetime(p); lifetime(p);
if p.at(T![:]) { if p.eat(T![:]) {
lifetime_bounds(p); lifetime_bounds(p);
} }
m.complete(p, LIFETIME_PARAM); m.complete(p, LIFETIME_PARAM);
@ -106,14 +106,19 @@ fn const_param(p: &mut Parser<'_>, m: Marker) {
} }
fn lifetime_bounds(p: &mut Parser<'_>) { fn lifetime_bounds(p: &mut Parser<'_>) {
assert!(p.at(T![:])); let marker = p.start();
p.bump(T![:]); while {
while p.at(LIFETIME_IDENT) { if !matches!(p.current(), LIFETIME_IDENT | T![>] | T![,]) {
lifetime(p); p.error("expected lifetime");
}
type_bound(p)
} {
if !p.eat(T![+]) { if !p.eat(T![+]) {
break; break;
} }
} }
marker.complete(p, TYPE_BOUND_LIST);
} }
// test type_param_bounds // test type_param_bounds

View file

@ -11,6 +11,8 @@ SOURCE_FILE
LIFETIME_IDENT "'a" LIFETIME_IDENT "'a"
COLON ":" COLON ":"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'b" LIFETIME_IDENT "'b"
R_ANGLE ">" R_ANGLE ">"

View file

@ -11,6 +11,8 @@ SOURCE_FILE
LIFETIME_IDENT "'a" LIFETIME_IDENT "'a"
COLON ":" COLON ":"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'a" LIFETIME_IDENT "'a"
COMMA "," COMMA ","
@ -20,6 +22,8 @@ SOURCE_FILE
LIFETIME_IDENT "'b" LIFETIME_IDENT "'b"
COLON ":" COLON ":"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'b" LIFETIME_IDENT "'b"
COMMA "," COMMA ","

View file

@ -96,6 +96,7 @@ SOURCE_FILE
LIFETIME LIFETIME
LIFETIME_IDENT "'a" LIFETIME_IDENT "'a"
COLON ":" COLON ":"
TYPE_BOUND_LIST
R_ANGLE ">" R_ANGLE ">"
SEMICOLON ";" SEMICOLON ";"
WHITESPACE "\n" WHITESPACE "\n"
@ -111,6 +112,8 @@ SOURCE_FILE
LIFETIME_IDENT "'a" LIFETIME_IDENT "'a"
COLON ":" COLON ":"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'b" LIFETIME_IDENT "'b"
R_ANGLE ">" R_ANGLE ">"
@ -128,6 +131,8 @@ SOURCE_FILE
LIFETIME_IDENT "'a" LIFETIME_IDENT "'a"
COLON ":" COLON ":"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'b" LIFETIME_IDENT "'b"
WHITESPACE " " WHITESPACE " "
@ -148,11 +153,14 @@ SOURCE_FILE
LIFETIME_IDENT "'a" LIFETIME_IDENT "'a"
COLON ":" COLON ":"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'b" LIFETIME_IDENT "'b"
WHITESPACE " " WHITESPACE " "
PLUS "+" PLUS "+"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'c" LIFETIME_IDENT "'c"
R_ANGLE ">" R_ANGLE ">"
@ -202,6 +210,8 @@ SOURCE_FILE
LIFETIME_IDENT "'a" LIFETIME_IDENT "'a"
COLON ":" COLON ":"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'b" LIFETIME_IDENT "'b"
PLUS "+" PLUS "+"
@ -212,6 +222,8 @@ SOURCE_FILE
LIFETIME_IDENT "'b" LIFETIME_IDENT "'b"
COLON ":" COLON ":"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'c" LIFETIME_IDENT "'c"
COMMA "," COMMA ","

View file

@ -237,6 +237,8 @@ SOURCE_FILE
LIFETIME_IDENT "'a" LIFETIME_IDENT "'a"
COLON ":" COLON ":"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'d" LIFETIME_IDENT "'d"
COMMA "," COMMA ","
@ -246,11 +248,14 @@ SOURCE_FILE
LIFETIME_IDENT "'d" LIFETIME_IDENT "'d"
COLON ":" COLON ":"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND_LIST
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'a" LIFETIME_IDENT "'a"
WHITESPACE " " WHITESPACE " "
PLUS "+" PLUS "+"
WHITESPACE " " WHITESPACE " "
TYPE_BOUND
LIFETIME LIFETIME
LIFETIME_IDENT "'b" LIFETIME_IDENT "'b"
COMMA "," COMMA ","