diff --git a/src/cli/diagnostics.rs b/src/cli/diagnostics.rs index 7c90ee7..2018bba 100644 --- a/src/cli/diagnostics.rs +++ b/src/cli/diagnostics.rs @@ -97,6 +97,7 @@ fn convert(error: Error, cx: RenderCx<'_>) -> Diag { Diag::new("string literal guard too large").highlight(span) } Error::ReservedMultiHash(span) => Diag::new("reserved multi-hash").highlight(span), + Error::ImplRestrictedTraitAlias => Diag::new("trait aliases cannot be impl-restricted"), Error::InvalidEscapeSequence(span) => Diag::new("invalid escape sequence").highlight(span), Error::EmptyCharLit(span) => Diag::new("empty char literal").highlight(span), Error::MultiScalarCharLit(span) => Diag::new("multi-scalar char literal").highlight(span), diff --git a/src/lib/ast/item.rs b/src/lib/ast/item.rs index c76a130..452ea4a 100644 --- a/src/lib/ast/item.rs +++ b/src/lib/ast/item.rs @@ -259,7 +259,7 @@ pub(crate) struct StructItem<'src> { #[derive(Debug)] pub(crate) struct TraitItem<'src> { - pub(crate) modifiers: TraitItemModifiers, + pub(crate) modifiers: TraitItemModifiers<'src>, pub(crate) binder: Ident<'src>, pub(crate) generics: Generics<'src>, pub(crate) bounds: Vec>, @@ -275,10 +275,11 @@ pub(crate) struct TraitAliasItem<'src> { } #[derive(Default, Debug)] -pub(crate) struct TraitItemModifiers { +pub(crate) struct TraitItemModifiers<'src> { pub(crate) constness: Constness, pub(crate) safety: Safety, pub(crate) autoness: Autoness, + pub(crate) impl_restriction: Option>, } #[derive(Default, Debug)] diff --git a/src/lib/error.rs b/src/lib/error.rs index b6fcbf4..8e9ecdd 100644 --- a/src/lib/error.rs +++ b/src/lib/error.rs @@ -48,6 +48,7 @@ pub enum Error { FrontmatterOpeningTooLarge(Span), GenericArgsOnFieldExpr(Span), HigherRankedBinderOnInvalidBound(Span), + ImplRestrictedTraitAlias, InvalidAbiStr(Span), InvalidAssocItemKind(Span), InvalidDigit(Span), diff --git a/src/lib/fmter/item.rs b/src/lib/fmter/item.rs index 8579c3c..44daec6 100644 --- a/src/lib/fmter/item.rs +++ b/src/lib/fmter/item.rs @@ -526,13 +526,18 @@ impl Fmt for (ast::TraitItem<'_>, Vec>) { } } -impl Fmt for TrailingSpace { +impl Fmt for TrailingSpace> { fn fmt(self, cx: &mut Cx<'_>) { - let Self(ast::TraitItemModifiers { constness, safety, autoness }) = self; + let Self(ast::TraitItemModifiers { constness, safety, autoness, impl_restriction }) = self; constness.trailing_space().fmt(cx); safety.trailing_space().fmt(cx); autoness.trailing_space().fmt(cx); + if let Some(path) = impl_restriction { + fmt!(cx, "impl"); + Restriction(path).fmt(cx); + fmt!(cx, " "); + } } } @@ -683,21 +688,34 @@ impl Fmt for TrailingSpace> { match vis { ast::Visibility::Inherited => {} ast::Visibility::Restricted(path) => { - fmt!(cx, "pub("); - if let [ast::PathSeg { ident: ast::Ident!("crate" | "super" | "self"), args: () }] = - &*path.segs - { - } else { - fmt!(cx, "in "); - } - path.fmt(cx); - fmt!(cx, ") "); + fmt!(cx, "pub"); + Restriction(path).fmt(cx); + fmt!(cx, " "); } ast::Visibility::Public => fmt!(cx, "pub "), } } } +struct Restriction<'src>(ast::Path<'src, ast::NoGenericArgs>); + +impl Fmt for Restriction<'_> { + fn fmt(self, cx: &mut Cx<'_>) { + let Self(path) = self; + + fmt!(cx, "("); + if let [seg] = path.segs.as_slice() + && let name @ ("crate" | "super" | "self") = seg.ident.name + { + name.fmt(cx); + } else { + fmt!(cx, "in "); + path.fmt(cx); + } + fmt!(cx, ")"); + } +} + impl Fmt for TrailingSpace { fn fmt(self, cx: &mut Cx<'_>) { let Self(constness) = self; diff --git a/src/lib/lib.rs b/src/lib/lib.rs index b022435..cc8c034 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -13,6 +13,7 @@ #![feature(mut_ref)] #![feature(negative_impls)] #![feature(never_type)] +#![feature(try_blocks)] #![feature(type_changing_struct_update)] // Lints #![expect(incomplete_features, reason = "deref_patterns")] diff --git a/src/lib/parser.rs b/src/lib/parser.rs index 971b512..c4c3aed 100644 --- a/src/lib/parser.rs +++ b/src/lib/parser.rs @@ -20,10 +20,7 @@ mod test; mod ty; mod weak; -type Result = std::result::Result; - -#[derive(Debug)] -struct BufferedError(()); +type Result = std::result::Result; #[expect(clippy::missing_errors_doc)] // FIXME: TODO #[expect(clippy::result_unit_err)] // handled via an out-parameter @@ -37,7 +34,7 @@ pub fn parse<'src>( let shebang = file.shebang.map(|shebang| this.source(shebang)); let frontmatter = file.frontmatter.map(|frontmatter| this.source(frontmatter)); - this.parse_file(shebang, frontmatter).map_err(drop) + this.parse_file(shebang, frontmatter) } struct Parser<'t, 'e, 'src> { @@ -170,7 +167,7 @@ impl<'t, 'e, 'src> Parser<'t, 'e, 'src> { fn fatal(&mut self, error: Error) -> Result { self.error(error); - Err(BufferedError(())) + Err(()) } // FIXME: Overload the ret ty to allow for `-> Option` @@ -237,7 +234,6 @@ impl<'t, 'e, 'src> Parser<'t, 'e, 'src> { Parser { errors, ..*self } } - // FIXME: Improve impl fn probe( &mut self, parse: impl FnOnce(&mut Parser<'_, '_, 'src>) -> Option, @@ -245,6 +241,7 @@ impl<'t, 'e, 'src> Parser<'t, 'e, 'src> { let mut errors = ErrorBuffer::Hold(Vec::new()); let mut this = self.snapshot(&mut errors); parse(&mut this).inspect(|_| { + let Self { tokens: _, errors: _, token: _, index: _, source: _, edition: _ }; self.tokens = this.tokens; self.token = this.token; self.index = this.index; diff --git a/src/lib/parser/item.rs b/src/lib/parser/item.rs index ad2cd7a..529b110 100644 --- a/src/lib/parser/item.rs +++ b/src/lib/parser/item.rs @@ -10,6 +10,7 @@ use crate::{ error::{Buffer as ErrorBuffer, Error}, span::Span, }; +use std::mem; impl<'src> Parser<'_, '_, 'src> { /// Parse a sequence of items. @@ -156,11 +157,11 @@ impl<'src> Parser<'_, '_, 'src> { let start = self.token.span; - let qualifiers: Vec<_> = + let mut qualifiers: Vec<_> = self.parse_item_qualifiers().map(|(qualifier, _)| qualifier).collect(); // FIXME: Provide more targeted diagnostics if the qualifiers don't make sense. - match qualifiers.as_slice() { + match qualifiers.as_mut_slice() { [] => {} [Qualifier::Type] => return self.fin_parse_ty_alias_item(defaultness), // FEATURE: `const_block_items` @@ -201,7 +202,7 @@ impl<'src> Parser<'_, '_, 'src> { return self.fin_parse_static_item(safety); } - &[mut ref qualifiers @ .., Qualifier::Fn] => { + &mut [mut ref mut qualifiers @ .., Qualifier::Fn] => { let mut modifiers = ast::FnItemModifiers { defaultness, .. }; (modifiers.constness, qualifiers) = Qualifier::strip_const(qualifiers); @@ -222,7 +223,7 @@ impl<'src> Parser<'_, '_, 'src> { return self.fin_parse_fn_item(modifiers, cx, attrs); } - &[mut ref qualifiers @ .., Qualifier::Trait] => { + &mut [mut ref mut qualifiers @ .., Qualifier::Trait] => { let mut modifiers = ast::TraitItemModifiers::default(); // FEATURE: `const_trait_impl` @@ -233,6 +234,13 @@ impl<'src> Parser<'_, '_, 'src> { [Qualifier::Auto, qualifiers @ ..] => (ast::Autoness::Auto, qualifiers), _ => (ast::Autoness::Not, qualifiers), }; + (modifiers.impl_restriction, qualifiers) = match qualifiers { + [Qualifier::ImplRestriction(path), qualifiers @ ..] => { + let Ok(path) = path else { return Err(()) }; + (Some(mem::replace(path, ast::Path { segs: Vec::new() })), qualifiers) + } + _ => (None, qualifiers), + }; if !qualifiers.is_empty() { self.error(Error::InvalidItemPrefix(start.until(self.token.span))); } @@ -327,6 +335,13 @@ impl<'src> Parser<'_, '_, 'src> { }, TokenKind::Impl => { self.advance(); + + // FEATURE: `impl_restriction` + if let Some(path) = self.parse_restriction(Some(TokenKind::Trait)) { + yield (Qualifier::ImplRestriction(Box::new(path)), self.token.kind); + continue; + } + yield (Qualifier::Impl, self.token.kind); // Once we encounter `impl`, don't attempt to look for more item qualifiers. // That's because the grammar following an impl item's `impl` is very complex & @@ -807,7 +822,7 @@ impl<'src> Parser<'_, '_, 'src> { /// fn fin_parse_trait_item( &mut self, - modifiers: ast::TraitItemModifiers, + modifiers: ast::TraitItemModifiers<'src>, attrs: &mut Vec>, ) -> Result> { let binder = self.parse_common_ident()?; @@ -836,7 +851,7 @@ impl<'src> Parser<'_, '_, 'src> { /// Finish parsing a trait alias item. fn fin_parse_trait_alias_item( &mut self, - modifiers: ast::TraitItemModifiers, + modifiers: ast::TraitItemModifiers<'src>, binder: ast::Ident<'src>, params: Vec>, ) -> Result> { @@ -845,7 +860,7 @@ impl<'src> Parser<'_, '_, 'src> { self.parse(TokenKind::Semicolon)?; - let ast::TraitItemModifiers { constness, safety, autoness } = modifiers; + let ast::TraitItemModifiers { constness, safety, autoness, impl_restriction } = modifiers; match safety { ast::Safety::Inherited => {} @@ -857,6 +872,10 @@ impl<'src> Parser<'_, '_, 'src> { ast::Autoness::Not => {} } + if impl_restriction.is_some() { + self.error(Error::ImplRestrictedTraitAlias); + } + Ok(ast::ItemKind::TraitAlias(Box::new(ast::TraitAliasItem { constness, binder, @@ -1025,43 +1044,57 @@ impl<'src> Parser<'_, '_, 'src> { return Ok(ast::Visibility::Inherited); } - enum VisKeyword { + if let Some(path) = self.parse_restriction(None) { + return Ok(ast::Visibility::Restricted(path?)); + } + + Ok(ast::Visibility::Public) + } + + fn begins_visibility(&self) -> bool { + // To kept in sync with `Self::parse_visibility`. + + self.token.kind == TokenKind::Pub + } + + fn parse_restriction( + &mut self, + disambiguator: Option, + ) -> Option>> { + enum Herald { In, CrateSuperSelf(Span), } - // FIXME: Only do this lookahead dance for tuple struct fields. This way, we can - // can give better errors on invalid vis restrictions in the common cases. if self.token.kind == TokenKind::OpenRoundBracket && let token = self.peek(1) - && let Some(keyword) = match token.kind { + && let Some(herald) = match token.kind { TokenKind::Crate | TokenKind::Super | TokenKind::SelfLower - if let TokenKind::CloseRoundBracket = self.peek(2).kind => + if let TokenKind::CloseRoundBracket = self.peek(2).kind + && disambiguator.is_none_or(|t| self.peek(3).kind == t) => { - Some(VisKeyword::CrateSuperSelf(token.span)) + Some(Herald::CrateSuperSelf(token.span)) } - TokenKind::In => Some(VisKeyword::In), + TokenKind::In => Some(Herald::In), _ => None, } { - self.advance(); - self.advance(); + self.advance(); // parenthesis + self.advance(); // herald - let path = match keyword { - VisKeyword::In => self.parse_path(PathMode::Normal)?, - VisKeyword::CrateSuperSelf(span) => ast::Path::ident(self.ident(span)), + let path = try { + let path = match herald { + Herald::In => self.parse_path(PathMode::Normal)?, + Herald::CrateSuperSelf(span) => ast::Path::ident(self.ident(span)), + }; + self.parse(TokenKind::CloseRoundBracket)?; + path }; - self.parse(TokenKind::CloseRoundBracket)?; - return Ok(ast::Visibility::Restricted(path)); - } - Ok(ast::Visibility::Public) - } - - fn begins_visibility(&self) -> bool { - // To kept in sync with `Self::parse_visibility`. + return Some(path); + } - self.token.kind == TokenKind::Pub + None } } @@ -1121,7 +1154,6 @@ pub(super) enum ItemCx { Trait, } -#[derive(Clone, Copy)] enum Qualifier<'src> { Async, Auto, @@ -1130,6 +1162,7 @@ enum Qualifier<'src> { Fn, Gen, Impl, + ImplRestriction(Box>>), Mod, Reuse, Safe, @@ -1140,21 +1173,21 @@ enum Qualifier<'src> { } impl<'src> Qualifier<'src> { - fn strip_const(qualifiers: &[Self]) -> (ast::Constness, &[Self]) { + fn strip_const(qualifiers: &mut [Self]) -> (ast::Constness, &mut [Self]) { match qualifiers { [Self::Const, qualifiers @ ..] => (ast::Constness::Const, qualifiers), _ => (ast::Constness::Not, qualifiers), } } - fn strip_unsafe(qualifiers: &[Self]) -> (ast::Safety, &[Self]) { + fn strip_unsafe(qualifiers: &mut [Self]) -> (ast::Safety, &mut [Self]) { match qualifiers { [Self::Unsafe, qualifiers @ ..] => (ast::Safety::Unsafe, qualifiers), _ => (ast::Safety::Inherited, qualifiers), } } - fn strip_safety(qualifiers: &[Self]) -> (ast::Safety<()>, &[Self]) { + fn strip_safety(qualifiers: &mut [Self]) -> (ast::Safety<()>, &mut [Self]) { match qualifiers { [Self::Unsafe, qualifiers @ ..] => (ast::Safety::Unsafe, qualifiers), [Self::Safe, qualifiers @ ..] => (ast::Safety::Safe(()), qualifiers), @@ -1162,7 +1195,7 @@ impl<'src> Qualifier<'src> { } } - fn strip_extern(qualifiers: &[Self]) -> (ast::Externness<'src>, &[Self]) { + fn strip_extern(qualifiers: &mut [Self]) -> (ast::Externness<'src>, &mut [Self]) { match qualifiers { [Self::Extern(abi), qualifiers @ ..] => (ast::Externness::Extern(*abi), qualifiers), _ => (ast::Externness::Not, qualifiers),