Skip to content

Commit 19ca3b6

Browse files
crisbetokirjs
authored andcommitted
refactor(compiler): add spread elements to expression AST
Updates the expression AST to support spread elements that will be used for arrays and function calls.
1 parent e9c3932 commit 19ca3b6

6 files changed

Lines changed: 70 additions & 23 deletions

File tree

packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
SafeCall,
3131
SafeKeyedRead,
3232
SafePropertyRead,
33+
SpreadElement,
3334
TaggedTemplateLiteral,
3435
TemplateLiteral,
3536
TemplateLiteralElement,
@@ -484,6 +485,13 @@ class AstTranslator implements AstVisitor {
484485
return ts.factory.createParenthesizedExpression(this.translate(ast.expression));
485486
}
486487

488+
visitSpreadElement(ast: SpreadElement) {
489+
const expression = wrapForDiagnostics(this.translate(ast.expression));
490+
const node = ts.factory.createSpreadElement(expression);
491+
addParseSpanInfo(node, ast.sourceSpan);
492+
return node;
493+
}
494+
487495
private convertToSafeCall(
488496
ast: Call | SafeCall,
489497
expr: ts.Expression,
@@ -546,40 +554,40 @@ class VeSafeLhsInferenceBugDetector implements AstVisitor {
546554
visitBinary(ast: Binary): boolean {
547555
return ast.left.visit(this) || ast.right.visit(this);
548556
}
549-
visitChain(ast: Chain): boolean {
557+
visitChain(): boolean {
550558
return false;
551559
}
552560
visitConditional(ast: Conditional): boolean {
553561
return ast.condition.visit(this) || ast.trueExp.visit(this) || ast.falseExp.visit(this);
554562
}
555-
visitCall(ast: Call): boolean {
563+
visitCall(): boolean {
556564
return true;
557565
}
558-
visitSafeCall(ast: SafeCall): boolean {
566+
visitSafeCall(): boolean {
559567
return false;
560568
}
561-
visitImplicitReceiver(ast: ImplicitReceiver): boolean {
569+
visitImplicitReceiver(): boolean {
562570
return false;
563571
}
564-
visitThisReceiver(ast: ThisReceiver): boolean {
572+
visitThisReceiver(): boolean {
565573
return false;
566574
}
567575
visitInterpolation(ast: Interpolation): boolean {
568576
return ast.expressions.some((exp) => exp.visit(this));
569577
}
570-
visitKeyedRead(ast: KeyedRead): boolean {
578+
visitKeyedRead(): boolean {
571579
return false;
572580
}
573-
visitLiteralArray(ast: LiteralArray): boolean {
581+
visitLiteralArray(): boolean {
574582
return true;
575583
}
576-
visitLiteralMap(ast: LiteralMap): boolean {
584+
visitLiteralMap(): boolean {
577585
return true;
578586
}
579-
visitLiteralPrimitive(ast: LiteralPrimitive): boolean {
587+
visitLiteralPrimitive(): boolean {
580588
return false;
581589
}
582-
visitPipe(ast: BindingPipe): boolean {
590+
visitPipe(): boolean {
583591
return true;
584592
}
585593
visitPrefixNot(ast: PrefixNot): boolean {
@@ -594,28 +602,31 @@ class VeSafeLhsInferenceBugDetector implements AstVisitor {
594602
visitNonNullAssert(ast: NonNullAssert): boolean {
595603
return ast.expression.visit(this);
596604
}
597-
visitPropertyRead(ast: PropertyRead): boolean {
605+
visitPropertyRead(): boolean {
598606
return false;
599607
}
600-
visitSafePropertyRead(ast: SafePropertyRead): boolean {
608+
visitSafePropertyRead(): boolean {
601609
return false;
602610
}
603-
visitSafeKeyedRead(ast: SafeKeyedRead): boolean {
611+
visitSafeKeyedRead(): boolean {
604612
return false;
605613
}
606-
visitTemplateLiteral(ast: TemplateLiteral, context: any) {
614+
visitTemplateLiteral() {
607615
return false;
608616
}
609-
visitTemplateLiteralElement(ast: TemplateLiteralElement, context: any) {
617+
visitTemplateLiteralElement() {
610618
return false;
611619
}
612-
visitTaggedTemplateLiteral(ast: TaggedTemplateLiteral, context: any) {
620+
visitTaggedTemplateLiteral() {
613621
return false;
614622
}
615-
visitParenthesizedExpression(ast: ParenthesizedExpression, context: any) {
623+
visitParenthesizedExpression(ast: ParenthesizedExpression) {
616624
return ast.expression.visit(this);
617625
}
618-
visitRegularExpressionLiteral(ast: RegularExpressionLiteral, context: any) {
626+
visitRegularExpressionLiteral() {
619627
return false;
620628
}
629+
visitSpreadElement(ast: SpreadElement) {
630+
return ast.expression.visit(this);
631+
}
621632
}

packages/compiler/src/expression_parser/ast.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,19 @@ export class LiteralArray extends AST {
210210
}
211211
}
212212

213+
export class SpreadElement extends AST {
214+
constructor(
215+
span: ParseSpan,
216+
sourceSpan: AbsoluteSourceSpan,
217+
readonly expression: AST,
218+
) {
219+
super(span, sourceSpan);
220+
}
221+
override visit(visitor: AstVisitor, context: any = null): any {
222+
return visitor.visitSpreadElement(this, context);
223+
}
224+
}
225+
213226
export interface LiteralMapPropertyKey {
214227
kind: 'property';
215228
key: string;
@@ -636,6 +649,7 @@ export interface AstVisitor {
636649
visitTaggedTemplateLiteral(ast: TaggedTemplateLiteral, context: any): any;
637650
visitParenthesizedExpression(ast: ParenthesizedExpression, context: any): any;
638651
visitRegularExpressionLiteral(ast: RegularExpressionLiteral, context: any): any;
652+
visitSpreadElement(ast: SpreadElement, context: any): any;
639653
visitASTWithSource?(ast: ASTWithSource, context: any): any;
640654
/**
641655
* This function is optionally defined to allow classes that implement this
@@ -739,6 +753,9 @@ export class RecursiveAstVisitor implements AstVisitor {
739753
this.visit(ast.expression, context);
740754
}
741755
visitRegularExpressionLiteral(ast: RegularExpressionLiteral, context: any) {}
756+
visitSpreadElement(ast: SpreadElement, context: any) {
757+
this.visit(ast.expression, context);
758+
}
742759
// This is not part of the AstVisitor interface, just a helper method
743760
visitAll(asts: AST[], context: any): any {
744761
for (const ast of asts) {

packages/compiler/src/expression_parser/serializer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ class SerializeExpressionVisitor implements expr.AstVisitor {
165165
return ast.tag.visit(this, context) + ast.template.visit(this, context);
166166
}
167167

168+
visitSpreadElement(ast: expr.SpreadElement, context: any) {
169+
return `...${ast.expression.visit(this, context)}`;
170+
}
171+
168172
visitParenthesizedExpression(ast: expr.ParenthesizedExpression, context: any) {
169173
return '(' + ast.expression.visit(this, context) + ')';
170174
}

packages/compiler/test/expression_parser/utils/unparser.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
SafeCall,
3131
SafeKeyedRead,
3232
SafePropertyRead,
33+
SpreadElement,
3334
TaggedTemplateLiteral,
3435
TemplateLiteral,
3536
TemplateLiteralElement,
@@ -248,6 +249,11 @@ class Unparser implements AstVisitor {
248249
this._expression += `/${ast.body}/${ast.flags || ''}`;
249250
}
250251

252+
visitSpreadElement(ast: SpreadElement, context: any) {
253+
this._expression += '...';
254+
this._visit(ast.expression);
255+
}
256+
251257
private _visit(ast: AST) {
252258
ast.visit(this);
253259
}

packages/compiler/test/expression_parser/utils/validator.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
SafeCall,
2929
SafeKeyedRead,
3030
SafePropertyRead,
31+
SpreadElement,
3132
TaggedTemplateLiteral,
3233
TemplateLiteral,
3334
TemplateLiteralElement,
@@ -160,6 +161,10 @@ class ASTValidator extends RecursiveAstVisitor {
160161
override visitRegularExpressionLiteral(ast: RegularExpressionLiteral, context: any): void {
161162
this.validate(ast, () => super.visitRegularExpressionLiteral(ast, context));
162163
}
164+
165+
override visitSpreadElement(ast: SpreadElement, context: any): void {
166+
this.validate(ast, () => super.visitSpreadElement(ast, context));
167+
}
163168
}
164169

165170
function inSpan(span: ParseSpan, parentSpan: ParseSpan | undefined): parentSpan is ParseSpan {

packages/compiler/test/render3/util/expression.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,26 +111,30 @@ class ExpressionSourceHumanizer extends e.RecursiveAstVisitor implements t.Visit
111111
this.recordAst(ast);
112112
super.visitSafeCall(ast, null);
113113
}
114-
override visitTemplateLiteral(ast: e.TemplateLiteral, context: any): void {
114+
override visitTemplateLiteral(ast: e.TemplateLiteral): void {
115115
this.recordAst(ast);
116116
super.visitTemplateLiteral(ast, null);
117117
}
118-
override visitTemplateLiteralElement(ast: e.TemplateLiteralElement, context: any): void {
118+
override visitTemplateLiteralElement(ast: e.TemplateLiteralElement): void {
119119
this.recordAst(ast);
120120
super.visitTemplateLiteralElement(ast, null);
121121
}
122-
override visitTaggedTemplateLiteral(ast: e.TaggedTemplateLiteral, context: any): void {
122+
override visitTaggedTemplateLiteral(ast: e.TaggedTemplateLiteral): void {
123123
this.recordAst(ast);
124124
super.visitTaggedTemplateLiteral(ast, null);
125125
}
126-
override visitParenthesizedExpression(ast: e.ParenthesizedExpression, context: any): void {
126+
override visitParenthesizedExpression(ast: e.ParenthesizedExpression): void {
127127
this.recordAst(ast);
128128
super.visitParenthesizedExpression(ast, null);
129129
}
130-
override visitRegularExpressionLiteral(ast: e.RegularExpressionLiteral, context: any): void {
130+
override visitRegularExpressionLiteral(ast: e.RegularExpressionLiteral): void {
131131
this.recordAst(ast);
132132
super.visitRegularExpressionLiteral(ast, null);
133133
}
134+
override visitSpreadElement(ast: e.SpreadElement): void {
135+
this.recordAst(ast);
136+
super.visitSpreadElement(ast, null);
137+
}
134138

135139
visitTemplate(ast: t.Template) {
136140
t.visitAll(this, ast.directives);

0 commit comments

Comments
 (0)