Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/cli/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
5 changes: 3 additions & 2 deletions src/lib/ast/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Bound<'src>>,
Expand All @@ -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<Path<'src, NoGenericArgs>>,
}

#[derive(Default, Debug)]
Expand Down
1 change: 1 addition & 0 deletions src/lib/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub enum Error {
FrontmatterOpeningTooLarge(Span),
GenericArgsOnFieldExpr(Span),
HigherRankedBinderOnInvalidBound(Span),
ImplRestrictedTraitAlias,
InvalidAbiStr(Span),
InvalidAssocItemKind(Span),
InvalidDigit(Span),
Expand Down
40 changes: 29 additions & 11 deletions src/lib/fmter/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,13 +526,18 @@ impl Fmt for (ast::TraitItem<'_>, Vec<ast::Attr<'_, ast::attr::Inner>>) {
}
}

impl Fmt for TrailingSpace<ast::TraitItemModifiers> {
impl Fmt for TrailingSpace<ast::TraitItemModifiers<'_>> {
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, " ");
}
}
}

Expand Down Expand Up @@ -683,21 +688,34 @@ impl Fmt for TrailingSpace<ast::Visibility<'_>> {
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<ast::Constness> {
fn fmt(self, cx: &mut Cx<'_>) {
let Self(constness) = self;
Expand Down
1 change: 1 addition & 0 deletions src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
11 changes: 4 additions & 7 deletions src/lib/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ mod test;
mod ty;
mod weak;

type Result<T, E = BufferedError> = std::result::Result<T, E>;

#[derive(Debug)]
struct BufferedError(());
type Result<T, E = ()> = std::result::Result<T, E>;

#[expect(clippy::missing_errors_doc)] // FIXME: TODO
#[expect(clippy::result_unit_err)] // handled via an out-parameter
Expand All @@ -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> {
Expand Down Expand Up @@ -170,7 +167,7 @@ impl<'t, 'e, 'src> Parser<'t, 'e, 'src> {

fn fatal<T>(&mut self, error: Error) -> Result<T> {
self.error(error);
Err(BufferedError(()))
Err(())
}

// FIXME: Overload the ret ty to allow for `-> Option<Span>`
Expand Down Expand Up @@ -237,14 +234,14 @@ impl<'t, 'e, 'src> Parser<'t, 'e, 'src> {
Parser { errors, ..*self }
}

// FIXME: Improve impl
fn probe<T>(
&mut self,
parse: impl FnOnce(&mut Parser<'_, '_, 'src>) -> Option<T>,
) -> Option<T> {
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;
Expand Down
99 changes: 66 additions & 33 deletions src/lib/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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` <https://github.com/rust-lang/rust/issues/149226>
Expand Down Expand Up @@ -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);
Expand All @@ -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` <https://github.com/rust-lang/rust/issues/143874>
Expand All @@ -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)));
}
Expand Down Expand Up @@ -327,6 +335,13 @@ impl<'src> Parser<'_, '_, 'src> {
},
TokenKind::Impl => {
self.advance();

// FEATURE: `impl_restriction` <https://github.com/rust-lang/rust/issues/105077>
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 &
Expand Down Expand Up @@ -807,7 +822,7 @@ impl<'src> Parser<'_, '_, 'src> {
/// <!-- FIXME: Add an EBNF section back in -->
fn fin_parse_trait_item(
&mut self,
modifiers: ast::TraitItemModifiers,
modifiers: ast::TraitItemModifiers<'src>,
attrs: &mut Vec<ast::Attr<'src>>,
) -> Result<ast::ItemKind<'src>> {
let binder = self.parse_common_ident()?;
Expand Down Expand Up @@ -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<ast::GenericParam<'src>>,
) -> Result<ast::ItemKind<'src>> {
Expand All @@ -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 => {}
Expand All @@ -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,
Expand Down Expand Up @@ -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<TokenKind>,
) -> Option<Result<ast::Path<'src, ast::NoGenericArgs>>> {
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
}
}

Expand Down Expand Up @@ -1121,7 +1154,6 @@ pub(super) enum ItemCx {
Trait,
}

#[derive(Clone, Copy)]
enum Qualifier<'src> {
Async,
Auto,
Expand All @@ -1130,6 +1162,7 @@ enum Qualifier<'src> {
Fn,
Gen,
Impl,
ImplRestriction(Box<Result<ast::Path<'src, ast::NoGenericArgs>>>),
Mod,
Reuse,
Safe,
Expand All @@ -1140,29 +1173,29 @@ 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),
_ => (ast::Safety::Inherited, qualifiers),
}
}

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),
Expand Down