fix(ivy): let ngtsc evaluate default parameters in the callee context (#29888)

Previously, during the evaluation of a function call where no argument
was provided for a parameter that has a default value, the default value
would be taken from the context of the caller, instead of the callee.

This commit fixes the behavior by resolving the default value of a
parameter in the context of the callee.

PR Close #29888
This commit is contained in:
JoostK 2019-04-13 21:24:02 +02:00 committed by Ben Lesh
parent cb34514d05
commit c3c0df9d56
2 changed files with 10 additions and 4 deletions

View File

@ -427,20 +427,20 @@ export class StaticInterpreter {
const args = this.evaluateFunctionArguments(node, context); const args = this.evaluateFunctionArguments(node, context);
const newScope: Scope = new Map<ts.ParameterDeclaration, ResolvedValue>(); const newScope: Scope = new Map<ts.ParameterDeclaration, ResolvedValue>();
const calleeContext = {...context, scope: newScope};
fn.parameters.forEach((param, index) => { fn.parameters.forEach((param, index) => {
let arg = args[index]; let arg = args[index];
if (param.node.dotDotDotToken !== undefined) { if (param.node.dotDotDotToken !== undefined) {
arg = args.slice(index); arg = args.slice(index);
} }
if (arg === undefined && param.initializer !== null) { if (arg === undefined && param.initializer !== null) {
arg = this.visitExpression(param.initializer, context); arg = this.visitExpression(param.initializer, calleeContext);
} }
newScope.set(param.node, arg); newScope.set(param.node, arg);
}); });
return ret.expression !== undefined ? return ret.expression !== undefined ? this.visitExpression(ret.expression, calleeContext) :
this.visitExpression(ret.expression, {...context, scope: newScope}) : undefined;
undefined;
} }
private visitConditionalExpression(node: ts.ConditionalExpression, context: Context): private visitConditionalExpression(node: ts.ConditionalExpression, context: Context):

View File

@ -75,6 +75,12 @@ describe('ngtsc metadata', () => {
expect(evaluate(`function foo(bar) { return bar; }`, 'foo("test")')).toEqual('test'); expect(evaluate(`function foo(bar) { return bar; }`, 'foo("test")')).toEqual('test');
}); });
it('function call default value works', () => {
expect(evaluate(`function foo(bar = 1) { return bar; }`, 'foo()')).toEqual(1);
expect(evaluate(`function foo(bar = 1) { return bar; }`, 'foo(2)')).toEqual(2);
expect(evaluate(`function foo(a, c = a) { return c; }; const a = 1;`, 'foo(2)')).toEqual(2);
});
it('function call spread works', () => { it('function call spread works', () => {
expect(evaluate(`function foo(a, ...b) { return [a, b]; }`, 'foo(1, ...[2, 3])')).toEqual([ expect(evaluate(`function foo(a, ...b) { return [a, b]; }`, 'foo(1, ...[2, 3])')).toEqual([
1, [2, 3] 1, [2, 3]