fix(parser): should parser error when function declaration has no name (#3461)

https://oxc-project.github.io/oxc/playground/?code=3YCAAICNgICAgICAgICzncl%2FKeF7k4Y7upgY2l43c79%2FYxaAgA%3D%3D
This commit is contained in:
Dunqing 2024-05-30 19:58:50 +08:00 committed by GitHub
parent baed1ca645
commit 350cd9158a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 159 additions and 49 deletions

View file

@ -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) {

View file

@ -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),

View file

@ -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,

View file

@ -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.

View file

@ -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.

View file

@ -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");

View file

@ -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<Statement<'a>> {
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<BindingIdentifier<'a>> {
) -> Result<Option<BindingIdentifier<'a>>> {
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)
}
}

View file

@ -25,7 +25,7 @@ pub enum Tristate {
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FunctionKind {
Declaration { single_statement: bool },
Declaration,
Expression,
DefaultExport,
TSDeclaration,

View file

@ -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)
}

View file

@ -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*() {

View file

@ -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

View file

@ -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;