Skip to content

Commit e401a30

Browse files
committed
Refactor parameter decorator parsing
1 parent 2a7f4fb commit e401a30

File tree

4 files changed

+44
-44
lines changed

4 files changed

+44
-44
lines changed

src/ast.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ export class FunctionTypeNode extends TypeNode {
921921
public returnType: TypeNode,
922922
/** Explicitly provided this type, if any. */
923923
public explicitThisType: NamedTypeNode | null, // can't be a function
924-
/** Decorators on an explicit `this` parameter, if any, preserved for transforms. */
924+
/** Decorators on an explicit `this` parameter, if any. Preserved as transform-only syntax. */
925925
public explicitThisDecorators: DecoratorNode[] | null,
926926
/** Whether nullable or not. */
927927
isNullable: bool,
@@ -969,7 +969,7 @@ export class ParameterNode extends Node {
969969
public type: TypeNode,
970970
/** Initializer expression, if any. */
971971
public initializer: Expression | null,
972-
/** Decorators, if any, preserved so transforms can rewrite them before validation. */
972+
/** Decorators, if any. Preserved as transform-only syntax so transforms can rewrite or remove them before validation. */
973973
public decorators: DecoratorNode[] | null,
974974
/** Source range. */
975975
range: Range
@@ -1669,7 +1669,7 @@ export class Source extends Node {
16691669
debugInfoIndex: i32 = -1;
16701670
/** Re-exported sources. */
16711671
exportPaths: string[] | null = null;
1672-
/** Source-level statements that preserved parameter decorators while parsing. */
1672+
/** Source-level statements known to contain preserved parameter decorators, revisited after transforms for validation. */
16731673
parameterDecoratorStatements: Statement[] | null = null;
16741674

16751675
/** Checks if this source represents native code. */

src/compiler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ export class Compiler extends DiagnosticEmitter {
536536

537537
// initialize lookup maps, built-ins, imports, exports, etc.
538538
this.program.initialize();
539-
// Reject any parameter decorators that transforms left on the AST.
539+
// Reject transform-only parameter decorators that remain on the AST after transforms.
540540
this.program.validateParameterDecorators();
541541

542542

src/parser.ts

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export class Parser extends DiagnosticEmitter {
120120
currentModuleName: string | null = null;
121121
/** Nesting depth of source-level statements while parsing. */
122122
currentSourceStatementDepth: i32 = 0;
123-
/** Indicates whether the current source-level statement preserved any parameter decorators. */
123+
/** Indicates whether the current source-level statement should be revisited for post-transform parameter-decorator validation. */
124124
currentSourceStatementHasParameterDecorators: bool = false;
125125

126126
/** Constructs a new parser. */
@@ -210,15 +210,9 @@ export class Parser extends DiagnosticEmitter {
210210

211211
// check decorators
212212
let decorators: DecoratorNode[] | null = null;
213-
while (tn.skip(Token.At)) {
214-
if (startPos < 0) startPos = tn.tokenPos;
215-
let decorator = this.parseDecorator(tn);
216-
if (!decorator) {
217-
this.skipStatement(tn);
218-
continue;
219-
}
220-
if (!decorators) decorators = [decorator];
221-
else decorators.push(decorator);
213+
if (tn.peek() == Token.At) {
214+
startPos = tn.nextTokenPos;
215+
decorators = this.parseDecorators(tn, true);
222216
}
223217

224218
// check modifiers
@@ -717,7 +711,7 @@ export class Parser extends DiagnosticEmitter {
717711
// Indicates whether tryParseSignature determined that it is handling a Signature
718712
private tryParseSignatureIsSignature: bool = false;
719713

720-
/** Parses a function type, preserving parameter decorators for transforms. */
714+
/** Parses a function type, preserving leading parameter decorators for transforms while still reporting misplaced ones. */
721715
tryParseFunctionType(
722716
tn: Tokenizer
723717
): FunctionTypeNode | null {
@@ -744,7 +738,8 @@ export class Parser extends DiagnosticEmitter {
744738
do {
745739
let paramStart = -1;
746740
let kind = ParameterKind.Default;
747-
let decorators = this.parseParameterDecorators(tn);
741+
// Preserve leading parameter decorators in the AST so transforms can inspect or remove them later.
742+
let decorators = this.parseDecorators(tn);
748743
if (decorators) {
749744
paramStart = decorators[0].range.start;
750745
isSignature = true;
@@ -915,6 +910,27 @@ export class Parser extends DiagnosticEmitter {
915910

916911
// statements
917912

913+
/** Parses zero or more decorators starting at the current token. */
914+
private parseDecorators(
915+
tn: Tokenizer,
916+
skipStatementOnError: bool = false
917+
): DecoratorNode[] | null {
918+
let decorators: DecoratorNode[] | null = null;
919+
while (tn.skip(Token.At)) {
920+
let decorator = this.parseDecorator(tn);
921+
if (!decorator) {
922+
if (skipStatementOnError) {
923+
this.skipStatement(tn);
924+
continue;
925+
}
926+
break;
927+
}
928+
if (!decorators) decorators = [decorator];
929+
else decorators.push(decorator);
930+
}
931+
return decorators;
932+
}
933+
918934
parseDecorator(
919935
tn: Tokenizer
920936
): DecoratorNode | null {
@@ -959,23 +975,9 @@ export class Parser extends DiagnosticEmitter {
959975
return null;
960976
}
961977

962-
private parseParameterDecorators(
963-
tn: Tokenizer
964-
): DecoratorNode[] | null {
965-
// Preserve parameter decorators in the AST so transforms can inspect or remove them later.
966-
let decorators: DecoratorNode[] | null = null;
967-
while (tn.skip(Token.At)) {
968-
let decorator = this.parseDecorator(tn);
969-
if (!decorator) break;
970-
if (!decorators) decorators = [decorator];
971-
else decorators.push(decorator);
972-
}
973-
return decorators;
974-
}
975-
976-
/** Tries to parse decorators that appear after a parameter has already started and reports them. */
978+
/** Consumes misplaced parameter decorators after a parameter has already started so they diagnose as TS1206 instead of cascading. */
977979
private tryParseParameterDecorators(tn: Tokenizer): void {
978-
let decorators = this.parseParameterDecorators(tn);
980+
let decorators = this.parseDecorators(tn);
979981
if (decorators) {
980982
this.error(
981983
DiagnosticCode.Decorators_are_not_valid_here,
@@ -984,7 +986,7 @@ export class Parser extends DiagnosticEmitter {
984986
}
985987
}
986988

987-
/** Remembers when a source-level statement preserved parameter decorators in one of its function signatures. */
989+
/** Records source-level statements whose function signatures preserved parameter decorators for post-transform validation. */
988990
private noteFunctionTypeParameterDecorators(signature: FunctionTypeNode): void {
989991
if (signature.explicitThisDecorators) {
990992
this.currentSourceStatementHasParameterDecorators = true;
@@ -1303,7 +1305,7 @@ export class Parser extends DiagnosticEmitter {
13031305

13041306
/** Explicit `this` parameter captured by the current parseParameters call, if any. */
13051307
private parseParametersThis: NamedTypeNode | null = null;
1306-
/** Decorators on the explicit `this` parameter, preserved for transforms. */
1308+
/** Decorators on the explicit `this` parameter captured by the current parseParameters call. Preserved as transform-only syntax. */
13071309
private parseParametersThisDecorators: DecoratorNode[] | null = null;
13081310

13091311
parseParameters(
@@ -1327,7 +1329,8 @@ export class Parser extends DiagnosticEmitter {
13271329
while (true) {
13281330
if (tn.skip(Token.CloseParen)) break;
13291331

1330-
let paramDecorators = this.parseParameterDecorators(tn);
1332+
// Preserve leading parameter decorators in the AST so transforms can inspect or remove them later.
1333+
let paramDecorators = this.parseDecorators(tn);
13311334

13321335
if (first && tn.skip(Token.This)) {
13331336
if (tn.skip(Token.Colon)) {
@@ -1993,14 +1996,9 @@ export class Parser extends DiagnosticEmitter {
19931996
let isInterface = parent.kind == NodeKind.InterfaceDeclaration;
19941997
let startPos = 0;
19951998
let decorators: DecoratorNode[] | null = null;
1996-
if (tn.skip(Token.At)) {
1997-
startPos = tn.tokenPos;
1998-
do {
1999-
let decorator = this.parseDecorator(tn);
2000-
if (!decorator) break;
2001-
if (!decorators) decorators = new Array();
2002-
decorators.push(decorator);
2003-
} while (tn.skip(Token.At));
1999+
if (tn.peek() == Token.At) {
2000+
startPos = tn.nextTokenPos;
2001+
decorators = this.parseDecorators(tn);
20042002
if (isInterface && decorators) {
20052003
this.error(
20062004
DiagnosticCode.Decorators_are_not_valid_here,

src/program.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ export enum OperatorKind {
248248
// LogicalOr // a || b
249249
}
250250

251+
/** Reports transform-only parameter decorators that were preserved by the parser but not removed by transforms. */
251252
class ParameterDecoratorValidator extends NodeWalker {
252253
constructor(private diagnostics: DiagnosticEmitter) {
253254
super();
@@ -1532,7 +1533,7 @@ export class Program extends DiagnosticEmitter {
15321533
}
15331534
}
15341535

1535-
/** Rejects parameter decorators that survive transform time. These remain transform-only syntax. */
1536+
/** Rejects preserved parameter decorators that survive transform time by revisiting only the source-level statements that contained them. */
15361537
validateParameterDecorators(): void {
15371538
if (this.parameterDecoratorsValidated) return;
15381539
this.parameterDecoratorsValidated = true;
@@ -1547,6 +1548,7 @@ export class Program extends DiagnosticEmitter {
15471548
let statement = parameterDecoratorStatements[j];
15481549
for (let m = 0, n = statements.length; m < n; ++m) {
15491550
if (statements[m] == statement) {
1551+
// Transforms may delete or replace a remembered statement entirely; only validate nodes still reachable from the source.
15501552
validator.visitNode(statement);
15511553
break;
15521554
}

0 commit comments

Comments
 (0)