diff --git a/crates/oxc_semantic/src/checker/javascript.rs b/crates/oxc_semantic/src/checker/javascript.rs index 18a2aa1fc5989..c8c24f6a24785 100644 --- a/crates/oxc_semantic/src/checker/javascript.rs +++ b/crates/oxc_semantic/src/checker/javascript.rs @@ -432,6 +432,37 @@ pub fn check_function_declaration<'a>( }; } +// It is a Syntax Error if IsLabelledFunction(Statement) is true. +pub fn check_function_declaration_in_labeled_statement<'a>( + body: &Statement<'a>, + node: &AstNode<'a>, + ctx: &SemanticBuilder<'a>, +) { + if let Statement::FunctionDeclaration(decl) = body { + if ctx.strict_mode() { + ctx.error(function_declaration_strict(decl.span)); + } else { + // skip(1) for `LabeledStatement` + for kind in ctx.nodes.ancestor_kinds(node.id()).skip(1) { + match kind { + // Nested labeled statement + AstKind::LabeledStatement(_) => continue, + AstKind::ForOfStatement(_) + | AstKind::ForInStatement(_) + | AstKind::ForStatement(_) + | AstKind::WhileStatement(_) + | AstKind::DoWhileStatement(_) + | AstKind::WithStatement(_) + | AstKind::IfStatement(_) => break, + + _ => return, + } + } + ctx.error(function_declaration_non_strict(decl.span)); + } + }; +} + // It is a Syntax Error if any element of the LexicallyDeclaredNames of // StatementList also occurs in the VarDeclaredNames of StatementList. pub fn check_variable_declarator_redeclaration( diff --git a/crates/oxc_semantic/src/checker/mod.rs b/crates/oxc_semantic/src/checker/mod.rs index a879a90e1237b..0b02c41f790b4 100644 --- a/crates/oxc_semantic/src/checker/mod.rs +++ b/crates/oxc_semantic/src/checker/mod.rs @@ -51,7 +51,7 @@ pub fn check<'a>(node: &AstNode<'a>, ctx: &SemanticBuilder<'a>) { AstKind::ContinueStatement(stmt) => js::check_continue_statement(stmt, node, ctx), AstKind::LabeledStatement(stmt) => { js::check_labeled_statement(stmt, node, ctx); - js::check_function_declaration(&stmt.body, true, ctx); + js::check_function_declaration_in_labeled_statement(&stmt.body, node, ctx); } AstKind::ForInStatement(stmt) => { js::check_function_declaration(&stmt.body, false, ctx); diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index 3a0c73ec03ed8..948c34d445705 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -3,10 +3,7 @@ commit: 578ac4df parser_babel Summary: AST Parsed : 2303/2322 (99.18%) Positive Passed: 2282/2322 (98.28%) -Negative Passed: 1551/1673 (92.71%) -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/annex-b/enabled/3.1-sloppy-labeled-functions-if-body/input.js -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-fn-decl-labeled-inside-if/input.js -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-fn-decl-labeled-inside-loop/input.js +Negative Passed: 1554/1673 (92.89%) Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-startindex-and-startline-specified-without-startcolumn/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/startline-and-startcolumn-specified/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/startline-specified/input.js @@ -729,6 +726,13 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc 32 │ } ╰──── + × Invalid function declaration + ╭─[babel/packages/babel-parser/test/fixtures/annex-b/enabled/3.1-sloppy-labeled-functions-if-body/input.js:1:11] + 1 │ if (0) x: function f() {} + · ─────────────── + ╰──── + help: In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement + × Identifier `f` has already been declared ╭─[babel/packages/babel-parser/test/fixtures/annex-b/enabled/3.4-var-redeclaration-catch-binding/input.js:2:17] 1 │ try {} catch (e) { var e; } @@ -802,6 +806,20 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc ╰──── help: In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement + × Invalid function declaration + ╭─[babel/packages/babel-parser/test/fixtures/core/categorized/invalid-fn-decl-labeled-inside-if/input.js:1:18] + 1 │ if (1) foo: bar: function foo(){} + · ──────────────── + ╰──── + help: In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement + + × Invalid function declaration + ╭─[babel/packages/babel-parser/test/fixtures/core/categorized/invalid-fn-decl-labeled-inside-loop/input.js:1:21] + 1 │ while (1) foo: bar: function foo(){} + · ──────────────── + ╰──── + help: In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement + × Cannot assign to this expression ╭─[babel/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-postfix-operation/input.js:1:1] 1 │ a++ = t diff --git a/tasks/coverage/snapshots/parser_test262.snap b/tasks/coverage/snapshots/parser_test262.snap index 403a96c94604f..e430bf0b4b362 100644 --- a/tasks/coverage/snapshots/parser_test262.snap +++ b/tasks/coverage/snapshots/parser_test262.snap @@ -3,7 +3,7 @@ commit: bc5c1417 parser_test262 Summary: AST Parsed : 44293/44293 (100.00%) Positive Passed: 44293/44293 (100.00%) -Negative Passed: 4487/4519 (99.29%) +Negative Passed: 4505/4519 (99.69%) Expect Syntax Error: tasks/coverage/test262/test/language/expressions/class/class-name-ident-let-escaped.js Expect Syntax Error: tasks/coverage/test262/test/language/expressions/class/class-name-ident-let.js Expect Syntax Error: tasks/coverage/test262/test/language/expressions/class/class-name-ident-static-escaped.js @@ -18,24 +18,6 @@ Expect Syntax Error: tasks/coverage/test262/test/language/statements/class/class Expect Syntax Error: tasks/coverage/test262/test/language/statements/class/class-name-ident-static.js Expect Syntax Error: tasks/coverage/test262/test/language/statements/class/class-name-ident-yield-escaped.js Expect Syntax Error: tasks/coverage/test262/test/language/statements/class/class-name-ident-yield.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/do-while/labelled-fn-stmt.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for/labelled-fn-stmt-const.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for/labelled-fn-stmt-expr.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for/labelled-fn-stmt-let.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for/labelled-fn-stmt-var.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for-in/labelled-fn-stmt-const.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for-in/labelled-fn-stmt-let.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for-in/labelled-fn-stmt-lhs.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for-in/labelled-fn-stmt-var.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for-of/labelled-fn-stmt-const.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for-of/labelled-fn-stmt-let.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for-of/labelled-fn-stmt-lhs.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/for-of/labelled-fn-stmt-var.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/if/labelled-fn-stmt-first.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/if/labelled-fn-stmt-lone.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/if/labelled-fn-stmt-second.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/while/labelled-fn-stmt.js -Expect Syntax Error: tasks/coverage/test262/test/language/statements/with/labelled-fn-stmt.js × '0'-prefixed octal literals and octal escape sequences are deprecated ╭─[test262/test/annexB/language/expressions/template-literal/legacy-octal-escape-sequence-strict.js:19:4] @@ -33070,6 +33052,14 @@ Expect Syntax Error: tasks/coverage/test262/test/language/statements/with/labell ╰──── help: Try insert a semicolon here + × Invalid function declaration + ╭─[test262/test/language/statements/do-while/labelled-fn-stmt.js:19:20] + 18 │ + 19 │ do label1: label2: function f() {} while (false) + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + × Lexical declaration cannot appear in a single-statement context ╭─[test262/test/language/statements/do-while/let-array-with-newline.js:20:4] 19 │ @@ -33497,6 +33487,38 @@ Expect Syntax Error: tasks/coverage/test262/test/language/statements/with/labell 23 │ } ╰──── + × Invalid function declaration + ╭─[test262/test/language/statements/for/labelled-fn-stmt-const.js:19:44] + 18 │ + 19 │ for (const x = 0; false; ) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/for/labelled-fn-stmt-expr.js:19:34] + 18 │ + 19 │ for ( ; false; ) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/for/labelled-fn-stmt-let.js:19:38] + 18 │ + 19 │ for (let x; false; ) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/for/labelled-fn-stmt-var.js:19:38] + 18 │ + 19 │ for (var x; false; ) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + × Lexical declaration cannot appear in a single-statement context ╭─[test262/test/language/statements/for/let-array-with-newline.js:20:17] 19 │ @@ -34635,6 +34657,38 @@ Expect Syntax Error: tasks/coverage/test262/test/language/statements/with/labell · ──── ╰──── + × Invalid function declaration + ╭─[test262/test/language/statements/for-in/labelled-fn-stmt-const.js:19:37] + 18 │ + 19 │ for (const x in {}) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/for-in/labelled-fn-stmt-let.js:19:35] + 18 │ + 19 │ for (let x in {}) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/for-in/labelled-fn-stmt-lhs.js:19:31] + 18 │ + 19 │ for (x in {}) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/for-in/labelled-fn-stmt-var.js:19:35] + 18 │ + 19 │ for (var x in {}) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + × Lexical declaration cannot appear in a single-statement context ╭─[test262/test/language/statements/for-in/let-array-with-newline.js:20:21] 19 │ @@ -35362,6 +35416,38 @@ Expect Syntax Error: tasks/coverage/test262/test/language/statements/with/labell · ╰── `)` expected ╰──── + × Invalid function declaration + ╭─[test262/test/language/statements/for-of/labelled-fn-stmt-const.js:19:37] + 18 │ + 19 │ for (const x of []) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/for-of/labelled-fn-stmt-let.js:19:35] + 18 │ + 19 │ for (let x of []) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/for-of/labelled-fn-stmt-lhs.js:19:31] + 18 │ + 19 │ for (x of []) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/for-of/labelled-fn-stmt-var.js:19:35] + 18 │ + 19 │ for (var x of []) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + × Lexical declaration cannot appear in a single-statement context ╭─[test262/test/language/statements/for-of/let-array-with-newline.js:20:19] 19 │ @@ -36634,6 +36720,30 @@ Expect Syntax Error: tasks/coverage/test262/test/language/statements/with/labell ╰──── help: Try insert a semicolon here + × Invalid function declaration + ╭─[test262/test/language/statements/if/labelled-fn-stmt-first.js:30:28] + 29 │ + 30 │ if (false) label1: label2: function test262() {} else ; + · ───────────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/if/labelled-fn-stmt-lone.js:30:28] + 29 │ + 30 │ if (false) label1: label2: function test262() {} + · ───────────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × Invalid function declaration + ╭─[test262/test/language/statements/if/labelled-fn-stmt-second.js:30:34] + 29 │ + 30 │ if (true) ; else label1: label2: function test262() {} + · ───────────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + × Lexical declaration cannot appear in a single-statement context ╭─[test262/test/language/statements/if/let-array-with-newline.js:20:12] 19 │ @@ -38494,6 +38604,14 @@ Expect Syntax Error: tasks/coverage/test262/test/language/statements/with/labell ╰──── help: Try insert a semicolon here + × Invalid function declaration + ╭─[test262/test/language/statements/while/labelled-fn-stmt.js:19:31] + 18 │ + 19 │ while (false) label1: label2: function f() {} + · ─────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + × Lexical declaration cannot appear in a single-statement context ╭─[test262/test/language/statements/while/let-array-with-newline.js:20:15] 19 │ @@ -38586,6 +38704,21 @@ Expect Syntax Error: tasks/coverage/test262/test/language/statements/with/labell ╰──── help: Try insert a semicolon here + × Invalid function declaration + ╭─[test262/test/language/statements/with/labelled-fn-stmt.js:27:27] + 26 │ + 27 │ with ({}) label1: label2: function test262() {} + · ───────────────────── + ╰──── + help: In strict mode code, functions can only be declared at top level or inside a block + + × 'with' statements are not allowed + ╭─[test262/test/language/statements/with/labelled-fn-stmt.js:27:1] + 26 │ + 27 │ with ({}) label1: label2: function test262() {} + · ──── + ╰──── + × Lexical declaration cannot appear in a single-statement context ╭─[test262/test/language/statements/with/let-array-with-newline.js:22:15] 21 │ if (false) {