LibJS: Add missing early errors in Rust parser

- Reject `true`, `false`, `null` as label identifiers
- Reject generator declarations in if-statement bodies (not covered
  by Annex B)
- Reject `await` as label in class static init blocks and modules
- Reject `arguments` in class static initialization blocks
- Reject generator shorthand without method body in object literals
- Reject `get constructor()` / `set constructor()` in class bodies
- Reject `super.#private` member access
This commit is contained in:
Andreas Kling 2026-03-19 19:57:05 -05:00 committed by Andreas Kling
parent 71713017c5
commit 6029a3d40e
3 changed files with 56 additions and 10 deletions

View file

@ -1081,6 +1081,13 @@ impl Parser<'_> {
}
}
// https://tc39.es/ecma262/#sec-class-definitions-static-semantics-early-errors
// It is a Syntax Error if PropName of MethodDefinition is "constructor"
// and SpecialMethod of MethodDefinition is true (getter/setter).
if !is_static && (is_getter || is_setter) && key_value.as_deref() == Some(ctor_name) {
self.syntax_error("Class constructor may not be an accessor");
}
let method_kind = if is_constructor {
MethodKind::Constructor
} else if is_getter {

View file

@ -202,13 +202,21 @@ impl Parser<'_> {
// falsely flagging parameter names like `function f(arguments)`.
if let ExpressionKind::Identifier(ref id) = expression.inner
&& id.name == utf16!("arguments")
&& !self.flags.strict_mode
&& !self
.scope_collector
.has_declaration_in_current_function(&id.name)
{
self.scope_collector
.set_contains_access_to_arguments_object_in_non_strict_mode();
// https://tc39.es/ecma262/#sec-class-static-initialization-blocks
// It is a Syntax Error if ContainsArguments of ClassStaticBlockBody is true.
if self.flags.in_class_static_init_block {
self.syntax_error(
"'arguments' is not allowed in class static initialization blocks",
);
} else if !self.flags.strict_mode
&& !self
.scope_collector
.has_declaration_in_current_function(&id.name)
{
self.scope_collector
.set_contains_access_to_arguments_object_in_non_strict_mode();
}
}
if !should_continue {
@ -918,6 +926,12 @@ impl Parser<'_> {
TokenType::Period => {
self.consume();
if self.match_token(TokenType::PrivateIdentifier) {
// https://tc39.es/ecma262/#sec-static-semantics-early-errors
// It is a Syntax Error if MemberExpression is SuperProperty
// and the PrivateIdentifier is present.
if matches!(lhs.inner, ExpressionKind::Super) {
self.syntax_error("Cannot access private field or method via 'super'");
}
// C++ uses rule_start (period position) for property identifiers.
let id = self.parse_private_identifier(start);
let property = self.expression(start, ExpressionKind::PrivateIdentifier(id));
@ -1636,6 +1650,10 @@ impl Parser<'_> {
if is_async {
self.syntax_error("Expected function after async keyword");
}
// Generator shorthand requires a method body.
if is_generator {
self.syntax_error("Expected method after generator star");
}
if is_getter || is_setter {
let method_kind = if is_getter {

View file

@ -269,7 +269,13 @@ impl Parser<'_> {
let predicate = self.parse_expression_any();
self.consume_token(TokenType::ParenClose);
let consequent = if !self.flags.strict_mode && self.match_token(TokenType::Function) {
// https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
// Annex B allows FunctionDeclarations (but NOT GeneratorDeclarations) in
// IfStatement bodies in sloppy mode.
let consequent = if !self.flags.strict_mode
&& self.match_token(TokenType::Function)
&& self.next_token().token_type != TokenType::Asterisk
{
self.parse_function_declaration_as_block_statement(start)
} else {
self.parse_statement(false)
@ -277,7 +283,10 @@ impl Parser<'_> {
let alternate = if self.match_token(TokenType::Else) {
self.consume();
if !self.flags.strict_mode && self.match_token(TokenType::Function) {
if !self.flags.strict_mode
&& self.match_token(TokenType::Function)
&& self.next_token().token_type != TokenType::Asterisk
{
Some(Box::new(
self.parse_function_declaration_as_block_statement(start),
))
@ -819,6 +828,14 @@ impl Parser<'_> {
self.discard_saved_state();
self.consume(); // consume :
// https://tc39.es/ecma262/#sec-labelled-statements
// LabelIdentifier : Identifier (not ReservedWord)
// `true`, `false`, and `null` are reserved words and cannot be labels.
if token.token_type == TokenType::BoolLiteral || token.token_type == TokenType::NullLiteral
{
self.syntax_error("Reserved word cannot be used as a label");
}
if self.flags.strict_mode
&& (label == utf16!("let") || crate::parser::is_strict_reserved_word(&label))
{
@ -827,8 +844,12 @@ impl Parser<'_> {
if self.flags.in_generator_function_context && label == utf16!("yield") {
self.syntax_error("'yield' label is not allowed in generator function context");
}
if self.flags.await_expression_is_valid && label == utf16!("await") {
self.syntax_error("'await' label is not allowed in async function context");
if (self.flags.await_expression_is_valid
|| self.flags.in_class_static_init_block
|| self.program_type == crate::parser::ProgramType::Module)
&& label == utf16!("await")
{
self.syntax_error("'await' is not allowed as a label in this context");
}
if self.labels_in_scope.contains_key(label.as_slice()) {