From a1e7e5f13e768f887ae16e6a567a25261f381aeb Mon Sep 17 00:00:00 2001 From: Boshen Date: Fri, 30 Oct 2026 11:35:35 +0800 Subject: [PATCH] fix(parser): reject optional class members in JavaScript (#28439) Reject TypeScript optional class members in JavaScript with a diagnostic on the question mark. The existing TypeScript compiler fixture covers the rejection. Validated with scoped Rust checks, full conformance, parser bindings, allocation tracking. AI-assisted (Codex). --- crates/oxc_parser/src/diagnostics.rs | 5 +++++ crates/oxc_parser/src/js/class.rs | 3 +++ .../coverage/snapshots/parser_typescript.snap | 20 ++++++++++++++++--- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/crates/oxc_parser/src/diagnostics.rs b/crates/oxc_parser/src/diagnostics.rs index 42db8552f4..3ab39ad270 100644 --- a/crates/oxc_parser/src/diagnostics.rs +++ b/crates/oxc_parser/src/diagnostics.rs @@ -1186,6 +1186,11 @@ parser_diagnostics! { .with_label(span) }; + optional_class_member_in_ts(span: Span) => { + OxcDiagnostic::error("Optional class members can only be used in TypeScript files.") + .with_label(span) + }; + parameter_modifiers_in_ts(modifier: Modifier, allowed: Option) => { ts_error("8012", "Parameter modifiers can only be used in TypeScript files.") .with_label(modifier.span()) diff --git a/crates/oxc_parser/src/js/class.rs b/crates/oxc_parser/src/js/class.rs index 7e8763582b..679318af22 100644 --- a/crates/oxc_parser/src/js/class.rs +++ b/crates/oxc_parser/src/js/class.rs @@ -629,6 +629,9 @@ impl<'a, C: Config> ParserImpl<'a, C> { let optional_span = (cur_token.kind() == Kind::Question).then(|| { let span = cur_token.span(); self.bump_any(); + if !self.is_ts { + self.error(diagnostics::optional_class_member_in_ts(span)); + } span }); diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index 4bb1e691f2..395ce08d3a 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -3,7 +3,7 @@ commit: 8a62bbe7 parser_typescript Summary: AST Parsed : 9859/9868 (99.91%) Positive Passed: 9849/9868 (99.81%) -Negative Passed: 1712/2807 (60.99%) +Negative Passed: 1713/2807 (61.03%) Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/compiler/FunctionDeclaration3.ts Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/compiler/FunctionDeclaration4.ts @@ -586,8 +586,6 @@ Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/compiler Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/compiler/jsFileCompilationDuplicateFunctionImplementationFileOrderReversed.ts -Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/compiler/jsFileCompilationOptionalClassElementSyntaxOfClass.ts - Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/compiler/jsFunctionWithPrototypeNoErrorTruncationNoCrash.ts Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/compiler/jsTypedefMergedWithModuleExportProperty.ts @@ -9102,6 +9100,22 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/ ╰──── help: Try inserting a semicolon here + × Optional class members can only be used in TypeScript files. + ╭─[typescript/tsc/testdata/tests/cases/compiler/jsFileCompilationOptionalClassElementSyntaxOfClass.ts:2:8] + 1 │ class C { + 2 │ foo?() { + · ─ + 3 │ } + ╰──── + + × Optional class members can only be used in TypeScript files. + ╭─[typescript/tsc/testdata/tests/cases/compiler/jsFileCompilationOptionalClassElementSyntaxOfClass.ts:4:8] + 3 │ } + 4 │ bar? = 1; + · ─ + 5 │ } + ╰──── + × Expected `,` or `)` but found `?` ╭─[typescript/tsc/testdata/tests/cases/compiler/jsFileCompilationOptionalParameter.ts:1:13] 1 │ function F(p?) { }