diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index d53ca7655..96ee54849 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1602,6 +1602,10 @@ impl<'a> BindingPatternKind<'a> { pub fn is_binding_identifier(&self) -> bool { matches!(self, Self::BindingIdentifier(_)) } + + pub fn is_assignment_pattern(&self) -> bool { + matches!(self, Self::AssignmentPattern(_)) + } } #[derive(Debug, Hash)] diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index 8ab930191..cf45cc2f7 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -40,9 +40,25 @@ fn check_variable_declarator(decl: &VariableDeclarator, ctx: &SemanticBuilder<'_ } fn check_formal_parameters(params: &FormalParameters, ctx: &SemanticBuilder<'_>) { + #[derive(Debug, Error, Diagnostic)] + #[error("A required parameter cannot follow an optional parameter.")] + #[diagnostic()] + struct RequiredParameterAfterOptionalParameter(#[label] Span); + if !params.is_empty() && params.kind == FormalParameterKind::Signature { check_duplicate_bound_names(params, ctx); } + + let mut has_optional = false; + for item in ¶ms.items { + // function a(optional?: number, required: number) { } + if has_optional && !item.pattern.optional && !item.pattern.kind.is_assignment_pattern() { + ctx.error(RequiredParameterAfterOptionalParameter(item.span)); + } + if item.pattern.optional { + has_optional = true; + } + } } fn check_duplicate_bound_names(bound_names: &T, ctx: &SemanticBuilder<'_>) { diff --git a/tasks/coverage/parser_babel.snap b/tasks/coverage/parser_babel.snap index e07c26c1a..921aa5ecf 100644 --- a/tasks/coverage/parser_babel.snap +++ b/tasks/coverage/parser_babel.snap @@ -1,7 +1,7 @@ parser_babel Summary: AST Parsed : 2090/2096 (99.71%) -Positive Passed: 2088/2096 (99.62%) -Negative Passed: 1351/1500 (90.07%) +Positive Passed: 2086/2096 (99.52%) +Negative Passed: 1355/1500 (90.33%) Expect Syntax Error: "annex-b/disabled/1.1-html-comments-close/input.js" Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions/input.js" Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-if-body/input.js" @@ -71,7 +71,6 @@ Expect Syntax Error: "typescript/class/modifiers-index-signatures/input.ts" Expect Syntax Error: "typescript/class/modifiers-invalid-order/input.ts" Expect Syntax Error: "typescript/class/modifiers-override-errors/input.ts" Expect Syntax Error: "typescript/class/parameter-properties-binding-patterns/input.ts" -Expect Syntax Error: "typescript/class/parameter-properties-not-constructor/input.ts" Expect Syntax Error: "typescript/class/private-fields-modifier-abstract/input.ts" Expect Syntax Error: "typescript/class/private-fields-modifier-private/input.ts" Expect Syntax Error: "typescript/class/private-fields-modifier-protected/input.ts" @@ -94,9 +93,6 @@ Expect Syntax Error: "typescript/expect-plugin/export-type-named/input.js" Expect Syntax Error: "typescript/export/double-declare/input.ts" Expect Syntax Error: "typescript/export/equals-in-script/input.ts" Expect Syntax Error: "typescript/function/empty-type-parameters/input.ts" -Expect Syntax Error: "typescript/function/parameter-properties/input.ts" -Expect Syntax Error: "typescript/function/pattern-optional-parameters/input.ts" -Expect Syntax Error: "typescript/function/pattern-optional-parameters-arrow/input.ts" Expect Syntax Error: "typescript/import/equals-in-script/input.ts" Expect Syntax Error: "typescript/import/equals-require-in-script/input.ts" Expect Syntax Error: "typescript/import/export-import-type/input.ts" @@ -187,6 +183,22 @@ Expect to Parse: "typescript/class/constructor-with-modifier-names/input.ts" 4 │ } ╰──── +Expect to Parse: "typescript/class/parameter-properties/input.ts" + × A required parameter cannot follow an optional parameter. + ╭─[typescript/class/parameter-properties/input.ts:6:1] + 6 │ private pi?: number, + 7 │ public readonly pur, + · ─────────────────── + 8 │ // Also works on AssignmentPattern + ╰──── + +Expect to Parse: "typescript/function/declare-pattern-parameters/input.ts" + × A required parameter cannot follow an optional parameter. + ╭─[typescript/function/declare-pattern-parameters/input.ts:1:1] + 1 │ declare function f([]?, {}) + · ── + ╰──── + Expect to Parse: "typescript/interface/get-set-properties/input.ts" × Expected `(` but found `:` ╭─[typescript/interface/get-set-properties/input.ts:1:1] @@ -9775,6 +9787,14 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" ╰──── help: Try insert a semicolon here + × A required parameter cannot follow an optional parameter. + ╭─[typescript/class/parameter-properties-not-constructor/input.ts:6:1] + 6 │ private pi?: number, + 7 │ public readonly pur, + · ─────────────────── + 8 │ // Also works on AssignmentPattern + ╰──── + × Unexpected token ╭─[typescript/const/invalid-initializer-ambient-context/input.ts:1:1] 1 │ declare module N { @@ -9822,6 +9842,26 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" · ─── ╰──── + × A required parameter cannot follow an optional parameter. + ╭─[typescript/function/parameter-properties/input.ts:5:1] + 5 │ private pi?: number, + 6 │ public readonly pur, + · ─────────────────── + 7 │ readonly x = 0, + ╰──── + + × A required parameter cannot follow an optional parameter. + ╭─[typescript/function/pattern-optional-parameters/input.ts:1:1] + 1 │ function f([]?, {}) {} + · ── + ╰──── + + × A required parameter cannot follow an optional parameter. + ╭─[typescript/function/pattern-optional-parameters-arrow/input.ts:1:1] + 1 │ ([]?, {}) => {} + · ── + ╰──── + × Expected a semicolon or an implicit semicolon after a statement, but found none ╭─[typescript/interface/declare-new-line/input.ts:1:1] 1 │ declare interface diff --git a/tasks/coverage/parser_typescript.snap b/tasks/coverage/parser_typescript.snap index 4738f23a6..66952b913 100644 --- a/tasks/coverage/parser_typescript.snap +++ b/tasks/coverage/parser_typescript.snap @@ -1,7 +1,7 @@ parser_typescript Summary: AST Parsed : 5239/5243 (99.92%) Positive Passed: 5232/5243 (99.79%) -Negative Passed: 1024/4879 (20.99%) +Negative Passed: 1025/4879 (21.01%) Expect Syntax Error: "compiler/ClassDeclaration10.ts" Expect Syntax Error: "compiler/ClassDeclaration11.ts" Expect Syntax Error: "compiler/ClassDeclaration13.ts" @@ -3210,7 +3210,6 @@ Expect Syntax Error: "conformance/parser/ecmascript5/ParameterLists/parserParame Expect Syntax Error: "conformance/parser/ecmascript5/ParameterLists/parserParameterList16.ts" Expect Syntax Error: "conformance/parser/ecmascript5/ParameterLists/parserParameterList17.ts" Expect Syntax Error: "conformance/parser/ecmascript5/ParameterLists/parserParameterList2.ts" -Expect Syntax Error: "conformance/parser/ecmascript5/ParameterLists/parserParameterList3.ts" Expect Syntax Error: "conformance/parser/ecmascript5/ParameterLists/parserParameterList4.ts" Expect Syntax Error: "conformance/parser/ecmascript5/ParameterLists/parserParameterList7.ts" Expect Syntax Error: "conformance/parser/ecmascript5/ParameterLists/parserParameterList8.ts" @@ -6370,6 +6369,13 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" ╰──── help: Try insert a semicolon here + × A required parameter cannot follow an optional parameter. + ╭─[compiler/fatarrowfunctionsOptionalArgsErrors1.ts:1:1] + 1 │ (arg1?, arg2) => 101; + · ──── + 2 │ (...arg?) => 102; + ╰──── + × A rest parameter cannot have an initializer ╭─[compiler/fatarrowfunctionsOptionalArgsErrors1.ts:3:1] 3 │ (...arg) => 103; @@ -15798,6 +15804,22 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" 10 │ function fn5() { } ╰──── + × A required parameter cannot follow an optional parameter. + ╭─[conformance/functions/functionOverloadErrorsSyntax.ts:1:1] + 1 │ //Function overload signature with optional parameter followed by non-optional parameter + 2 │ function fn4a(x?: number, y: string); + · ───────── + 3 │ function fn4a() { } + ╰──── + + × A required parameter cannot follow an optional parameter. + ╭─[conformance/functions/functionOverloadErrorsSyntax.ts:4:1] + 4 │ + 5 │ function fn4b(n: string, x?: number, y: string); + · ───────── + 6 │ function fn4b() { } + ╰──── + × Illegal 'use strict' directive in function with non-simple parameter list ╭─[conformance/functions/functionWithUseStrictAndSimpleParameterList_es2016.ts:3:1] 3 │ function a(a = 10) { @@ -17062,6 +17084,14 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" 3 │ } ╰──── + × A required parameter cannot follow an optional parameter. + ╭─[conformance/parser/ecmascript5/ParameterLists/parserParameterList3.ts:1:1] + 1 │ class C { + 2 │ F(A?, B) { } + · ─ + 3 │ } + ╰──── + × Expected `)` but found `Identifier` ╭─[conformance/parser/ecmascript5/ParameterLists/parserParameterList5.ts:1:1] 1 │ function A(): (public B) => C {