Skip to content

Commit

Permalink
fix(compiler-cli): type checking of expressions within ICUs (#39072)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
JoostK authored and atscott committed Oct 8, 2020
1 parent f84b378 commit 0a16e60
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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 {
Expand Down
15 changes: 15 additions & 0 deletions packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>`, `
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down

0 comments on commit 0a16e60

Please sign in to comment.