diff --git a/crates/oxc_semantic/src/checker/mod.rs b/crates/oxc_semantic/src/checker/mod.rs index 5a5424c807a65..785a807fb2495 100644 --- a/crates/oxc_semantic/src/checker/mod.rs +++ b/crates/oxc_semantic/src/checker/mod.rs @@ -87,6 +87,9 @@ pub fn check<'a>(kind: AstKind<'a>, ctx: &SemanticBuilder<'a>) { AstKind::MethodDefinition(method) => { ts::check_method_definition(method, ctx); } + AstKind::PropertyDefinition(prop) => { + ts::check_property_definition(prop, ctx); + } AstKind::ObjectProperty(prop) => { ts::check_object_property(prop, ctx); } diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index 78403e5562839..6c270a32995ef 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -271,6 +271,12 @@ pub fn check_method_definition<'a>(method: &MethodDefinition<'a>, ctx: &Semantic if method.kind.is_constructor() { ctx.error(diagnostics::illegal_abstract_modifier(method.key.span())); } + // abstract cannot be used with private identifiers + if method.key.is_private_identifier() { + ctx.error(diagnostics::abstract_cannot_be_used_with_private_identifier( + method.key.span(), + )); + } } let is_empty_body = method.value.r#type == FunctionType::TSEmptyBodyFunctionExpression; @@ -289,6 +295,13 @@ pub fn check_method_definition<'a>(method: &MethodDefinition<'a>, ctx: &Semantic } } +pub fn check_property_definition(prop: &PropertyDefinition, ctx: &SemanticBuilder<'_>) { + // abstract cannot be used with private identifiers + if prop.r#type.is_abstract() && prop.key.is_private_identifier() { + ctx.error(diagnostics::abstract_cannot_be_used_with_private_identifier(prop.key.span())); + } +} + pub fn check_object_property(prop: &ObjectProperty, ctx: &SemanticBuilder<'_>) { if let Expression::FunctionExpression(func) = &prop.value && prop.kind.is_accessor() diff --git a/crates/oxc_semantic/src/diagnostics.rs b/crates/oxc_semantic/src/diagnostics.rs index 934ea87a23232..0c41e6e552d12 100644 --- a/crates/oxc_semantic/src/diagnostics.rs +++ b/crates/oxc_semantic/src/diagnostics.rs @@ -375,6 +375,13 @@ pub fn illegal_abstract_modifier(span: Span) -> OxcDiagnostic { .with_label(span) } +/// 'abstract' modifier cannot be used with a private identifier. (18019) +#[cold] +pub fn abstract_cannot_be_used_with_private_identifier(span: Span) -> OxcDiagnostic { + ts_error("18019", "'abstract' modifier cannot be used with a private identifier.") + .with_label(span) +} + /// A parameter property is only allowed in a constructor implementation.ts(2369) #[cold] pub fn parameter_property_only_in_constructor_impl(span: Span) -> OxcDiagnostic { diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index 295f51777e82c..052092104946a 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -2,12 +2,10 @@ commit: fc58af40 parser_babel Summary: AST Parsed : 2221/2227 (99.73%) -Positive Passed: 2206/2227 (99.06%) -Negative Passed: 1647/1689 (97.51%) +Positive Passed: 2205/2227 (99.01%) +Negative Passed: 1649/1689 (97.63%) Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2026/explicit-resource-management/invalid-for-using-of-no-initializer/input.js -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/estree/class-private-property/typescript-invalid-abstract/input.ts - Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-type-assertion-and-assign/input.ts @@ -30,8 +28,6 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/ty Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/class/modifiers-invalid-order/input.ts -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-abstract/input.ts - Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/invalid-namespace-var/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/module-class/input.ts @@ -218,6 +214,16 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2026 · ╰── Opened here ╰──── +Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/estree/class-private-method/typescript-invalid-abstract/input.ts + + × TS(18019): 'abstract' modifier cannot be used with a private identifier. + ╭─[babel/packages/babel-parser/test/fixtures/estree/class-private-method/typescript-invalid-abstract/input.ts:2:12] + 1 │ abstract class TSAbstractClass { + 2 │ abstract #foo(name: string): boolean; + · ──── + 3 │ } + ╰──── + Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/class/constructor-with-modifier-names/input.ts × Multiple constructor implementations are not allowed. @@ -12170,6 +12176,14 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc ╰──── help: Add an initializer (e.g. ` = undefined`) here + × TS(18019): 'abstract' modifier cannot be used with a private identifier. + ╭─[babel/packages/babel-parser/test/fixtures/estree/class-private-property/typescript-invalid-abstract/input.ts:2:12] + 1 │ abstract class TSAbstractClass { + 2 │ abstract #foo: boolean; + · ──── + 3 │ } + ╰──── + × A 'set' accessor must have exactly one parameter. ╭─[babel/packages/babel-parser/test/fixtures/estree/typescript/getter-setter/input.ts:3:9] 2 │ ({ get x() { return 1 } }); @@ -13053,6 +13067,14 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc 8 │ // Also works on AssignmentPattern ╰──── + × TS(18019): 'abstract' modifier cannot be used with a private identifier. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-abstract/input.ts:2:12] + 1 │ abstract class A { + 2 │ abstract #a; + · ── + 3 │ } + ╰──── + × TS(18010): An accessibility modifier cannot be used with a private identifier. ╭─[babel/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-private/input.ts:2:3] 1 │ class A { diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index 83ac06e5bc11a..21d4a8a67a538 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -14772,6 +14772,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/statements/Va 27 │ async get #asyncProp() { return 1; } // Error ╰──── + × TS(18019): 'abstract' modifier cannot be used with a private identifier. + ╭─[typescript/tests/cases/conformance/classes/members/privateNames/privateNamesIncompatibleModifiers.ts:32:14] + 31 │ abstract class B { + 32 │ abstract #quux = 3; // Error + · ───── + 33 │ } + ╰──── + × Private identifier '#prop' is not allowed outside class bodies ╭─[typescript/tests/cases/conformance/classes/members/privateNames/privateNamesInterfaceExtendingClass.ts:10:7] 9 │ function func(x: I) { diff --git a/tasks/coverage/snapshots/semantic_babel.snap b/tasks/coverage/snapshots/semantic_babel.snap index f162f9b379efd..61673a700651d 100644 --- a/tasks/coverage/snapshots/semantic_babel.snap +++ b/tasks/coverage/snapshots/semantic_babel.snap @@ -195,9 +195,7 @@ after transform: ScopeId(1): [ScopeId(2)] rebuilt : ScopeId(1): [] semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/estree/class-private-method/typescript-invalid-abstract/input.ts -Scope children mismatch: -after transform: ScopeId(1): [ScopeId(2)] -rebuilt : ScopeId(1): [] +'abstract' modifier cannot be used with a private identifier. semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/estree/class-private-property/typescript/input.js Unresolved references mismatch: