Skip to content

Commit 40e1edc

Browse files
devversionthePunderWoman
authored andcommitted
fix(compiler-cli): properly catch fatal diagnostics in type checking (#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 #54309
1 parent 4482cbd commit 40e1edc

File tree

2 files changed

+71
-26
lines changed

2 files changed

+71
-26
lines changed

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

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

@@ -437,21 +454,22 @@ export class NgCompiler {
437454
const diagnostics: ts.Diagnostic[] =
438455
[...this.getNonTemplateDiagnostics().filter(diag => diag.file === file)];
439456

457+
// Type check code may throw fatal diagnostic errors if e.g. the type check
458+
// block cannot be generated. Gracefully return the associated diagnostic.
459+
// Note: If a fatal diagnostic is raised, do not repeat the same diagnostics
460+
// by running the extended template checking code, which will attempt to
461+
// generate the same TCB.
440462
try {
441463
diagnostics.push(...this.getTemplateDiagnosticsForFile(file, optimizeFor));
464+
442465
if (this.options.strictTemplates) {
443466
diagnostics.push(...this.getExtendedTemplateDiagnostics(file));
444467
}
445-
} catch (e) {
446-
// Type check code may throw fatal diagnostic errors if e.g. the type check
447-
// block cannot be generated. Gracefully return the associated diagnostic.
448-
// Note: If a fatal diagnostic is raised, do not repeat the same diagnostics
449-
// by running the extended template checking code, which will attempt to
450-
// generate the same TCB.
451-
if (e instanceof FatalDiagnosticError) {
452-
diagnostics.push(e.toDiagnostic());
468+
} catch (err: unknown) {
469+
if (!(err instanceof FatalDiagnosticError)) {
470+
throw err;
453471
}
454-
throw e;
472+
diagnostics.push(err.toDiagnostic());
455473
}
456474

457475
return this.addMessageTextDetails(diagnostics);
@@ -464,6 +482,12 @@ export class NgCompiler {
464482
const compilation = this.ensureAnalyzed();
465483
const ttc = compilation.templateTypeChecker;
466484
const diagnostics: ts.Diagnostic[] = [];
485+
486+
// Type check code may throw fatal diagnostic errors if e.g. the type check
487+
// block cannot be generated. Gracefully return the associated diagnostic.
488+
// Note: If a fatal diagnostic is raised, do not repeat the same diagnostics
489+
// by running the extended template checking code, which will attempt to
490+
// generate the same TCB.
467491
try {
468492
diagnostics.push(...ttc.getDiagnosticsForComponent(component));
469493

@@ -876,23 +900,16 @@ export class NgCompiler {
876900

877901
private getTemplateDiagnostics(): ReadonlyArray<ts.Diagnostic> {
878902
const compilation = this.ensureAnalyzed();
879-
880-
// Get the diagnostics.
881903
const diagnostics: ts.Diagnostic[] = [];
904+
905+
// Get diagnostics for all files.
882906
for (const sf of this.inputProgram.getSourceFiles()) {
883907
if (sf.isDeclarationFile || this.adapter.isShim(sf)) {
884908
continue;
885909
}
886910

887-
try {
888-
diagnostics.push(
889-
...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
890-
} catch (err) {
891-
if (!(err instanceof FatalDiagnosticError)) {
892-
throw err;
893-
}
894-
diagnostics.push(err.toDiagnostic());
895-
}
911+
diagnostics.push(
912+
...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
896913
}
897914

898915
const program = this.programDriver.getProgram();
@@ -1018,8 +1035,8 @@ export class NgCompiler {
10181035
]);
10191036

10201037
// If an entrypoint is present, then all user imports should be directed through the
1021-
// entrypoint and private exports are not needed. The compiler will validate that all publicly
1022-
// visible directives/pipes are importable via this entrypoint.
1038+
// entrypoint and private exports are not needed. The compiler will validate that all
1039+
// publicly visible directives/pipes are importable via this entrypoint.
10231040
if (this.entryPoint === null && this.options.generateDeepReexports === true) {
10241041
// No entrypoint is present and deep re-exports were requested, so configure the aliasing
10251042
// 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)