Skip to content

Commit

Permalink
fix(parser): should parser error when function declaration has no name
Browse files Browse the repository at this point in the history
  • Loading branch information
Dunqing committed May 29, 2024
1 parent b188778 commit 7cbab0b
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 33 deletions.
25 changes: 15 additions & 10 deletions crates/oxc_parser/src/js/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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())
}

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

Expand All @@ -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())?;

Expand Down Expand Up @@ -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);
Expand All @@ -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)
}
}
2 changes: 1 addition & 1 deletion crates/oxc_parser/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub enum Tristate {

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FunctionKind {
Declaration { single_statement: bool },
Declaration,
Expression,
DefaultExport,
TSDeclaration,
Expand Down
12 changes: 4 additions & 8 deletions crates/oxc_parser/src/ts/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand All @@ -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)
}

Expand Down
89 changes: 75 additions & 14 deletions tasks/coverage/parser_babel.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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 })
Expand Down Expand Up @@ -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*() {
Expand All @@ -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*() {
Expand All @@ -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*() {
Expand All @@ -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*() {
Expand All @@ -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*() {
Expand All @@ -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*() {
Expand Down
41 changes: 41 additions & 0 deletions tasks/coverage/parser_test262.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3587,13 +3587,29 @@ 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 │
16 │ async function () {} = 1;
· ─
╰────

× 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 │
Expand All @@ -3608,13 +3624,29 @@ 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 │
16 │ function() {} = 1;
· ─
╰────

× 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 │
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions tasks/coverage/parser_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 7cbab0b

Please sign in to comment.