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>`, `

View File

@ -92,7 +92,7 @@ export {getParseErrors, isSyntaxError, syntaxError, Version} from './util';
export {SourceMap} from './output/source_map';
export * from './injectable_compiler_2';
export * from './render3/view/api';
export {BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, Element as TmplAstElement, Node as TmplAstNode, RecursiveVisitor as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text as TmplAstText, TextAttribute as TmplAstTextAttribute, Variable as TmplAstVariable,} from './render3/r3_ast';
export {BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, Element as TmplAstElement, Icu as TmplAstIcu, Node as TmplAstNode, RecursiveVisitor as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text as TmplAstText, TextAttribute as TmplAstTextAttribute, Variable as TmplAstVariable} from './render3/r3_ast';
export * from './render3/view/t2_api';
export * from './render3/view/t2_binder';
export {Identifiers as R3Identifiers} from './render3/r3_identifiers';