diff --git a/crates/oxc_linter/src/rules/eslint/array_callback_return/return_checker.rs b/crates/oxc_linter/src/rules/eslint/array_callback_return/return_checker.rs index 925a8c208210c..7b6647bb12e24 100644 --- a/crates/oxc_linter/src/rules/eslint/array_callback_return/return_checker.rs +++ b/crates/oxc_linter/src/rules/eslint/array_callback_return/return_checker.rs @@ -279,7 +279,7 @@ mod tests { fn test_switch_always_explicit() { // Return Explicit let always_explicit = r#" - function() { + function d() { switch (a) { case "C": switch (b) { @@ -299,7 +299,7 @@ mod tests { #[test] fn test_switch_always_implicit() { let always_implicit = r#" - function() { + function d() { switch (a) { case "C": switch (b) { @@ -319,7 +319,7 @@ mod tests { #[test] fn test_switch_always_mixed() { let always_mixed = r#" - function() { + function d() { switch (a) { case "C": switch (b) { diff --git a/crates/oxc_linter/src/rules/eslint/no_obj_calls.rs b/crates/oxc_linter/src/rules/eslint/no_obj_calls.rs index 65fb95448f82d..095dfc2560ca5 100644 --- a/crates/oxc_linter/src/rules/eslint/no_obj_calls.rs +++ b/crates/oxc_linter/src/rules/eslint/no_obj_calls.rs @@ -199,7 +199,7 @@ fn test() { ("let obj = Intl();", None), ("let newObj = new Reflect();", None), ("let obj = Reflect();", None), - ("function() { JSON.parse(Atomics()) }", None), + ("function d() { JSON.parse(Atomics()) }", None), // reference test cases ("let j = JSON; j();", None), ("let a = JSON; let b = a; let c = b; b();", None), diff --git a/crates/oxc_linter/src/rules/jsdoc/require_yields.rs b/crates/oxc_linter/src/rules/jsdoc/require_yields.rs index 97b2af6be28c7..b6a4d8aeac887 100644 --- a/crates/oxc_linter/src/rules/jsdoc/require_yields.rs +++ b/crates/oxc_linter/src/rules/jsdoc/require_yields.rs @@ -102,13 +102,13 @@ impl Rule for RequireYields { // This rule checks generator function should have JSDoc `@yields` tag. // By default, this rule only checks: // ``` - // function*() { yield withValue; } + // function*d() { yield withValue; } // ``` // // If `config.forceRequireYields` is `true`, also checks: // ``` - // function*() {} - // function*() { yield; } + // function*d() {} + // function*d() { yield; } // ``` // // If generator function does not have JSDoc, it will be skipped. @@ -673,7 +673,7 @@ fn test() { * @generator * @yields */ - function*() {yield 1;} + function*d() {yield 1;} ", Some(serde_json::json!([ { @@ -853,7 +853,7 @@ fn test() { * @function * @generator */ - function*() {} + function*d() {} ", Some(serde_json::json!([ { @@ -1446,7 +1446,7 @@ fn test() { * fail(`@generator`+missing `@yields`, with config) * @generator */ - function*() {} + function*d() {} ", Some(serde_json::json!([{ "withGeneratorTag": true, }])), None, diff --git a/crates/oxc_linter/src/snapshots/no_obj_calls.snap b/crates/oxc_linter/src/snapshots/no_obj_calls.snap index 8ce0a2e2e3223..133bbce699519 100644 --- a/crates/oxc_linter/src/snapshots/no_obj_calls.snap +++ b/crates/oxc_linter/src/snapshots/no_obj_calls.snap @@ -108,9 +108,9 @@ expression: no_obj_calls help: Reflect is not a function. ⚠ eslint(no-obj-calls): Disallow calling some global objects as functions - ╭─[no_obj_calls.tsx:1:25] - 1 │ function() { JSON.parse(Atomics()) } - · ───────── + ╭─[no_obj_calls.tsx:1:27] + 1 │ function d() { JSON.parse(Atomics()) } + · ───────── ╰──── help: Atomics is not a function. diff --git a/crates/oxc_linter/src/snapshots/require_yields.snap b/crates/oxc_linter/src/snapshots/require_yields.snap index 78ec43cbefdf2..2eb1ba4074cd0 100644 --- a/crates/oxc_linter/src/snapshots/require_yields.snap +++ b/crates/oxc_linter/src/snapshots/require_yields.snap @@ -97,8 +97,8 @@ expression: require_yields ⚠ eslint-plugin-jsdoc(require-yields): Missing JSDoc `@yields` declaration for generator function. ╭─[require_yields.tsx:6:25] 5 │ */ - 6 │ function*() {} - · ────────────── + 6 │ function*d() {} + · ─────────────── 7 │ ╰──── help: Add `@yields` tag to the JSDoc comment. diff --git a/crates/oxc_module_lexer/tests/esm.rs b/crates/oxc_module_lexer/tests/esm.rs index 514d08b0251b1..b82d18ad9bc82 100644 --- a/crates/oxc_module_lexer/tests/esm.rs +++ b/crates/oxc_module_lexer/tests/esm.rs @@ -728,7 +728,7 @@ import { g } from './test-circular2.js'; #[test] fn comments() { - let source = " /*\n VERSION\n */\nimport util from 'util';\n\n//\nfunction x() {\n}\n\n/**/\n// '\n/* / */\n/*\n\n * export { b }\n\\*/\nexport { a }\n\n function () {\n/***/\n }\n "; + let source = " /*\n VERSION\n */\nimport util from 'util';\n\n//\nfunction x() {\n}\n\n/**/\n// '\n/* / */\n/*\n\n * export { b }\n\\*/\nexport { a }\n\n function d() {\n/***/\n }\n "; let ModuleLexer { imports, exports, .. } = parse(source); assert_eq!(imports.len(), 1); assert_eq!(source.slice(imports[0].s, imports[0].e), "util"); diff --git a/crates/oxc_parser/src/js/function.rs b/crates/oxc_parser/src/js/function.rs index e3d7bd22ef21a..da5d063420d93 100644 --- a/crates/oxc_parser/src/js/function.rs +++ b/crates/oxc_parser/src/js/function.rs @@ -11,7 +11,7 @@ use super::{list::FormalParameterList, FunctionKind}; impl FunctionKind { pub(crate) fn is_id_required(self) -> bool { - matches!(self, Self::Declaration { single_statement: true }) + matches!(self, Self::Declaration) } pub(crate) fn is_expression(self) -> bool { @@ -80,7 +80,7 @@ impl<'a> ParserImpl<'a> { } let function_type = match func_kind { - FunctionKind::Declaration { .. } | FunctionKind::DefaultExport => { + FunctionKind::Declaration | FunctionKind::DefaultExport => { if body.is_none() { FunctionType::TSDeclareFunction } else { @@ -123,8 +123,7 @@ impl<'a> ParserImpl<'a> { &mut self, stmt_ctx: StatementContext, ) -> Result> { - let func_kind = - FunctionKind::Declaration { single_statement: stmt_ctx.is_single_statement() }; + let func_kind = FunctionKind::Declaration; let decl = self.parse_function_impl(func_kind)?; if stmt_ctx.is_single_statement() { if decl.r#async { @@ -153,7 +152,7 @@ impl<'a> ParserImpl<'a> { let r#async = self.eat(Kind::Async); self.expect(Kind::Function)?; let generator = self.eat(Kind::Star); - let id = self.parse_function_id(func_kind, r#async, generator); + let id = self.parse_function_id(func_kind, r#async, generator)?; self.parse_function(span, id, r#async, generator, func_kind, Modifiers::empty()) } @@ -168,7 +167,7 @@ impl<'a> ParserImpl<'a> { let r#async = modifiers.contains(ModifierKind::Async); self.expect(Kind::Function)?; let generator = self.eat(Kind::Star); - let id = self.parse_function_id(func_kind, r#async, generator); + let id = self.parse_function_id(func_kind, r#async, generator)?; self.parse_function(start_span, id, r#async, generator, func_kind, modifiers) } @@ -182,7 +181,7 @@ impl<'a> ParserImpl<'a> { self.expect(Kind::Function)?; let generator = self.eat(Kind::Star); - let id = self.parse_function_id(func_kind, r#async, generator); + let id = self.parse_function_id(func_kind, r#async, generator)?; let function = self.parse_function(span, id, r#async, generator, func_kind, Modifiers::empty())?; @@ -257,7 +256,7 @@ impl<'a> ParserImpl<'a> { kind: FunctionKind, r#async: bool, generator: bool, - ) -> Option> { + ) -> Result>> { let ctx = self.ctx; if kind.is_expression() { self.ctx = self.ctx.and_await(r#async).and_yield(generator); @@ -270,9 +269,15 @@ impl<'a> ParserImpl<'a> { self.ctx = ctx; if kind.is_id_required() && id.is_none() { - self.error(diagnostics::expect_function_name(self.cur_token().span())); + match self.cur_kind() { + Kind::LParen => { + self.error(diagnostics::expect_function_name(self.cur_token().span())); + } + kind if kind.is_reserved_keyword() => self.expect_without_advance(Kind::Ident)?, + _ => {} + } } - id + Ok(id) } } diff --git a/crates/oxc_parser/src/js/mod.rs b/crates/oxc_parser/src/js/mod.rs index 28bb73a264592..27f4667bf48d4 100644 --- a/crates/oxc_parser/src/js/mod.rs +++ b/crates/oxc_parser/src/js/mod.rs @@ -25,7 +25,7 @@ pub enum Tristate { #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum FunctionKind { - Declaration { single_statement: bool }, + Declaration, Expression, DefaultExport, TSDeclaration, diff --git a/crates/oxc_parser/src/ts/statement.rs b/crates/oxc_parser/src/ts/statement.rs index f454e969ccbc2..b14e66ee5f08c 100644 --- a/crates/oxc_parser/src/ts/statement.rs +++ b/crates/oxc_parser/src/ts/statement.rs @@ -325,14 +325,10 @@ impl<'a> ParserImpl<'a> { self.parse_ts_declare_function(start_span, modifiers) .map(Declaration::FunctionDeclaration) } else if self.ts_enabled() { - self.parse_ts_function_impl( - start_span, - FunctionKind::Declaration { single_statement: true }, - modifiers, - ) - .map(Declaration::FunctionDeclaration) + self.parse_ts_function_impl(start_span, FunctionKind::Declaration, modifiers) + .map(Declaration::FunctionDeclaration) } else { - self.parse_function_impl(FunctionKind::Declaration { single_statement: true }) + self.parse_function_impl(FunctionKind::Declaration) .map(Declaration::FunctionDeclaration) } } @@ -348,7 +344,7 @@ impl<'a> ParserImpl<'a> { let r#async = modifiers.contains(ModifierKind::Async); self.expect(Kind::Function)?; let func_kind = FunctionKind::TSDeclaration; - let id = self.parse_function_id(func_kind, r#async, false); + let id = self.parse_function_id(func_kind, r#async, false)?; self.parse_function(start_span, id, r#async, false, func_kind, modifiers) } diff --git a/tasks/coverage/parser_babel.snap b/tasks/coverage/parser_babel.snap index 45959e3725996..dd490c55dfb9b 100644 --- a/tasks/coverage/parser_babel.snap +++ b/tasks/coverage/parser_babel.snap @@ -1383,32 +1383,32 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" · ──── ╰──── - × Expected `(` but found `null` + × Expected `Identifier` but found `null` ╭─[core/uncategorised/401/input.js:1:10] 1 │ function null() { } · ──┬─ - · ╰── `(` expected + · ╰── `Identifier` expected ╰──── - × Expected `(` but found `true` + × Expected `Identifier` but found `true` ╭─[core/uncategorised/402/input.js:1:10] 1 │ function true() { } · ──┬─ - · ╰── `(` expected + · ╰── `Identifier` expected ╰──── - × Expected `(` but found `false` + × Expected `Identifier` but found `false` ╭─[core/uncategorised/403/input.js:1:10] 1 │ function false() { } · ──┬── - · ╰── `(` expected + · ╰── `Identifier` expected ╰──── - × Expected `(` but found `if` + × Expected `Identifier` but found `if` ╭─[core/uncategorised/404/input.js:1:10] 1 │ function if() { } · ─┬ - · ╰── `(` expected + · ╰── `Identifier` expected ╰──── × Expected a semicolon or an implicit semicolon after a statement, but found none @@ -3667,11 +3667,11 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" ╰──── help: Try insert a semicolon here - × Expected `(` but found `default` + × Expected `Identifier` but found `default` ╭─[es2015/uncategorised/226/input.js:1:10] 1 │ function default() {} · ───┬─── - · ╰── `(` expected + · ╰── `Identifier` expected ╰──── × Cannot assign to 'eval' in strict mode @@ -4238,18 +4238,18 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" · ╰── `:` expected ╰──── - × Expected `(` but found `enum` + × Expected `Identifier` but found `enum` ╭─[es2015/uncategorised/376/input.js:1:10] 1 │ function enum() {} · ──┬─ - · ╰── `(` expected + · ╰── `Identifier` expected ╰──── - × Expected `(` but found `enum` + × Expected `Identifier` but found `enum` ╭─[es2015/uncategorised/377/input.js:1:10] 1 │ function enum() {} · ──┬─ - · ╰── `(` expected + · ╰── `Identifier` expected ╰──── × Expected `{` but found `enum` @@ -5010,6 +5010,13 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" · ────────── ╰──── + × Expected function name + ╭─[es2017/async-functions/invalid-escape-sequence-function/input.js:1:20] + 1 │ \u0061sync function() { await x } + · ─ + ╰──── + help: Function name is required in function declaration or named export + × Keywords cannot contain escape characters ╭─[es2017/async-functions/invalid-escape-sequence-function-list/input.js:1:2] 1 │ (\u0061sync function() { await x }) @@ -7126,6 +7133,15 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" · ───── ╰──── + × Expected function name + ╭─[esprima/es2015-generator/generator-parameter-binding-element/input.js:2:13] + 1 │ (function*() { + 2 │ function(x = yield 3) {} + · ─ + 3 │ }) + ╰──── + help: Function name is required in function declaration or named export + × A 'yield' expression is only allowed in a generator body. ╭─[esprima/es2015-generator/generator-parameter-binding-element/input.js:2:18] 1 │ (function*() { @@ -7143,6 +7159,15 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" 3 │ }) ╰──── + × Expected function name + ╭─[esprima/es2015-generator/generator-parameter-binding-property/input.js:2:13] + 1 │ (function*() { + 2 │ function({x: y = yield 3}) {} + · ─ + 3 │ }) + ╰──── + help: Function name is required in function declaration or named export + × A 'yield' expression is only allowed in a generator body. ╭─[esprima/es2015-generator/generator-parameter-binding-property/input.js:2:22] 1 │ (function*() { @@ -7160,6 +7185,15 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" 3 │ }) ╰──── + × Expected function name + ╭─[esprima/es2015-generator/generator-parameter-computed-property-name/input.js:2:13] + 1 │ (function*() { + 2 │ function({[yield 3]: y}) {} + · ─ + 3 │ }) + ╰──── + help: Function name is required in function declaration or named export + × A 'yield' expression is only allowed in a generator body. ╭─[esprima/es2015-generator/generator-parameter-computed-property-name/input.js:2:16] 1 │ (function*() { @@ -7177,6 +7211,15 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" 3 │ }) ╰──── + × Expected function name + ╭─[esprima/es2015-generator/generator-parameter-invalid-binding-element/input.js:2:14] + 1 │ (function*() { + 2 │ function*(x = yield 3) {} + · ─ + 3 │ }) + ╰──── + help: Function name is required in function declaration or named export + × yield expression not allowed in formal parameter ╭─[esprima/es2015-generator/generator-parameter-invalid-binding-element/input.js:2:19] 1 │ (function*() { @@ -7186,6 +7229,15 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" 3 │ }) ╰──── + × Expected function name + ╭─[esprima/es2015-generator/generator-parameter-invalid-binding-property/input.js:2:14] + 1 │ (function*() { + 2 │ function*({x: y = yield 3}) {} + · ─ + 3 │ }) + ╰──── + help: Function name is required in function declaration or named export + × yield expression not allowed in formal parameter ╭─[esprima/es2015-generator/generator-parameter-invalid-binding-property/input.js:2:23] 1 │ (function*() { @@ -7195,6 +7247,15 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" 3 │ }) ╰──── + × Expected function name + ╭─[esprima/es2015-generator/generator-parameter-invalid-computed-property-name/input.js:2:14] + 1 │ (function*() { + 2 │ function*({[yield 3]: y}) {} + · ─ + 3 │ }) + ╰──── + help: Function name is required in function declaration or named export + × yield expression not allowed in formal parameter ╭─[esprima/es2015-generator/generator-parameter-invalid-computed-property-name/input.js:2:17] 1 │ (function*() { diff --git a/tasks/coverage/parser_test262.snap b/tasks/coverage/parser_test262.snap index e4f2aee8852a2..fc54699278fa6 100644 --- a/tasks/coverage/parser_test262.snap +++ b/tasks/coverage/parser_test262.snap @@ -3587,6 +3587,14 @@ Expect Syntax Error: "language/import/import-attributes/json-named-bindings.js" · ─── ╰──── + × Expected function name + ╭─[language/expressions/assignmenttargettype/direct-primaryexpression-asyncfunctionexpression.js:16:16] + 15 │ + 16 │ async function () {} = 1; + · ─ + ╰──── + help: Function name is required in function declaration or named export + × Unexpected token ╭─[language/expressions/assignmenttargettype/direct-primaryexpression-asyncfunctionexpression.js:16:22] 15 │ @@ -3594,6 +3602,14 @@ Expect Syntax Error: "language/import/import-attributes/json-named-bindings.js" · ─ ╰──── + × Expected function name + ╭─[language/expressions/assignmenttargettype/direct-primaryexpression-asyncgeneratorexpression.js:16:16] + 15 │ + 16 │ async function () {} = 1; + · ─ + ╰──── + help: Function name is required in function declaration or named export + × Unexpected token ╭─[language/expressions/assignmenttargettype/direct-primaryexpression-asyncgeneratorexpression.js:16:22] 15 │ @@ -3608,6 +3624,14 @@ Expect Syntax Error: "language/import/import-attributes/json-named-bindings.js" · ─ ╰──── + × Expected function name + ╭─[language/expressions/assignmenttargettype/direct-primaryexpression-functionexpression.js:16:9] + 15 │ + 16 │ function() {} = 1; + · ─ + ╰──── + help: Function name is required in function declaration or named export + × Unexpected token ╭─[language/expressions/assignmenttargettype/direct-primaryexpression-functionexpression.js:16:15] 15 │ @@ -3615,6 +3639,14 @@ Expect Syntax Error: "language/import/import-attributes/json-named-bindings.js" · ─ ╰──── + × Expected function name + ╭─[language/expressions/assignmenttargettype/direct-primaryexpression-generatorexpression.js:16:12] + 15 │ + 16 │ function * () {} = 1; + · ─ + ╰──── + help: Function name is required in function declaration or named export + × Unexpected token ╭─[language/expressions/assignmenttargettype/direct-primaryexpression-generatorexpression.js:16:18] 15 │ @@ -28778,6 +28810,15 @@ Expect Syntax Error: "language/import/import-attributes/json-named-bindings.js" ╰──── help: Wrap this declaration in a block statement + × Expected function name + ╭─[language/statements/expression/S12.4_A1.js:19:9] + 18 │ //CHECK#1 + 19 │ function(){}(); + · ─ + 20 │ // + ╰──── + help: Function name is required in function declaration or named export + × Empty parenthesized expression ╭─[language/statements/expression/S12.4_A1.js:19:13] 18 │ //CHECK#1 diff --git a/tasks/coverage/parser_typescript.snap b/tasks/coverage/parser_typescript.snap index 63693bb3ff5c6..ab83b4ff64dde 100644 --- a/tasks/coverage/parser_typescript.snap +++ b/tasks/coverage/parser_typescript.snap @@ -16857,6 +16857,13 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" · ╰── `(` expected ╰──── + × Expected function name + ╭─[conformance/parser/ecmascript5/ErrorRecovery/parserEqualsGreaterThanAfterFunction2.ts:1:10] + 1 │ function (a => b; + · ─ + ╰──── + help: Function name is required in function declaration or named export + × Expected `,` but found `=>` ╭─[conformance/parser/ecmascript5/ErrorRecovery/parserEqualsGreaterThanAfterFunction2.ts:1:13] 1 │ function (a => b;