Skip to content

Commit 8e237a0

Browse files
devversionthePunderWoman
authored andcommitted
fix(compiler-cli): properly catch fatal diagnostics in type checking (angular#54309)
An identical addition to: 760b1f3. This commit expands the `try/catch`-es: - to properly NOT throw and just convert the diagnostic. - to be in place for all top-level instances. Notably, this logic cannot reside in the template type checker directly as otherwise we would risk multiple duplicate diagnostics. PR Close angular#54309
1 parent e921e10 commit 8e237a0

2 files changed

Lines changed: 71 additions & 26 deletions

File tree

packages/compiler-cli/src/ngtsc/core/src/compiler.ts

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -427,11 +427,28 @@ export class NgCompiler {
427427
* Get all Angular-related diagnostics for this compilation.
428428
*/
429429
getDiagnostics(): ts.Diagnostic[] {
430-
const diagnostics: ts.Diagnostic[] = [];
431-
diagnostics.push(...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics());
432-
if (this.options.strictTemplates) {
433-
diagnostics.push(...this.getExtendedTemplateDiagnostics());
430+
const diagnostics: ts.Diagnostic[] = [
431+
...this.getNonTemplateDiagnostics(),
432+
];
433+
434+
// Type check code may throw fatal diagnostic errors if e.g. the type check
435+
// block cannot be generated. Gracefully return the associated diagnostic.
436+
// Note: If a fatal diagnostic is raised, do not repeat the same diagnostics
437+
// by running the extended template checking code, which will attempt to
438+
// generate the same TCB.
439+
try {
440+
diagnostics.push(...this.getTemplateDiagnostics());
441+
442+
if (this.options.strictTemplates) {
443+
diagnostics.push(...this.getExtendedTemplateDiagnostics());
444+
}
445+
} catch (err: unknown) {
446+
if (!(err instanceof FatalDiagnosticError)) {
447+
throw err;
448+
}
449+
diagnostics.push(err.toDiagnostic());
434450
}
451+
435452
return this.addMessageTextDetails(diagnostics);
436453
}
437454

@@ -444,21 +461,22 @@ export class NgCompiler {
444461
const diagnostics: ts.Diagnostic[] =
445462
[...this.getNonTemplateDiagnostics().filter(diag => diag.file === file)];
446463

464+
// Type check code may throw fatal diagnostic errors if e.g. the type check
465+
// block cannot be generated. Gracefully return the associated diagnostic.
466+
// Note: If a fatal diagnostic is raised, do not repeat the same diagnostics
467+
// by running the extended template checking code, which will attempt to
468+
// generate the same TCB.
447469
try {
448470
diagnostics.push(...this.getTemplateDiagnosticsForFile(file, optimizeFor));
471+
449472
if (this.options.strictTemplates) {
450473
diagnostics.push(...this.getExtendedTemplateDiagnostics(file));
451474
}
452-
} catch (e) {
453-
// Type check code may throw fatal diagnostic errors if e.g. the type check
454-
// block cannot be generated. Gracefully return the associated diagnostic.
455-
// Note: If a fatal diagnostic is raised, do not repeat the same diagnostics
456-
// by running the extended template checking code, which will attempt to
457-
// generate the same TCB.
458-
if (e instanceof FatalDiagnosticError) {
459-
diagnostics.push(e.toDiagnostic());
475+
} catch (err: unknown) {
476+
if (!(err instanceof FatalDiagnosticError)) {
477+
throw err;
460478
}
461-
throw e;
479+
diagnostics.push(err.toDiagnostic());
462480
}
463481

464482
return this.addMessageTextDetails(diagnostics);
@@ -471,6 +489,12 @@ export class NgCompiler {
471489
const compilation = this.ensureAnalyzed();
472490
const ttc = compilation.templateTypeChecker;
473491
const diagnostics: ts.Diagnostic[] = [];
492+
493+
// Type check code may throw fatal diagnostic errors if e.g. the type check
494+
// block cannot be generated. Gracefully return the associated diagnostic.
495+
// Note: If a fatal diagnostic is raised, do not repeat the same diagnostics
496+
// by running the extended template checking code, which will attempt to
497+
// generate the same TCB.
474498
try {
475499
diagnostics.push(...ttc.getDiagnosticsForComponent(component));
476500

@@ -884,23 +908,16 @@ export class NgCompiler {
884908

885909
private getTemplateDiagnostics(): ReadonlyArray<ts.Diagnostic> {
886910
const compilation = this.ensureAnalyzed();
887-
888-
// Get the diagnostics.
889911
const diagnostics: ts.Diagnostic[] = [];
912+
913+
// Get diagnostics for all files.
890914
for (const sf of this.inputProgram.getSourceFiles()) {
891915
if (sf.isDeclarationFile || this.adapter.isShim(sf)) {
892916
continue;
893917
}
894918

895-
try {
896-
diagnostics.push(
897-
...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
898-
} catch (err) {
899-
if (!(err instanceof FatalDiagnosticError)) {
900-
throw err;
901-
}
902-
diagnostics.push(err.toDiagnostic());
903-
}
919+
diagnostics.push(
920+
...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
904921
}
905922

906923
const program = this.programDriver.getProgram();
@@ -1028,8 +1045,8 @@ export class NgCompiler {
10281045
]);
10291046

10301047
// If an entrypoint is present, then all user imports should be directed through the
1031-
// entrypoint and private exports are not needed. The compiler will validate that all publicly
1032-
// visible directives/pipes are importable via this entrypoint.
1048+
// entrypoint and private exports are not needed. The compiler will validate that all
1049+
// publicly visible directives/pipes are importable via this entrypoint.
10331050
if (this.entryPoint === null && this.options.generateDeepReexports === true) {
10341051
// No entrypoint is present and deep re-exports were requested, so configure the aliasing
10351052
// system to generate them.

packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,34 @@ export declare class AnimationEvent {
6969
expect(getSourceCodeForDiagnostic(diags[0])).toBe('does_not_exist');
7070
});
7171

72+
it('should not fail with a runtime error when generating TCB', () => {
73+
env.tsconfig({strictTemplates: true});
74+
env.write('test.ts', `
75+
import {Component, input} from '@angular/core';
76+
77+
@Component({
78+
selector: 'sub-cmp',
79+
standalone: true,
80+
template: '',
81+
})
82+
class Sub { // intentionally not exported
83+
someInput = input.required<string>();
84+
}
85+
86+
@Component({
87+
template: \`<sub-cmp [someInput]="''" />\`,
88+
standalone: true,
89+
imports: [Sub],
90+
})
91+
export class MyComponent {}
92+
`);
93+
94+
const diagnostics = env.driveDiagnostics();
95+
expect(diagnostics).toEqual([jasmine.objectContaining(
96+
{messageText: jasmine.objectContaining({messageText: 'Unable to import symbol Sub.'})},
97+
)]);
98+
});
99+
72100
it('should check regular attributes that are directive inputs', () => {
73101
env.tsconfig(
74102
{fullTemplateTypeCheck: true, strictInputTypes: true, strictAttributeTypes: true});

0 commit comments

Comments
 (0)