From c0c1d716380d0f8b0172736d892be007738107f6 Mon Sep 17 00:00:00 2001 From: Ryan Chandler Date: Wed, 11 Dec 2024 20:12:07 +0000 Subject: [PATCH] Various: Add support for protected(set) and public(set) --- crates/ast/src/ast/modifier.rs | 30 +++++++++++++++++++++++--- crates/ast/src/node.rs | 2 ++ crates/formatter/src/format/mod.rs | 2 ++ crates/lexer/src/lib.rs | 16 ++++++++++++++ crates/parser/src/internal/modifier.rs | 2 ++ crates/semantics/src/walker.rs | 14 ++++++------ crates/token/src/lib.rs | 12 +++++++++-- 7 files changed, 67 insertions(+), 11 deletions(-) diff --git a/crates/ast/src/ast/modifier.rs b/crates/ast/src/ast/modifier.rs index 0336c56..52cac71 100644 --- a/crates/ast/src/ast/modifier.rs +++ b/crates/ast/src/ast/modifier.rs @@ -24,7 +24,9 @@ pub enum Modifier { Abstract(Keyword), Readonly(Keyword), Public(Keyword), + PublicSet(Keyword), Protected(Keyword), + ProtectedSet(Keyword), Private(Keyword), PrivateSet(Keyword), } @@ -37,7 +39,9 @@ impl Modifier { Modifier::Abstract(k) => k, Modifier::Readonly(k) => k, Modifier::Public(k) => k, + Modifier::PublicSet(k) => k, Modifier::Protected(k) => k, + Modifier::ProtectedSet(k) => k, Modifier::Private(k) => k, Modifier::PrivateSet(k) => k, } @@ -71,6 +75,8 @@ impl Modifier { Modifier::Protected(k) => interner.lookup(&k.value), Modifier::Private(k) => interner.lookup(&k.value), Modifier::PrivateSet(k) => interner.lookup(&k.value), + Modifier::ProtectedSet(k) => interner.lookup(&k.value), + Modifier::PublicSet(k) => interner.lookup(&k.value), } } } @@ -85,7 +91,9 @@ impl HasSpan for Modifier { | Modifier::Public(value) | Modifier::Protected(value) | Modifier::Private(value) - | Modifier::PrivateSet(value) => value.span(), + | Modifier::PrivateSet(value) + | Modifier::ProtectedSet(value) + | Modifier::PublicSet(value) => value.span(), } } } @@ -135,7 +143,7 @@ impl Sequence { self.iter().find(|modifier| { matches!( modifier, - Modifier::Public(..) | Modifier::Protected(..) | Modifier::Private(..) | Modifier::PrivateSet(..) + Modifier::Public(..) | Modifier::Protected(..) | Modifier::Private(..) | Modifier::PrivateSet(..) | Modifier::ProtectedSet(..) | Modifier::PublicSet(..) ) }) } @@ -146,7 +154,7 @@ impl Sequence { } pub fn get_first_write_visibility(&self) -> Option<&Modifier> { - self.iter().find(|modifier| matches!(modifier, Modifier::PrivateSet(..))) + self.iter().find(|modifier| matches!(modifier, Modifier::PrivateSet(..) | Modifier::ProtectedSet(..) | Modifier::PublicSet(..))) } /// Returns `true` if the sequence contains a visibility modifier for reading or writing. @@ -188,4 +196,20 @@ impl Sequence { pub fn contains_private_set(&self) -> bool { self.iter().any(|modifier| matches!(modifier, Modifier::PrivateSet(..))) } + + pub fn get_protected_set(&self) -> Option<&Modifier> { + self.iter().find(|modifier| matches!(modifier, Modifier::ProtectedSet(..))) + } + + pub fn contains_protected_set(&self) -> bool { + self.iter().any(|modifier| matches!(modifier, Modifier::ProtectedSet(..))) + } + + pub fn get_public_set(&self) -> Option<&Modifier> { + self.iter().find(|modifier| matches!(modifier, Modifier::PublicSet(..))) + } + + pub fn contains_public_set(&self) -> bool { + self.iter().any(|modifier| matches!(modifier, Modifier::PublicSet(..))) + } } diff --git a/crates/ast/src/node.rs b/crates/ast/src/node.rs index cdf779b..7fcd99f 100644 --- a/crates/ast/src/node.rs +++ b/crates/ast/src/node.rs @@ -1608,6 +1608,8 @@ impl<'a> Node<'a> { Modifier::Static(node) => Node::Keyword(node), Modifier::Readonly(node) => Node::Keyword(node), Modifier::PrivateSet(node) => Node::Keyword(node), + Modifier::ProtectedSet(node) => Node::Keyword(node), + Modifier::PublicSet(node) => Node::Keyword(node), }], Node::Namespace(node) => { let mut children = vec![Node::Keyword(&node.r#namespace)]; diff --git a/crates/formatter/src/format/mod.rs b/crates/formatter/src/format/mod.rs index 477e073..38afaa2 100644 --- a/crates/formatter/src/format/mod.rs +++ b/crates/formatter/src/format/mod.rs @@ -1384,6 +1384,8 @@ impl<'a> Format<'a> for Modifier { Modifier::Protected(keyword) => keyword.format(f), Modifier::Private(keyword) => keyword.format(f), Modifier::PrivateSet(keyword) => keyword.format(f), + Modifier::ProtectedSet(keyword) => keyword.format(f), + Modifier::PublicSet(keyword) => keyword.format(f), } }) } diff --git a/crates/lexer/src/lib.rs b/crates/lexer/src/lib.rs index 6e2f6aa..4dfe432 100644 --- a/crates/lexer/src/lib.rs +++ b/crates/lexer/src/lib.rs @@ -419,6 +419,22 @@ impl<'a, 'i> Lexer<'a, 'i> { break; } + // special case for `public(set)` + [b'(', ..] if length == 6 => { + if self.input.is_at(b"public(set)", true) { + break 'identifier (TokenKind::PublicSet, 6 + 5); + } + + break; + } + // special case for `protected(set)` + [b'(', ..] if length == 9 => { + if self.input.is_at(b"protected(set)", true) { + break 'identifier (TokenKind::ProtectedSet, 9 + 5); + } + + break; + } _ => { break; } diff --git a/crates/parser/src/internal/modifier.rs b/crates/parser/src/internal/modifier.rs index 6827b4a..4462f25 100644 --- a/crates/parser/src/internal/modifier.rs +++ b/crates/parser/src/internal/modifier.rs @@ -36,6 +36,8 @@ pub fn parse_optional_modifier(stream: &mut TokenStream<'_, '_>) -> Result Modifier::Abstract(utils::expect_any_keyword(stream)?), Some(T!["readonly"]) => Modifier::Readonly(utils::expect_any_keyword(stream)?), Some(T!["private(set)"]) => Modifier::PrivateSet(utils::expect_any_keyword(stream)?), + Some(T!["protected(set)"]) => Modifier::ProtectedSet(utils::expect_any_keyword(stream)?), + Some(T!["public(set)"]) => Modifier::PublicSet(utils::expect_any_keyword(stream)?), _ => return Ok(None), })) } diff --git a/crates/semantics/src/walker.rs b/crates/semantics/src/walker.rs index 1335d87..ccf024b 100644 --- a/crates/semantics/src/walker.rs +++ b/crates/semantics/src/walker.rs @@ -344,7 +344,7 @@ impl SemanticsWalker { last_read_visibility = Some(modifier.span()); } - Modifier::PrivateSet(_) => { + Modifier::PrivateSet(_) | Modifier::ProtectedSet(_) | Modifier::PublicSet(_) => { if let Some(last_visibility) = last_write_visibility { context.report( Issue::error(format!( @@ -1038,9 +1038,9 @@ impl SemanticsWalker { last_visibility = Some(modifier.span()); } } - Modifier::PrivateSet(_) => { + Modifier::PrivateSet(k) | Modifier::ProtectedSet(k) | Modifier::PublicSet(k) => { context.report( - Issue::error("`private(set)` modifier is not allowed on methods".to_string()) + Issue::error(format!("`{}` modifier is not allowed on methods", context.interner.lookup(&k.value))) .with_annotation( Annotation::primary(modifier.span()).with_message("`private(set)` modifier"), ) @@ -1591,7 +1591,7 @@ impl SemanticsWalker { let mut last_visibility: Option = None; for modifier in class_like_constant.modifiers.iter() { match modifier { - Modifier::Readonly(k) | Modifier::Static(k) | Modifier::Abstract(k) | Modifier::PrivateSet(k) => { + Modifier::Readonly(k) | Modifier::Static(k) | Modifier::Abstract(k) | Modifier::PrivateSet(k) | Modifier::ProtectedSet(k) | Modifier::PublicSet(k) => { context.report( Issue::error(format!( "`{}` modifier is not allowed on constants", @@ -2067,7 +2067,7 @@ impl Walker> for SemanticsWalker { for modifier in class.modifiers.iter() { match &modifier { - Modifier::Static(_) | Modifier::PrivateSet(_) => { + Modifier::Static(_) | Modifier::PrivateSet(_) | Modifier::ProtectedSet(_) | Modifier::PublicSet(_) => { context.report( Issue::error(format!( "class `{}` cannot have `{}` modifier", @@ -2786,6 +2786,8 @@ impl Walker> for SemanticsWalker { Modifier::Static(_) | Modifier::Abstract(_) | Modifier::PrivateSet(_) + | Modifier::ProtectedSet(_) + | Modifier::PublicSet(_) | Modifier::Public(_) | Modifier::Protected(_) | Modifier::Private(_) => { @@ -3297,7 +3299,7 @@ impl Walker> for SemanticsWalker { last_read_visibility = Some(modifier.span()); } } - Modifier::PrivateSet(_) => { + Modifier::PrivateSet(_) | Modifier::ProtectedSet(_) | Modifier::PublicSet(_) => { if let Some(s) = last_write_visibility { context.report( Issue::error(format!( diff --git a/crates/token/src/lib.rs b/crates/token/src/lib.rs index 9d18b0b..b6bbe7f 100644 --- a/crates/token/src/lib.rs +++ b/crates/token/src/lib.rs @@ -490,14 +490,14 @@ impl TokenKind { #[inline(always)] pub fn is_visibility_modifier(&self) -> bool { - matches!(self, T!["public" | "protected" | "private" | "private(set)"]) + matches!(self, T!["public" | "protected" | "private" | "private(set)" | "protected(set)" | "public(set)"]) } #[inline(always)] pub fn is_modifier(&self) -> bool { matches!( self, - T!["public" | "protected" | "private" | "private(set)" | "static" | "final" | "abstract" | "readonly"] + T!["public" | "protected" | "private" | "private(set)" | "protected(set)" | "public(set)" | "static" | "final" | "abstract" | "readonly"] ) } @@ -539,7 +539,9 @@ impl TokenKind { | "private" | "private(set)" | "protected" + | "protected(set)" | "public" + | "public(set)" | "include" | "include_once" | "eval" @@ -1160,9 +1162,15 @@ macro_rules! T { ("protected") => { $crate::TokenKind::Protected }; + ("protected(set)") => { + $crate::TokenKind::ProtectedSet + }; ("public") => { $crate::TokenKind::Public }; + ("public(set)") => { + $crate::TokenKind::PublicSet + }; ("Qualified\\Identifier") => { $crate::TokenKind::QualifiedIdentifier };