From 95298a2e61eb852b165faaaa2dcf1713001731ce Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 22 Oct 2024 12:09:06 +0200 Subject: [PATCH] fix: Fix incorrect parsing of use bounds Also lower them a bit more --- crates/hir-def/src/hir/type_ref.rs | 27 ++++++- crates/hir-def/src/pretty.rs | 23 +++++- crates/hir-ty/src/display.rs | 15 +++- crates/hir-ty/src/lower.rs | 5 +- crates/parser/src/grammar/generic_params.rs | 25 +++++- crates/parser/src/syntax_kind/generated.rs | 2 + .../parser/inline/ok/precise_capturing.rast | 16 ++-- .../parser/inline/ok/precise_capturing.rs | 2 +- crates/syntax/rust.ungram | 9 ++- crates/syntax/src/ast/generated/nodes.rs | 79 ++++++++++++++++++- crates/syntax/src/ast/node_ext.rs | 6 +- 11 files changed, 184 insertions(+), 25 deletions(-) diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index b74cd90f69..5fba206693 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -157,9 +157,19 @@ pub enum TypeBound { Path(Path, TraitBoundModifier), ForLifetime(Box<[Name]>, Path), Lifetime(LifetimeRef), + Use(Box<[UseArgRef]>), Error, } +#[cfg(target_pointer_width = "64")] +const _: [(); 56] = [(); ::std::mem::size_of::()]; + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum UseArgRef { + Name(Name), + Lifetime(LifetimeRef), +} + /// A modifier on a bound, currently this is only used for `?Sized`, where the /// modifier is `Maybe`. #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -295,7 +305,7 @@ impl TypeRef { TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { go_path(path, f) } - TypeBound::Lifetime(_) | TypeBound::Error => (), + TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (), } } } @@ -328,7 +338,7 @@ impl TypeRef { TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { go_path(path, f) } - TypeBound::Lifetime(_) | TypeBound::Error => (), + TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (), } } } @@ -380,7 +390,16 @@ impl TypeBound { None => TypeBound::Error, } } - ast::TypeBoundKind::Use(_) => TypeBound::Error, + ast::TypeBoundKind::Use(gal) => TypeBound::Use( + gal.use_bound_generic_args() + .map(|p| match p { + ast::UseBoundGenericArg::Lifetime(l) => { + UseArgRef::Lifetime(LifetimeRef::new(&l)) + } + ast::UseBoundGenericArg::NameRef(n) => UseArgRef::Name(n.as_name()), + }) + .collect(), + ), ast::TypeBoundKind::Lifetime(lifetime) => { TypeBound::Lifetime(LifetimeRef::new(&lifetime)) } @@ -391,7 +410,7 @@ impl TypeBound { match self { TypeBound::Path(p, m) => Some((p, m)), TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)), - TypeBound::Lifetime(_) | TypeBound::Error => None, + TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => None, } } } diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs index d5ef17a91f..49c0ad4124 100644 --- a/crates/hir-def/src/pretty.rs +++ b/crates/hir-def/src/pretty.rs @@ -1,6 +1,9 @@ //! Display and pretty printing routines. -use std::fmt::{self, Write}; +use std::{ + fmt::{self, Write}, + mem, +}; use hir_expand::mod_path::PathKind; use intern::Interned; @@ -11,7 +14,7 @@ use crate::{ db::DefDatabase, lang_item::LangItemTarget, path::{GenericArg, GenericArgs, Path}, - type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef}, + type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef, UseArgRef}, }; pub(crate) fn print_path( @@ -273,6 +276,22 @@ pub(crate) fn print_type_bounds( print_path(db, path, buf, edition)?; } TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast(), edition))?, + TypeBound::Use(args) => { + write!(buf, "use<")?; + let mut first = true; + for arg in args { + if !mem::take(&mut first) { + write!(buf, ", ")?; + } + match arg { + UseArgRef::Name(it) => write!(buf, "{}", it.display(db.upcast(), edition))?, + UseArgRef::Lifetime(it) => { + write!(buf, "{}", it.name.display(db.upcast(), edition))? + } + } + } + write!(buf, ">")? + } TypeBound::Error => write!(buf, "{{unknown}}")?, } } diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 10f5bcdad8..79861345b8 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -19,7 +19,7 @@ use hir_def::{ lang_item::{LangItem, LangItemTarget}, nameres::DefMap, path::{Path, PathKind}, - type_ref::{TraitBoundModifier, TypeBound, TypeRef}, + type_ref::{TraitBoundModifier, TypeBound, TypeRef, UseArgRef}, visibility::Visibility, GenericDefId, HasModule, ImportPathConfig, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, @@ -2025,6 +2025,19 @@ impl HirDisplay for TypeBound { )?; path.hir_fmt(f) } + TypeBound::Use(args) => { + let edition = f.edition(); + write!( + f, + "use<{}> ", + args.iter() + .map(|it| match it { + UseArgRef::Lifetime(lt) => lt.name.display(f.db.upcast(), edition), + UseArgRef::Name(n) => n.display(f.db.upcast(), edition), + }) + .format(", ") + ) + } TypeBound::Error => write!(f, "{{error}}"), } } diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index c7ed68448b..f7db948998 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -1067,7 +1067,7 @@ impl<'a> TyLoweringContext<'a> { lifetime, }))) } - TypeBound::Error => None, + TypeBound::Use(_) | TypeBound::Error => None, }; clause.into_iter().chain( trait_ref @@ -1087,6 +1087,7 @@ impl<'a> TyLoweringContext<'a> { path.segments().last() } TypeBound::Path(_, TraitBoundModifier::Maybe) + | TypeBound::Use(_) | TypeBound::Error | TypeBound::Lifetime(_) => None, }; @@ -1571,7 +1572,7 @@ pub(crate) fn generic_predicates_for_param_query( }) }) } - TypeBound::Lifetime(_) | TypeBound::Error => false, + TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false, } } WherePredicate::Lifetime { .. } => false, diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs index ecfabca092..92311238c2 100644 --- a/crates/parser/src/grammar/generic_params.rs +++ b/crates/parser/src/grammar/generic_params.rs @@ -144,10 +144,31 @@ fn type_bound(p: &mut Parser<'_>) -> bool { LIFETIME_IDENT => lifetime(p), T![for] => types::for_type(p, false), // test precise_capturing - // fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T> {} + // fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T, Self> {} T![use] if p.nth_at(1, T![<]) => { p.bump_any(); - generic_param_list(p) + let m = p.start(); + delimited( + p, + T![<], + T![>], + T![,], + || "expected identifier or lifetime".into(), + TokenSet::new(&[T![Self], IDENT, LIFETIME_IDENT]), + |p| { + if p.at(T![Self]) { + let m = p.start(); + p.bump(T![Self]); + m.complete(p, NAME_REF); + } else if p.at(LIFETIME_IDENT) { + lifetime(p); + } else { + name_ref(p); + } + true + }, + ); + m.complete(p, USE_BOUND_GENERIC_ARGS); } T![?] if p.nth_at(1, T![for]) => { // test question_for_type_trait_bound diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index 8da338c0a2..21730244a3 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs @@ -312,6 +312,8 @@ pub enum SyntaxKind { UNDERSCORE_EXPR, UNION, USE, + USE_BOUND_GENERIC_ARG, + USE_BOUND_GENERIC_ARGS, USE_TREE, USE_TREE_LIST, VARIANT, 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 cf52f1e479..f9c0a245af 100644 --- a/crates/parser/test_data/parser/inline/ok/precise_capturing.rast +++ b/crates/parser/test_data/parser/inline/ok/precise_capturing.rast @@ -50,16 +50,18 @@ SOURCE_FILE WHITESPACE " " TYPE_BOUND USE_KW "use" - GENERIC_PARAM_LIST + USE_BOUND_GENERIC_ARGS L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'b" + LIFETIME + LIFETIME_IDENT "'b" COMMA "," WHITESPACE " " - TYPE_PARAM - NAME - IDENT "T" + NAME_REF + IDENT "T" + COMMA "," + WHITESPACE " " + NAME_REF + SELF_TYPE_KW "Self" R_ANGLE ">" WHITESPACE " " BLOCK_EXPR diff --git a/crates/parser/test_data/parser/inline/ok/precise_capturing.rs b/crates/parser/test_data/parser/inline/ok/precise_capturing.rs index ec208d5062..9ac2305f3a 100644 --- a/crates/parser/test_data/parser/inline/ok/precise_capturing.rs +++ b/crates/parser/test_data/parser/inline/ok/precise_capturing.rs @@ -1 +1 @@ -fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T> {} +fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T, Self> {} diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 90441c27f6..3609da0bb2 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -657,7 +657,14 @@ TypeBoundList = TypeBound = Lifetime | ('~' 'const' | 'const')? 'async'? '?'? Type -| 'use' GenericParamList +| 'use' UseBoundGenericArgs + +UseBoundGenericArgs = + '<' (UseBoundGenericArg (',' UseBoundGenericArg)* ','?)? '>' + +UseBoundGenericArg = + Lifetime +| NameRef //************************// // Patterns // diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 4f8bff489c..b9eb1abb11 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -1993,13 +1993,15 @@ pub struct TypeBound { pub(crate) syntax: SyntaxNode, } impl TypeBound { - #[inline] - pub fn generic_param_list(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn lifetime(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn ty(&self) -> Option { support::child(&self.syntax) } #[inline] + pub fn use_bound_generic_args(&self) -> Option { + support::child(&self.syntax) + } + #[inline] pub fn question_mark_token(&self) -> Option { support::token(&self.syntax, T![?]) } #[inline] pub fn async_token(&self) -> Option { support::token(&self.syntax, T![async]) } @@ -2076,6 +2078,21 @@ impl Use { pub fn use_token(&self) -> Option { support::token(&self.syntax, T![use]) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct UseBoundGenericArgs { + pub(crate) syntax: SyntaxNode, +} +impl UseBoundGenericArgs { + #[inline] + pub fn use_bound_generic_args(&self) -> AstChildren { + support::children(&self.syntax) + } + #[inline] + pub fn l_angle_token(&self) -> Option { support::token(&self.syntax, T![<]) } + #[inline] + pub fn r_angle_token(&self) -> Option { support::token(&self.syntax, T![>]) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct UseTree { pub(crate) syntax: SyntaxNode, @@ -2402,6 +2419,12 @@ pub enum Type { TupleType(TupleType), } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum UseBoundGenericArg { + Lifetime(Lifetime), + NameRef(NameRef), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AnyHasArgList { pub(crate) syntax: SyntaxNode, @@ -4435,6 +4458,20 @@ impl AstNode for Use { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for UseBoundGenericArgs { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == USE_BOUND_GENERIC_ARGS } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for UseTree { #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == USE_TREE } @@ -5560,6 +5597,34 @@ impl AstNode for Type { } } } +impl From for UseBoundGenericArg { + #[inline] + fn from(node: Lifetime) -> UseBoundGenericArg { UseBoundGenericArg::Lifetime(node) } +} +impl From for UseBoundGenericArg { + #[inline] + fn from(node: NameRef) -> UseBoundGenericArg { UseBoundGenericArg::NameRef(node) } +} +impl AstNode for UseBoundGenericArg { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, LIFETIME | NAME_REF) } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + LIFETIME => UseBoundGenericArg::Lifetime(Lifetime { syntax }), + NAME_REF => UseBoundGenericArg::NameRef(NameRef { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + UseBoundGenericArg::Lifetime(it) => &it.syntax, + UseBoundGenericArg::NameRef(it) => &it.syntax, + } + } +} impl AnyHasArgList { #[inline] pub fn new(node: T) -> AnyHasArgList { @@ -6570,6 +6635,11 @@ impl std::fmt::Display for Type { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for UseBoundGenericArg { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Abi { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -7275,6 +7345,11 @@ impl std::fmt::Display for Use { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for UseBoundGenericArgs { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for UseTree { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 693bfe330b..11f5e662e3 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -795,7 +795,7 @@ pub enum TypeBoundKind { /// for<'a> ... ForType(ast::ForType), /// use - Use(ast::GenericParamList), + Use(ast::UseBoundGenericArgs), /// 'a Lifetime(ast::Lifetime), } @@ -806,8 +806,8 @@ impl ast::TypeBound { TypeBoundKind::PathType(path_type) } else if let Some(for_type) = support::children(self.syntax()).next() { TypeBoundKind::ForType(for_type) - } else if let Some(generic_param_list) = self.generic_param_list() { - TypeBoundKind::Use(generic_param_list) + } else if let Some(args) = self.use_bound_generic_args() { + TypeBoundKind::Use(args) } else if let Some(lifetime) = self.lifetime() { TypeBoundKind::Lifetime(lifetime) } else {