Skip to content

Commit

Permalink
refactor(compiler-cli): add extendedTemplateCheck phase to compiler (
Browse files Browse the repository at this point in the history
…#43107)

This commit integrates extended template checks with the compiler, by
adding another phase of diagnostics generation. This integration is
under the `_extendedTemplateDiagnostics` flag.

Refs #42966

PR Close #43107
  • Loading branch information
danieltre23 authored and alxhub committed Aug 19, 2021
1 parent f70d2a2 commit 58a19a3
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 9 deletions.
1 change: 1 addition & 0 deletions packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel
Expand Up @@ -26,6 +26,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/transform",
"//packages/compiler-cli/src/ngtsc/typecheck/api",
"//packages/compiler-cli/src/ngtsc/typecheck/diagnostics",
"//packages/compiler-cli/src/ngtsc/typecheck/extended/api",
"//packages/compiler-cli/src/ngtsc/util",
"//packages/compiler-cli/src/ngtsc/xi18n",
"@npm//@types/node",
Expand Down
7 changes: 7 additions & 0 deletions packages/compiler-cli/src/ngtsc/annotations/src/component.ts
Expand Up @@ -24,6 +24,7 @@ import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost, reflectObj
import {ComponentScopeReader, LocalModuleScopeRegistry, TypeCheckScopeRegistry} from '../../scope';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform';
import {TemplateSourceMapping, TypeCheckContext} from '../../typecheck/api';
import {ExtendedTemplateChecker} from '../../typecheck/extended/api';
import {SubsetOfKeys} from '../../util/src/typescript';
import {Xi18nContext} from '../../xi18n';

Expand Down Expand Up @@ -612,6 +613,12 @@ export class ComponentDecoratorHandler implements
meta.template.sourceMapping, meta.template.file, meta.template.errors);
}

extendedTemplateCheck(
component: ts.ClassDeclaration,
extendedTemplateChecker: ExtendedTemplateChecker): ts.Diagnostic[] {
return extendedTemplateChecker.getExtendedTemplateDiagnosticsForComponent(component);
}

resolve(
node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>,
symbol: ComponentSymbol): ResolveResult<ComponentResolutionData> {
Expand Down
2 changes: 2 additions & 0 deletions packages/compiler-cli/src/ngtsc/core/BUILD.bazel
Expand Up @@ -37,6 +37,8 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/typecheck",
"//packages/compiler-cli/src/ngtsc/typecheck/api",
"//packages/compiler-cli/src/ngtsc/typecheck/diagnostics",
"//packages/compiler-cli/src/ngtsc/typecheck/extended",
"//packages/compiler-cli/src/ngtsc/typecheck/extended/src/template_checks/invalid_banana_in_box",
"//packages/compiler-cli/src/ngtsc/util",
"//packages/compiler-cli/src/ngtsc/xi18n",
"@npm//typescript",
Expand Down
12 changes: 11 additions & 1 deletion packages/compiler-cli/src/ngtsc/core/api/src/options.ts
Expand Up @@ -36,6 +36,16 @@ export interface TestOnlyOptions {
tracePerformance?: string;
}

/**
* Internal only options for compiler.
*/
export interface InternalOptions {
/**
* Whether to run all template checks and generate extended template diagnostics.
*/
_extendedTemplateDiagnostics?: boolean;
}

/**
* A merged interface of all of the various Angular compiler options, as well as the standard
* `ts.CompilerOptions`.
Expand All @@ -45,4 +55,4 @@ export interface TestOnlyOptions {
export interface NgCompilerOptions extends ts.CompilerOptions, LegacyNgcOptions, BazelAndG3Options,
NgcCompatibilityOptions, StrictTemplateOptions,
TestOnlyOptions, I18nOptions, TargetOptions,
MiscOptions {}
InternalOptions, MiscOptions {}
52 changes: 46 additions & 6 deletions packages/compiler-cli/src/ngtsc/core/src/compiler.ts
Expand Up @@ -30,6 +30,8 @@ import {ivySwitchTransform} from '../../switch';
import {aliasTransformFactory, CompilationMode, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform';
import {TemplateTypeCheckerImpl} from '../../typecheck';
import {OptimizeFor, TemplateTypeChecker, TypeCheckingConfig} from '../../typecheck/api';
import {ExtendedTemplateCheckerImpl} from '../../typecheck/extended';
import {InvalidBananaInBoxCheck} from '../../typecheck/extended/src/template_checks/invalid_banana_in_box';
import {getSourceFileOrNull, isDtsPath, resolveModuleName, toUnredirectedSourceFile} from '../../util/src/typescript';
import {Xi18nContext} from '../../xi18n';
import {LazyRoute, NgCompilerAdapter, NgCompilerOptions} from '../api';
Expand Down Expand Up @@ -316,6 +318,12 @@ export class NgCompiler {
readonly usePoisonedData: boolean,
private livePerfRecorder: ActivePerfRecorder,
) {
if (this.options._extendedTemplateDiagnostics === true &&
this.options.strictTemplates === false) {
throw new Error(
'The \'_extendedTemplateDiagnostics\' option requires \'strictTemplates\' to also be enabled.');
}

this.constructionDiagnostics.push(...this.adapter.constructionDiagnostics);
const incompatibleTypeCheckOptionsDiagnostic = verifyCompatibleTypeCheckOptions(this.options);
if (incompatibleTypeCheckOptionsDiagnostic !== null) {
Expand Down Expand Up @@ -425,8 +433,12 @@ export class NgCompiler {
* Get all Angular-related diagnostics for this compilation.
*/
getDiagnostics(): ts.Diagnostic[] {
return this.addMessageTextDetails(
[...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics()]);
const diagnostics: ts.Diagnostic[] = [];
diagnostics.push(...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics());
if (this.options._extendedTemplateDiagnostics) {
diagnostics.push(...this.getExtendedTemplateDiagnostics());
}
return this.addMessageTextDetails(diagnostics);
}

/**
Expand All @@ -435,10 +447,14 @@ export class NgCompiler {
* If a `ts.SourceFile` is passed, only diagnostics related to that file are returned.
*/
getDiagnosticsForFile(file: ts.SourceFile, optimizeFor: OptimizeFor): ts.Diagnostic[] {
return this.addMessageTextDetails([
...this.getNonTemplateDiagnostics().filter(diag => diag.file === file),
...this.getTemplateDiagnosticsForFile(file, optimizeFor)
]);
const diagnostics: ts.Diagnostic[] = [];
diagnostics.push(
...this.getNonTemplateDiagnostics().filter(diag => diag.file === file),
...this.getTemplateDiagnosticsForFile(file, optimizeFor));
if (this.options._extendedTemplateDiagnostics) {
diagnostics.push(...this.getExtendedTemplateDiagnostics(file));
}
return this.addMessageTextDetails(diagnostics);
}

/**
Expand Down Expand Up @@ -892,6 +908,30 @@ export class NgCompiler {
return this.nonTemplateDiagnostics;
}

/**
* Calls the `extendedTemplateCheck` phase of the trait compiler
* @param sf optional parameter to get diagnostics for a certain file
* or all files in the program if `sf` is undefined
* @returns generated extended template diagnostics
*/
private getExtendedTemplateDiagnostics(sf?: ts.SourceFile): ts.Diagnostic[] {
const diagnostics: ts.Diagnostic[] = [];
const compilation = this.ensureAnalyzed();
const typeChecker = this.inputProgram.getTypeChecker();
const templateChecks = [new InvalidBananaInBoxCheck()];
const extendedTemplateChecker = new ExtendedTemplateCheckerImpl(
compilation.templateTypeChecker, typeChecker, templateChecks);
if (sf !== undefined) {
return compilation.traitCompiler.extendedTemplateCheck(sf, extendedTemplateChecker);
}
for (const sf of this.inputProgram.getSourceFiles()) {
diagnostics.push(
...compilation.traitCompiler.extendedTemplateCheck(sf, extendedTemplateChecker));
}

return diagnostics;
}

private makeCompilation(): LazyCompilationState {
const checker = this.inputProgram.getTypeChecker();

Expand Down
1 change: 1 addition & 0 deletions packages/compiler-cli/src/ngtsc/transform/BUILD.bazel
Expand Up @@ -19,6 +19,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/translator",
"//packages/compiler-cli/src/ngtsc/typecheck/api",
"//packages/compiler-cli/src/ngtsc/typecheck/extended/api",
"//packages/compiler-cli/src/ngtsc/util",
"//packages/compiler-cli/src/ngtsc/xi18n",
"@npm//typescript",
Expand Down
5 changes: 5 additions & 0 deletions packages/compiler-cli/src/ngtsc/transform/src/api.ts
Expand Up @@ -15,6 +15,7 @@ import {IndexingContext} from '../../indexer';
import {ClassDeclaration, Decorator} from '../../reflection';
import {ImportManager} from '../../translator';
import {TypeCheckContext} from '../../typecheck/api';
import {ExtendedTemplateChecker} from '../../typecheck/extended/api';
import {Xi18nContext} from '../../xi18n';

/**
Expand Down Expand Up @@ -187,6 +188,10 @@ export interface DecoratorHandler<D, A, S extends SemanticSymbol|null, R> {
(ctx: TypeCheckContext, node: ClassDeclaration, analysis: Readonly<A>,
resolution: Readonly<R>): void;

extendedTemplateCheck?
(component: ts.ClassDeclaration, extendedTemplateChecker: ExtendedTemplateChecker):
ts.Diagnostic[];

/**
* Generate a description of the field which should be added to the class, including any
* initialization code to be generated.
Expand Down
26 changes: 25 additions & 1 deletion packages/compiler-cli/src/ngtsc/transform/src/compilation.ts
Expand Up @@ -14,8 +14,9 @@ import {IncrementalBuild} from '../../incremental/api';
import {SemanticDepGraphUpdater, SemanticSymbol} from '../../incremental/semantic_graph';
import {IndexingContext} from '../../indexer';
import {PerfEvent, PerfRecorder} from '../../perf';
import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost} from '../../reflection';
import {ClassDeclaration, DeclarationNode, Decorator, isNamedClassDeclaration, ReflectionHost} from '../../reflection';
import {ProgramTypeCheckAdapter, TypeCheckContext} from '../../typecheck/api';
import {ExtendedTemplateChecker} from '../../typecheck/extended/api';
import {getSourceFile, isExported} from '../../util/src/typescript';
import {Xi18nContext} from '../../xi18n';

Expand Down Expand Up @@ -490,6 +491,29 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
}
}

extendedTemplateCheck(sf: ts.SourceFile, extendedTemplateChecker: ExtendedTemplateChecker):
ts.Diagnostic[] {
const classes = this.fileToClasses.get(sf);
if (classes === undefined) {
return [];
}

const diagnostics: ts.Diagnostic[] = [];
for (const clazz of classes) {
if (!isNamedClassDeclaration(clazz)) {
continue;
}
const record = this.classes.get(clazz)!;
for (const trait of record.traits) {
if (trait.handler.extendedTemplateCheck === undefined) {
continue;
}
diagnostics.push(...trait.handler.extendedTemplateCheck(clazz, extendedTemplateChecker));
}
}
return diagnostics;
}

index(ctx: IndexingContext): void {
for (const clazz of this.classes.keys()) {
const record = this.classes.get(clazz)!;
Expand Down
Expand Up @@ -3,7 +3,7 @@ load("//tools:defaults.bzl", "ts_library")
ts_library(
name = "invalid_banana_in_box",
srcs = ["index.ts"],
visibility = ["//packages/compiler-cli/src/ngtsc/typecheck/extended:__subpackages__"],
visibility = ["//packages/compiler-cli/src/ngtsc:__subpackages__"],
deps = [
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/diagnostics",
Expand Down

0 comments on commit 58a19a3

Please sign in to comment.