From 0a16e60afae27c7af023cf617de05297dddf5135 Mon Sep 17 00:00:00 2001 From: JoostK Date: Thu, 1 Oct 2020 00:22:54 +0200 Subject: [PATCH] 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 --- .../src/ngtsc/typecheck/src/type_check_block.ts | 15 ++++++++++++++- .../src/ngtsc/typecheck/test/diagnostics_spec.ts | 15 +++++++++++++++ packages/compiler/src/compiler.ts | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index a5971b595b..4864dfaec2 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -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 { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts index 5838f2fc8e..0104b024a2 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts @@ -176,6 +176,21 @@ runInEachFileSystem(() => { ]); }); + it('checks expressions in ICUs', () => { + const messages = diagnose( + `{switch, plural, other { {{interpolation}} + {nestedSwitch, plural, other { {{nestedInterpolation}} }} + }}`, + `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( `
{{ person.name | pipe:person.age:1 }}
`, ` diff --git a/packages/compiler/src/compiler.ts b/packages/compiler/src/compiler.ts index c734932b88..e60ef28312 100644 --- a/packages/compiler/src/compiler.ts +++ b/packages/compiler/src/compiler.ts @@ -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';