fix(parser): reject readonly modifier on constructors (#26612)

The parser accepted `class C { readonly constructor() {} }` and silently discarded `readonly`. Report TS1024 on the modifier instead, matching TypeScript and the existing validation for ordinary methods.
This commit is contained in:
camc314
2026-09-14 05:08:44 +00:00
parent aaff7583a6
commit 1916f31135
12 changed files with 72 additions and 13 deletions
+6
View File
@@ -525,6 +525,12 @@ impl<'a, C: Config> ParserImpl<'a, C> {
if let Some(modifier) = modifiers.get(ModifierKind::Declare) {
self.error(diagnostics::declare_constructor(modifier.span()));
}
self.verify_modifiers(
modifiers,
ModifierKinds::all_except([ModifierKind::Readonly]),
false,
diagnostics::modifier_only_on_property_declaration_or_index_signature,
);
let value = self.parse_method(
modifiers.contains(ModifierKind::Async),
@@ -0,0 +1 @@
declare class C { readonly constructor(); }
@@ -0,0 +1,4 @@
class C {
readonly constructor(value: number);
constructor(value: number) {}
}
@@ -0,0 +1 @@
class C { public readonly "constructor"() {} }
@@ -0,0 +1 @@
class C { readonly constructor() {} }
@@ -0,0 +1,15 @@
class C {
readonly field = 1;
constructor(value: number);
constructor(public readonly value: number) {}
}
class D {
protected constructor() {}
readonly = 1;
}
declare class E {
readonly field: number;
constructor();
}
+2 -2
View File
@@ -1,3 +1,3 @@
codegen_misc Summary:
AST Parsed : 108/108 (100.00%)
Positive Passed: 108/108 (100.00%)
AST Parsed : 109/109 (100.00%)
Positive Passed: 109/109 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
formatter_misc Summary:
AST Parsed : 108/108 (100.00%)
Positive Passed: 108/108 (100.00%)
AST Parsed : 109/109 (100.00%)
Positive Passed: 109/109 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
lexer_misc Summary:
AST Parsed : 326/326 (100.00%)
Positive Passed: 326/326 (100.00%)
AST Parsed : 331/331 (100.00%)
Positive Passed: 331/331 (100.00%)
+29 -3
View File
@@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 108/108 (100.00%)
Positive Passed: 108/108 (100.00%)
Negative Passed: 218/218 (100.00%)
AST Parsed : 109/109 (100.00%)
Positive Passed: 109/109 (100.00%)
Negative Passed: 222/222 (100.00%)
× Cannot assign to 'arguments' in strict mode
╭─[misc/fail/arguments-eval-ambient.ts:2:13]
@@ -4765,6 +4765,32 @@ Negative Passed: 218/218 (100.00%)
· ───────────
╰────
× TS(1024): 'readonly' modifier can only appear on a property declaration or index signature.
╭─[misc/fail/readonly-constructor-ambient.ts:1:19]
1 │ declare class C { readonly constructor(); }
· ────────
╰────
× TS(1024): 'readonly' modifier can only appear on a property declaration or index signature.
╭─[misc/fail/readonly-constructor-overload.ts:2:3]
1 │ class C {
2 │ readonly constructor(value: number);
· ────────
3 │ constructor(value: number) {}
╰────
× TS(1024): 'readonly' modifier can only appear on a property declaration or index signature.
╭─[misc/fail/readonly-constructor-quoted.ts:1:18]
1 │ class C { public readonly "constructor"() {} }
· ────────
╰────
× TS(1024): 'readonly' modifier can only appear on a property declaration or index signature.
╭─[misc/fail/readonly-constructor.ts:1:11]
1 │ class C { readonly constructor() {} }
· ────────
╰────
× Invalid rest element target in destructuring assignment
╭─[misc/fail/rest-assignment-target.js:1:6]
1 │ ({...[]} = {});
+7 -2
View File
@@ -1,6 +1,6 @@
semantic_misc Summary:
AST Parsed : 108/108 (100.00%)
Positive Passed: 104/108 (96.30%)
AST Parsed : 109/109 (100.00%)
Positive Passed: 104/109 (95.41%)
semantic Error: tasks/coverage/misc/pass/declare-let-private.ts
Bindings mismatch:
after transform: ScopeId(0): ["private"]
@@ -85,6 +85,11 @@ Unresolved reference IDs mismatch for "Object":
after transform: [ReferenceId(166), ReferenceId(170), ReferenceId(174), ReferenceId(185), ReferenceId(226), ReferenceId(228), ReferenceId(265), ReferenceId(267), ReferenceId(304), ReferenceId(306), ReferenceId(343), ReferenceId(345), ReferenceId(382), ReferenceId(386), ReferenceId(425), ReferenceId(427), ReferenceId(464), ReferenceId(468), ReferenceId(507), ReferenceId(509), ReferenceId(546), ReferenceId(550), ReferenceId(589), ReferenceId(591), ReferenceId(628), ReferenceId(632), ReferenceId(671), ReferenceId(673), ReferenceId(839), ReferenceId(843), ReferenceId(876), ReferenceId(880), ReferenceId(891), ReferenceId(895)]
rebuilt : [ReferenceId(169), ReferenceId(173), ReferenceId(177), ReferenceId(182), ReferenceId(291), ReferenceId(295), ReferenceId(364), ReferenceId(368), ReferenceId(437), ReferenceId(441), ReferenceId(625), ReferenceId(632), ReferenceId(777), ReferenceId(781), ReferenceId(810), ReferenceId(814), ReferenceId(819), ReferenceId(823)]
semantic Error: tasks/coverage/misc/pass/readonly-constructor.ts
Bindings mismatch:
after transform: ScopeId(0): ["C", "D", "E", "_defineProperty"]
rebuilt : ScopeId(0): ["C", "D", "_defineProperty"]
semantic Error: tasks/coverage/misc/pass/reserved-kw-ambient-context.ts
Bindings mismatch:
after transform: ScopeId(0): ["C", "C2", "f", "f2"]
@@ -1,3 +1,3 @@
transformer_misc Summary:
AST Parsed : 108/108 (100.00%)
Positive Passed: 108/108 (100.00%)
AST Parsed : 109/109 (100.00%)
Positive Passed: 109/109 (100.00%)