Sitelet https://github.com/angular/angular/commit/fe671d9024d737b0304cc66366450e6fa0560cd9
Skip to content

Commit fe671d9

Browse files
crisbetothePunderWoman
authored andcommitted
fix(compiler-cli): output function return types in linker
Fixes that the linker was ignoring the return types of function nodes, instead of outputting them. (cherry picked from commit d7dd34f)
1 parent 024ebe6 commit fe671d9

8 files changed

Lines changed: 148 additions & 18 deletions

File tree

‎packages/compiler-cli/linker/babel/src/ast/babel_ast_factory.ts‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,40 +145,46 @@ export class BabelAstFactory implements AstFactory<
145145
functionName: string,
146146
parameters: Parameter<t.TSType>[],
147147
body: t.Statement,
148+
returnType: t.TSType | null,
148149
): t.Statement {
149150
assert(body, t.isBlockStatement, 'a block');
150-
return t.functionDeclaration(
151+
const fn = t.functionDeclaration(
151152
t.identifier(functionName),
152153
parameters.map((param) => this.identifierWithType(param.name, param.type)),
153154
body,
154155
);
156+
return this.attachReturnType(fn, returnType);
155157
}
156158

157159
createArrowFunctionExpression(
158160
parameters: Parameter<t.TSType>[],
159161
body: t.Statement | t.Expression,
162+
returnType: t.TSType | null,
160163
): t.Expression {
161164
if (t.isStatement(body)) {
162165
assert(body, t.isBlockStatement, 'a block');
163166
}
164-
return t.arrowFunctionExpression(
167+
const fn = t.arrowFunctionExpression(
165168
parameters.map((param) => this.identifierWithType(param.name, param.type)),
166169
body,
167170
);
171+
return this.attachReturnType(fn, returnType);
168172
}
169173

170174
createFunctionExpression(
171175
functionName: string | null,
172176
parameters: Parameter<t.TSType>[],
173177
body: t.Statement,
178+
returnType: t.TSType | null,
174179
): t.Expression {
175180
assert(body, t.isBlockStatement, 'a block');
176181
const name = functionName !== null ? t.identifier(functionName) : null;
177-
return t.functionExpression(
182+
const fn = t.functionExpression(
178183
name,
179184
parameters.map((param) => this.identifierWithType(param.name, param.type)),
180185
body,
181186
);
187+
return this.attachReturnType(fn, returnType);
182188
}
183189

184190
createIdentifier = t.identifier;
@@ -370,6 +376,16 @@ export class BabelAstFactory implements AstFactory<
370376

371377
return node;
372378
}
379+
380+
private attachReturnType<
381+
T extends t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression,
382+
>(node: T, returnType: t.TSType | null): T {
383+
if (this.typesEnabled && returnType !== null) {
384+
node.returnType = t.tsTypeAnnotation(returnType);
385+
}
386+
387+
return node;
388+
}
373389
}
374390

375391
function getEntityTypeFromExpression(

‎packages/compiler-cli/linker/babel/test/ast/babel_ast_factory_spec.ts‎

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,28 @@ describe('BabelAstFactory', () => {
152152
{name: 'arg2', type: factory.createBuiltInType('number')},
153153
],
154154
stmts,
155+
null,
155156
);
156157
expect(generate(fn).code).toEqual(
157158
['function foo(arg1, arg2: number) {', ' x = 10;', ' y = 20;', '}'].join('\n'),
158159
);
159160
});
161+
162+
it('should create a function declaration with a return type', () => {
163+
const stmts = statement.ast`{x = 10; y = 20;}`;
164+
const fn = factory.createFunctionDeclaration(
165+
'foo',
166+
[
167+
{name: 'arg1', type: null},
168+
{name: 'arg2', type: factory.createBuiltInType('number')},
169+
],
170+
stmts,
171+
factory.createBuiltInType('boolean'),
172+
);
173+
expect(generate(fn).code).toEqual(
174+
['function foo(arg1, arg2: number): boolean {', ' x = 10;', ' y = 20;', '}'].join('\n'),
175+
);
176+
});
160177
});
161178

162179
describe('createFunctionExpression()', () => {
@@ -169,13 +186,30 @@ describe('BabelAstFactory', () => {
169186
{name: 'arg2', type: factory.createBuiltInType('number')},
170187
],
171188
stmts,
189+
null,
172190
);
173191
expect(t.isStatement(fn)).toBe(false);
174192
expect(generate(fn).code).toEqual(
175193
['function foo(arg1, arg2: number) {', ' x = 10;', ' y = 20;', '}'].join('\n'),
176194
);
177195
});
178196

197+
it('should create a function expression with a return type', () => {
198+
const stmts = statement.ast`{x = 10; y = 20;}`;
199+
const fn = factory.createFunctionExpression(
200+
'foo',
201+
[
202+
{name: 'arg1', type: null},
203+
{name: 'arg2', type: factory.createBuiltInType('number')},
204+
],
205+
stmts,
206+
factory.createBuiltInType('boolean'),
207+
);
208+
expect(generate(fn).code).toEqual(
209+
['function foo(arg1, arg2: number): boolean {', ' x = 10;', ' y = 20;', '}'].join('\n'),
210+
);
211+
});
212+
179213
it('should create an anonymous function expression node if the name is null', () => {
180214
const stmts = statement.ast`{x = 10; y = 20;}`;
181215
const fn = factory.createFunctionExpression(
@@ -185,6 +219,7 @@ describe('BabelAstFactory', () => {
185219
{name: 'arg2', type: factory.createBuiltInType('number')},
186220
],
187221
stmts,
222+
null,
188223
);
189224
expect(generate(fn).code).toEqual(
190225
['function (arg1, arg2: number) {', ' x = 10;', ' y = 20;', '}'].join('\n'),
@@ -201,13 +236,27 @@ describe('BabelAstFactory', () => {
201236
{name: 'arg2', type: factory.createBuiltInType('number')},
202237
],
203238
expr,
239+
null,
204240
);
205241
expect(generate(fn).code).toEqual('(arg1, arg2: number) => arg2 + arg1');
206242
});
207243

244+
it('should create an arrow function with a return type', () => {
245+
const expr = expression.ast`arg2 + arg1`;
246+
const fn = factory.createArrowFunctionExpression(
247+
[
248+
{name: 'arg1', type: null},
249+
{name: 'arg2', type: factory.createBuiltInType('number')},
250+
],
251+
expr,
252+
factory.createBuiltInType('number'),
253+
);
254+
expect(generate(fn).code).toEqual('(arg1, arg2: number): number => arg2 + arg1');
255+
});
256+
208257
it('should create an arrow function with an implicit return object literal', () => {
209258
const expr = expression.ast`{a: 1, b: 2}`;
210-
const fn = factory.createArrowFunctionExpression([], expr);
259+
const fn = factory.createArrowFunctionExpression([], expr, null);
211260
expect(generate(fn).code).toEqual(['() => ({', ' a: 1,', ' b: 2', '})'].join('\n'));
212261
});
213262

@@ -219,6 +268,7 @@ describe('BabelAstFactory', () => {
219268
{name: 'arg2', type: factory.createBuiltInType('number')},
220269
],
221270
stmts,
271+
null,
222272
);
223273
expect(generate(fn).code).toEqual(
224274
['(arg1, arg2: number) => {', ' x = 10;', ' y = 20;', ' return x + y;', '}'].join('\n'),

‎packages/compiler-cli/linker/src/file_linker/emit_scopes/emit_scope.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class EmitScope<TStatement, TExpression, TType> {
7979
private wrapInIifeWithStatements(expression: TExpression, statements: TStatement[]): TExpression {
8080
const returnStatement = this.factory.createReturnStatement(expression);
8181
const body = this.factory.createBlock([...statements, returnStatement]);
82-
const fn = this.factory.createFunctionExpression(/* name */ null, /* args */ [], body);
82+
const fn = this.factory.createFunctionExpression(/* name */ null, /* args */ [], body, null);
8383
return this.factory.createCallExpression(fn, /* args */ [], /* pure */ false);
8484
}
8585
}

‎packages/compiler-cli/linker/test/ast/ast_value_spec.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ describe('AstValue', () => {
346346
'foo',
347347
[],
348348
factory.createBlock([factory.createReturnStatement(factory.createLiteral(42))]),
349+
null,
349350
);
350351
expect(createAstValue(funcExpr).isFunction()).toEqual(true);
351352
});
@@ -361,6 +362,7 @@ describe('AstValue', () => {
361362
'foo',
362363
[],
363364
factory.createBlock([factory.createReturnStatement(factory.createLiteral(42))]),
365+
null,
364366
);
365367
expect(createAstValue<Function>(funcExpr).getFunctionReturnValue()).toEqual(
366368
createAstValue(factory.createLiteral(42)),
@@ -381,6 +383,7 @@ describe('AstValue', () => {
381383
factory.createBlock([
382384
factory.createExpressionStatement(factory.createLiteral('do nothing')),
383385
]),
386+
null,
384387
);
385388
expect(() => createAstValue<Function>(funcExpr).getFunctionReturnValue()).toThrowError(
386389
'Unsupported syntax, expected a function body with a single return statement.',
@@ -397,6 +400,7 @@ describe('AstValue', () => {
397400
{name: 'b', type: null},
398401
],
399402
factory.createBlock([]),
403+
null,
400404
);
401405
expect(createAstValue<Function>(funcExpr).getFunctionParameters()).toEqual(
402406
['a', 'b'].map((name) => createAstValue(factory.createIdentifier(name))),

‎packages/compiler-cli/src/ngtsc/translator/src/api/ast_factory.ts‎

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,13 @@ export interface AstFactory<TStatement, TExpression, TType> {
120120
* @param functionName the name of the function.
121121
* @param parameters the names of the function's parameters.
122122
* @param body a statement (or a block of statements) that are the body of the function.
123+
* @param returnType return type annotation for the function.
123124
*/
124125
createFunctionDeclaration(
125126
functionName: string,
126127
parameters: Parameter<TType>[],
127128
body: TStatement,
129+
returnType: TType | null,
128130
): TStatement;
129131

130132
/**
@@ -134,11 +136,13 @@ export interface AstFactory<TStatement, TExpression, TType> {
134136
* @param functionName the name of the function.
135137
* @param parameters the names of the function's parameters.
136138
* @param body a statement (or a block of statements) that are the body of the function.
139+
* @param returnType return type annotation for the function.
137140
*/
138141
createFunctionExpression(
139142
functionName: string | null,
140143
parameters: Parameter<TType>[],
141144
body: TStatement,
145+
returnType: TType | null,
142146
): TExpression;
143147

144148
/**
@@ -147,10 +151,12 @@ export interface AstFactory<TStatement, TExpression, TType> {
147151
*
148152
* @param parameters the names of the function's parameters.
149153
* @param body an expression or block of statements that are the body of the function.
154+
* @param returnType return type annotation for the function.
150155
*/
151156
createArrowFunctionExpression(
152157
parameters: Parameter<TType>[],
153158
body: TExpression | TStatement,
159+
returnType: TType | null,
154160
): TExpression;
155161

156162
/**
@@ -375,13 +381,7 @@ export type UnaryOperator = '+' | '-' | '!';
375381

376382
/** Supported built-in types. */
377383
export type BuiltInType =
378-
| 'any'
379-
| 'boolean'
380-
| 'number'
381-
| 'string'
382-
| 'function'
383-
| 'never'
384-
| 'unknown';
384+
'any' | 'boolean' | 'number' | 'string' | 'function' | 'never' | 'unknown';
385385

386386
export interface Parameter<TType> {
387387
name: string;
@@ -479,8 +479,7 @@ export interface ObjectLiteralSpread<TExpression> {
479479

480480
/** Possible properties in an object literal. */
481481
export type ObjectLiteralProperty<TExpression> =
482-
| ObjectLiteralAssignment<TExpression>
483-
| ObjectLiteralSpread<TExpression>;
482+
ObjectLiteralAssignment<TExpression> | ObjectLiteralSpread<TExpression>;
484483

485484
/**
486485
* Information used by the `AstFactory` to create a template literal string (i.e. a back-ticked

‎packages/compiler-cli/src/ngtsc/translator/src/translator.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export class ExpressionTranslatorVisitor<TFile, TStatement, TExpression, TType>
123123
stmt.name,
124124
this.translateParams(stmt.params, context),
125125
this.factory.createBlock(this.visitStatements(stmt.statements, context.withStatementMode)),
126+
stmt.type?.visitType(this, context) ?? null,
126127
),
127128
stmt.leadingComments,
128129
);
@@ -439,6 +440,7 @@ export class ExpressionTranslatorVisitor<TFile, TStatement, TExpression, TType>
439440
ast.name ?? null,
440441
this.translateParams(ast.params, context),
441442
this.factory.createBlock(this.visitStatements(ast.statements, context)),
443+
ast.type?.visitType(this, context) ?? null,
442444
),
443445
ast.leadingComments,
444446
);
@@ -451,6 +453,7 @@ export class ExpressionTranslatorVisitor<TFile, TStatement, TExpression, TType>
451453
Array.isArray(ast.body)
452454
? this.factory.createBlock(this.visitStatements(ast.body, context))
453455
: ast.body.visitExpression(this, context),
456+
ast.type?.visitType(this, context) ?? null,
454457
),
455458
ast.leadingComments,
456459
);

‎packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
204204
functionName: string,
205205
parameters: Parameter<ts.TypeNode>[],
206206
body: ts.Statement,
207+
returnType: ts.TypeNode | null,
207208
): ts.Statement {
208209
if (!ts.isBlock(body)) {
209210
throw new Error(`Invalid syntax, expected a block, but got ${ts.SyntaxKind[body.kind]}.`);
@@ -214,7 +215,7 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
214215
functionName,
215216
undefined,
216217
parameters.map((param) => this.createParameter(param)),
217-
undefined,
218+
returnType ?? undefined,
218219
body,
219220
);
220221
}
@@ -223,6 +224,7 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
223224
functionName: string | null,
224225
parameters: Parameter<ts.TypeNode>[],
225226
body: ts.Statement,
227+
returnType: ts.TypeNode | null,
226228
): ts.Expression {
227229
if (!ts.isBlock(body)) {
228230
throw new Error(`Invalid syntax, expected a block, but got ${ts.SyntaxKind[body.kind]}.`);
@@ -233,14 +235,15 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
233235
functionName ?? undefined,
234236
undefined,
235237
parameters.map((param) => this.createParameter(param)),
236-
undefined,
238+
returnType ?? undefined,
237239
body,
238240
);
239241
}
240242

241243
createArrowFunctionExpression(
242244
parameters: Parameter<ts.TypeNode>[],
243245
body: ts.Statement | ts.Expression,
246+
returnType: ts.TypeNode | null,
244247
): ts.Expression {
245248
if (ts.isStatement(body) && !ts.isBlock(body)) {
246249
throw new Error(`Invalid syntax, expected a block, but got ${ts.SyntaxKind[body.kind]}.`);
@@ -250,7 +253,7 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
250253
undefined,
251254
undefined,
252255
parameters.map((param) => this.createParameter(param)),
253-
undefined,
256+
returnType ?? undefined,
254257
undefined,
255258
body,
256259
);

0 commit comments

Comments
 (0)