fix(compiler-cli): type checking of expressions within ICUs (#39072)

Expressions within ICU expressions in templates were not previously
type-checked, as they were skipped while traversing the elements
within a template. This commit enables type checking of these
expressions by actually visiting the expressions.

BREAKING CHANGE:
Expressions within ICUs are now type-checked again, fixing a regression
in Ivy. This may cause compilation failures if errors are found in
expressions that appear within an ICU. Please correct these expressions
to resolve the type-check errors.

Fixes #39064

PR Close #39072
This commit is contained in:
JoostK
2020-10-01 00:22:54 +02:00
committed by atscott
parent f84b3786dc
commit 0a16e60afa
3 changed files with 30 additions and 2 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AST, BindingPipe, BindingType, BoundTarget, DYNAMIC_TYPE, ImplicitReceiver, MethodCall, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, SchemaMetadata, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler';
import {AST, BindingPipe, BindingType, BoundTarget, DYNAMIC_TYPE, ImplicitReceiver, MethodCall, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, SchemaMetadata, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstElement, TmplAstIcu, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler';
import * as ts from 'typescript';
import {Reference} from '../../imports';
@ -1333,6 +1333,8 @@ class Scope {
this.checkAndAppendReferencesOfNode(node);
} else if (node instanceof TmplAstBoundText) {
this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, node));
} else if (node instanceof TmplAstIcu) {
this.appendIcuExpressions(node);
}
}
@ -1459,6 +1461,17 @@ class Scope {
this.appendDeepSchemaChecks(node.children);
}
}
private appendIcuExpressions(node: TmplAstIcu): void {
for (const variable of Object.values(node.vars)) {
this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, variable));
}
for (const placeholder of Object.values(node.placeholders)) {
if (placeholder instanceof TmplAstBoundText) {
this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, placeholder));
}
}
}
}
interface TcbBoundInput {

View File

@ -176,6 +176,21 @@ runInEachFileSystem(() => {
]);
});
it('checks expressions in ICUs', () => {
const messages = diagnose(
`<span i18n>{switch, plural, other { {{interpolation}}
{nestedSwitch, plural, other { {{nestedInterpolation}} }}
}}</span>`,
`class TestComponent {}`);
expect(messages.sort()).toEqual([
`TestComponent.html(1, 13): Property 'switch' does not exist on type 'TestComponent'.`,
`TestComponent.html(1, 39): Property 'interpolation' does not exist on type 'TestComponent'.`,
`TestComponent.html(2, 14): Property 'nestedSwitch' does not exist on type 'TestComponent'.`,
`TestComponent.html(2, 46): Property 'nestedInterpolation' does not exist on type 'TestComponent'.`,
]);
});
it('produces diagnostics for pipes', () => {
const messages = diagnose(
`<div>{{ person.name | pipe:person.age:1 }}</div>`, `