mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
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:
parent
baed1ca645
commit
350cd9158a
12 changed files with 159 additions and 49 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ pub enum Tristate {
|
|||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum FunctionKind {
|
||||
Declaration { single_statement: bool },
|
||||
Declaration,
|
||||
Expression,
|
||||
DefaultExport,
|
||||
TSDeclaration,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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*() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue