Skip to content

Commit 9cf14ff

Browse files
JoostKdylhunn
authored andcommitted
feat(compiler-cli): exclude abstract classes from strictInjectionParameters requirement (#44615)
In AOT compilations, the `strictInjectionParameters` compiler option can be enabled to report errors when an `@Injectable` annotated class has a constructor with parameters that do not provide an injection token, e.g. only a primitive type or interface. Since Ivy it's become required that any class with Angular behavior (e.g. the `ngOnDestroy` lifecycle hook) is decorated using an Angular decorator, which meant that `@Injectable()` may need to have been added to abstract base classes. Doing so would then report an error if `strictInjectionParameters` is enabled, if the abstract class has an incompatible constructor for DI purposes. This may be fine though, as a subclass may call the constructor explicitly without relying on Angular's DI mechanism. Therefore, this commit excludes abstract classes from the `strictInjectionParameters` check. This avoids an error from being reported at compile time. If the constructor ends up being used by Angular's DI system at runtime, then the factory function of the abstract class will throw an error by means of the `ɵɵinvalidFactory` instruction. In addition to the runtime error, this commit also analyzes the inheritance chain of an injectable without a constructor to verify that their inherited constructor is valid. Closes #37914 PR Close #44615
1 parent 401dec4 commit 9cf14ff

File tree

23 files changed

+632
-114
lines changed

23 files changed

+632
-114
lines changed

goldens/public-api/compiler-cli/error_code.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export enum ErrorCode {
4242
IMPORT_CYCLE_DETECTED = 3003,
4343
IMPORT_GENERATION_FAILURE = 3004,
4444
INJECTABLE_DUPLICATE_PROV = 9001,
45+
INJECTABLE_INHERITS_INVALID_CONSTRUCTOR = 2013,
4546
INLINE_TCB_REQUIRED = 8900,
4647
INLINE_TYPE_CTOR_REQUIRED = 8901,
4748
INVALID_BANANA_IN_BOX = 8101,

packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ import ts from 'typescript';
1010

1111
import {ParsedConfiguration} from '../../..';
1212
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations';
13+
import {InjectableClassRegistry} from '../../../src/ngtsc/annotations/common';
1314
import {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../../src/ngtsc/cycles';
1415
import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics';
1516
import {absoluteFromSourceFile, LogicalFileSystem, ReadonlyFileSystem} from '../../../src/ngtsc/file_system';
1617
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports';
1718
import {SemanticSymbol} from '../../../src/ngtsc/incremental/semantic_graph';
18-
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../../src/ngtsc/metadata';
19+
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, LocalMetadataRegistry, ResourceRegistry} from '../../../src/ngtsc/metadata';
1920
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
2021
import {NOOP_PERF_RECORDER} from '../../../src/ngtsc/perf';
2122
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver, TypeCheckScopeRegistry} from '../../../src/ngtsc/scope';
@@ -97,14 +98,15 @@ export class DecorationAnalyzer {
9798
new PartialEvaluator(this.reflectionHost, this.typeChecker, /* dependencyTracker */ null);
9899
importGraph = new ImportGraph(this.typeChecker, NOOP_PERF_RECORDER);
99100
cycleAnalyzer = new CycleAnalyzer(this.importGraph);
100-
injectableRegistry = new InjectableClassRegistry(this.reflectionHost);
101+
injectableRegistry = new InjectableClassRegistry(this.reflectionHost, this.isCore);
101102
typeCheckScopeRegistry = new TypeCheckScopeRegistry(this.scopeRegistry, this.fullMetaReader);
102103
handlers: DecoratorHandler<unknown, unknown, SemanticSymbol|null, unknown>[] = [
103104
new ComponentDecoratorHandler(
104105
this.reflectionHost, this.evaluator, this.fullRegistry, this.fullMetaReader,
105106
this.scopeRegistry, this.dtsModuleScopeResolver, this.scopeRegistry,
106-
this.typeCheckScopeRegistry, new ResourceRegistry(), this.isCore, this.resourceManager,
107-
this.rootDirs, !!this.compilerOptions.preserveWhitespaces,
107+
this.typeCheckScopeRegistry, new ResourceRegistry(), this.isCore,
108+
/* strictCtorDeps */ false, this.resourceManager, this.rootDirs,
109+
!!this.compilerOptions.preserveWhitespaces,
108110
/* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat,
109111
/* usePoisonedData */ false,
110112
/* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer,
@@ -117,7 +119,7 @@ export class DecorationAnalyzer {
117119
// clang-format off
118120
new DirectiveDecoratorHandler(
119121
this.reflectionHost, this.evaluator, this.fullRegistry, this.scopeRegistry,
120-
this.fullMetaReader, this.injectableRegistry, this.isCore,
122+
this.fullMetaReader, this.injectableRegistry, this.isCore, /* strictCtorDeps */ false,
121123
/* semanticDepGraphUpdater */ null,
122124
!!this.compilerOptions.annotateForClosureCompiler,
123125
// In ngcc we want to compile undecorated classes with Angular features. As of
@@ -134,7 +136,7 @@ export class DecorationAnalyzer {
134136
this.reflectionHost, this.evaluator, this.metaRegistry, this.scopeRegistry,
135137
this.injectableRegistry, this.isCore, NOOP_PERF_RECORDER),
136138
new InjectableDecoratorHandler(
137-
this.reflectionHost, this.isCore,
139+
this.reflectionHost, this.evaluator, this.isCore,
138140
/* strictCtorDeps */ false, this.injectableRegistry, NOOP_PERF_RECORDER,
139141
/* errorOnDuplicateProv */ false),
140142
new NgModuleDecoratorHandler(

packages/compiler-cli/src/ngtsc/annotations/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export * from './src/di';
1111
export * from './src/diagnostics';
1212
export * from './src/evaluation';
1313
export * from './src/factory';
14+
export * from './src/injectable_registry';
1415
export * from './src/metadata';
1516
export * from './src/references_registry';
1617
export * from './src/schema';

packages/compiler-cli/src/ngtsc/annotations/common/src/diagnostics.ts

Lines changed: 91 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import ts from 'typescript';
1010

1111
import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../../diagnostics';
1212
import {Reference} from '../../../imports';
13-
import {InjectableClassRegistry, MetadataReader} from '../../../metadata';
1413
import {describeResolvedType, DynamicValue, PartialEvaluator, ResolvedValue, traceDynamicValue} from '../../../partial_evaluator';
1514
import {ClassDeclaration, ReflectionHost} from '../../../reflection';
1615
import {DeclarationData, LocalModuleScopeRegistry} from '../../../scope';
1716
import {identifierOfNode} from '../../../util/src/typescript';
1817

19-
import {readBaseClass} from './util';
18+
import {InjectableClassRegistry} from './injectable_registry';
19+
import {isAbstractClassDeclaration, readBaseClass} from './util';
2020

2121

2222
/**
@@ -102,7 +102,10 @@ export function getProviderDiagnostics(
102102
const diagnostics: ts.Diagnostic[] = [];
103103

104104
for (const provider of providerClasses) {
105-
if (registry.isInjectable(provider.node)) {
105+
const injectableMeta = registry.getInjectableMeta(provider.node);
106+
if (injectableMeta !== null) {
107+
// The provided type is recognized as injectable, so we don't report a diagnostic for this
108+
// provider.
106109
continue;
107110
}
108111

@@ -124,9 +127,9 @@ Either add the @Injectable() decorator to '${
124127
}
125128

126129
export function getDirectiveDiagnostics(
127-
node: ClassDeclaration, reader: MetadataReader, evaluator: PartialEvaluator,
128-
reflector: ReflectionHost, scopeRegistry: LocalModuleScopeRegistry,
129-
kind: string): ts.Diagnostic[]|null {
130+
node: ClassDeclaration, injectableRegistry: InjectableClassRegistry,
131+
evaluator: PartialEvaluator, reflector: ReflectionHost, scopeRegistry: LocalModuleScopeRegistry,
132+
strictInjectionParameters: boolean, kind: 'Directive'|'Component'): ts.Diagnostic[]|null {
130133
let diagnostics: ts.Diagnostic[]|null = [];
131134

132135
const addDiagnostics = (more: ts.Diagnostic|ts.Diagnostic[]|null) => {
@@ -147,7 +150,8 @@ export function getDirectiveDiagnostics(
147150
addDiagnostics(makeDuplicateDeclarationError(node, duplicateDeclarations, kind));
148151
}
149152

150-
addDiagnostics(checkInheritanceOfDirective(node, reader, reflector, evaluator));
153+
addDiagnostics(checkInheritanceOfInjectable(
154+
node, injectableRegistry, reflector, evaluator, strictInjectionParameters, kind));
151155
return diagnostics;
152156
}
153157

@@ -159,9 +163,42 @@ export function getUndecoratedClassWithAngularFeaturesDiagnostic(node: ClassDecl
159163
`Angular decorator.`);
160164
}
161165

162-
export function checkInheritanceOfDirective(
163-
node: ClassDeclaration, reader: MetadataReader, reflector: ReflectionHost,
164-
evaluator: PartialEvaluator): ts.Diagnostic|null {
166+
export function checkInheritanceOfInjectable(
167+
node: ClassDeclaration, injectableRegistry: InjectableClassRegistry, reflector: ReflectionHost,
168+
evaluator: PartialEvaluator, strictInjectionParameters: boolean,
169+
kind: 'Directive'|'Component'|'Pipe'|'Injectable'): ts.Diagnostic|null {
170+
const classWithCtor = findInheritedCtor(node, injectableRegistry, reflector, evaluator);
171+
if (classWithCtor === null || classWithCtor.isCtorValid) {
172+
// The class does not inherit a constructor, or the inherited constructor is compatible
173+
// with DI; no need to report a diagnostic.
174+
return null;
175+
}
176+
177+
if (!classWithCtor.isDecorated) {
178+
// The inherited constructor exists in a class that does not have an Angular decorator.
179+
// This is an error, as there won't be a factory definition available for DI to invoke
180+
// the constructor.
181+
return getInheritedUndecoratedCtorDiagnostic(node, classWithCtor.ref, kind);
182+
}
183+
184+
if (!strictInjectionParameters || isAbstractClassDeclaration(node)) {
185+
// An invalid constructor is only reported as error under `strictInjectionParameters` and
186+
// only for concrete classes; follow the same exclusions for derived types.
187+
return null;
188+
}
189+
190+
return getInheritedInvalidCtorDiagnostic(node, classWithCtor.ref, kind);
191+
}
192+
193+
interface ClassWithCtor {
194+
ref: Reference<ClassDeclaration>;
195+
isCtorValid: boolean;
196+
isDecorated: boolean;
197+
}
198+
199+
export function findInheritedCtor(
200+
node: ClassDeclaration, injectableRegistry: InjectableClassRegistry, reflector: ReflectionHost,
201+
evaluator: PartialEvaluator): ClassWithCtor|null {
165202
if (!reflector.isClass(node) || reflector.getConstructorParameters(node) !== null) {
166203
// We should skip nodes that aren't classes. If a constructor exists, then no base class
167204
// definition is required on the runtime side - it's legal to inherit from any class.
@@ -178,44 +215,64 @@ export function checkInheritanceOfDirective(
178215
return null;
179216
}
180217

181-
// We can skip the base class if it has metadata.
182-
const baseClassMeta = reader.getDirectiveMetadata(baseClass);
183-
if (baseClassMeta !== null) {
184-
return null;
185-
}
186-
187-
// If the base class has a blank constructor we can skip it since it can't be using DI.
188-
const baseClassConstructorParams = reflector.getConstructorParameters(baseClass.node);
189-
const newParentClass = readBaseClass(baseClass.node, reflector, evaluator);
190-
191-
if (baseClassConstructorParams !== null && baseClassConstructorParams.length > 0) {
192-
// This class has a non-trivial constructor, that's an error!
193-
return getInheritedUndecoratedCtorDiagnostic(node, baseClass, reader);
194-
} else if (baseClassConstructorParams !== null || newParentClass === null) {
195-
// This class has a trivial constructor, or no constructor + is the
196-
// top of the inheritance chain, so it's okay.
197-
return null;
218+
const injectableMeta = injectableRegistry.getInjectableMeta(baseClass.node);
219+
if (injectableMeta !== null) {
220+
if (injectableMeta.ctorDeps !== null) {
221+
// The class has an Angular decorator with a constructor.
222+
return {
223+
ref: baseClass,
224+
isCtorValid: injectableMeta.ctorDeps !== 'invalid',
225+
isDecorated: true,
226+
};
227+
}
228+
} else {
229+
const baseClassConstructorParams = reflector.getConstructorParameters(baseClass.node);
230+
if (baseClassConstructorParams !== null) {
231+
// The class is not decorated, but it does have constructor. An undecorated class is only
232+
// allowed to have a constructor without parameters, otherwise it is invalid.
233+
return {
234+
ref: baseClass,
235+
isCtorValid: baseClassConstructorParams.length === 0,
236+
isDecorated: false,
237+
};
238+
}
198239
}
199240

200241
// Go up the chain and continue
201-
baseClass = newParentClass;
242+
baseClass = readBaseClass(baseClass.node, reflector, evaluator);
202243
}
203244

204245
return null;
205246
}
206247

248+
function getInheritedInvalidCtorDiagnostic(
249+
node: ClassDeclaration, baseClass: Reference,
250+
kind: 'Directive'|'Component'|'Pipe'|'Injectable') {
251+
const baseClassName = baseClass.debugName;
252+
253+
return makeDiagnostic(
254+
ErrorCode.INJECTABLE_INHERITS_INVALID_CONSTRUCTOR, node.name,
255+
`The ${kind.toLowerCase()} ${node.name.text} inherits its constructor from ${
256+
baseClassName}, ` +
257+
`but the latter has a constructor parameter that is not compatible with dependency injection. ` +
258+
`Either add an explicit constructor to ${node.name.text} or change ${
259+
baseClassName}'s constructor to ` +
260+
`use parameters that are valid for DI.`);
261+
}
262+
207263
function getInheritedUndecoratedCtorDiagnostic(
208-
node: ClassDeclaration, baseClass: Reference, reader: MetadataReader) {
209-
const subclassMeta = reader.getDirectiveMetadata(new Reference(node))!;
210-
const dirOrComp = subclassMeta.isComponent ? 'Component' : 'Directive';
264+
node: ClassDeclaration, baseClass: Reference,
265+
kind: 'Directive'|'Component'|'Pipe'|'Injectable') {
211266
const baseClassName = baseClass.debugName;
267+
const baseNeedsDecorator =
268+
kind === 'Component' || kind === 'Directive' ? 'Directive' : 'Injectable';
212269

213270
return makeDiagnostic(
214271
ErrorCode.DIRECTIVE_INHERITS_UNDECORATED_CTOR, node.name,
215-
`The ${dirOrComp.toLowerCase()} ${node.name.text} inherits its constructor from ${
272+
`The ${kind.toLowerCase()} ${node.name.text} inherits its constructor from ${
216273
baseClassName}, ` +
217274
`but the latter does not have an Angular decorator of its own. Dependency injection will not be able to ` +
218-
`resolve the parameters of ${
219-
baseClassName}'s constructor. Either add a @Directive decorator ` +
275+
`resolve the parameters of ${baseClassName}'s constructor. Either add a @${
276+
baseNeedsDecorator} decorator ` +
220277
`to ${baseClassName}, or add an explicit constructor to ${node.name.text}.`);
221278
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import {R3DependencyMetadata} from '@angular/compiler';
9+
10+
import {hasInjectableFields} from '../../../metadata';
11+
import {ClassDeclaration, ReflectionHost} from '../../../reflection';
12+
13+
import {getConstructorDependencies, unwrapConstructorDependencies} from './di';
14+
15+
16+
export interface InjectableMeta {
17+
ctorDeps: R3DependencyMetadata[]|'invalid'|null;
18+
}
19+
20+
/**
21+
* Registry that keeps track of classes that can be constructed via dependency injection (e.g.
22+
* injectables, directives, pipes).
23+
*/
24+
export class InjectableClassRegistry {
25+
private classes = new Map<ClassDeclaration, InjectableMeta>();
26+
27+
constructor(private host: ReflectionHost, private isCore: boolean) {}
28+
29+
registerInjectable(declaration: ClassDeclaration, meta: InjectableMeta): void {
30+
this.classes.set(declaration, meta);
31+
}
32+
33+
getInjectableMeta(declaration: ClassDeclaration): InjectableMeta|null {
34+
// Figure out whether the class is injectable based on the registered classes, otherwise
35+
// fall back to looking at its members since we might not have been able to register the class
36+
// if it was compiled in another compilation unit.
37+
if (this.classes.has(declaration)) {
38+
return this.classes.get(declaration)!;
39+
}
40+
41+
if (!hasInjectableFields(declaration, this.host)) {
42+
return null;
43+
}
44+
45+
const ctorDeps = getConstructorDependencies(declaration, this.host, this.isCore);
46+
return {
47+
ctorDeps: unwrapConstructorDependencies(ctorDeps),
48+
};
49+
}
50+
}

packages/compiler-cli/src/ngtsc/annotations/common/src/util.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,8 @@ export function resolveImportedFile(
380380
// Figure out what file is being imported.
381381
return moduleResolver.resolveModule(expr.value.moduleName!, origin.fileName);
382382
}
383+
384+
export function isAbstractClassDeclaration(clazz: ClassDeclaration): boolean {
385+
return clazz.modifiers !== undefined &&
386+
clazz.modifiers.some(mod => mod.kind === ts.SyntaxKind.AbstractKeyword);
387+
}

packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {assertSuccessfulReferenceEmit, ImportedFile, ModuleResolver, Reference,
1616
import {DependencyTracker} from '../../../incremental/api';
1717
import {extractSemanticTypeParameters, SemanticDepGraphUpdater} from '../../../incremental/semantic_graph';
1818
import {IndexingContext} from '../../../indexer';
19-
import {DirectiveMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, MetaKind, PipeMeta, ResourceRegistry} from '../../../metadata';
19+
import {DirectiveMeta, extractDirectiveTypeCheckMeta, MetadataReader, MetadataRegistry, MetaKind, PipeMeta, ResourceRegistry} from '../../../metadata';
2020
import {PartialEvaluator} from '../../../partial_evaluator';
2121
import {PerfEvent, PerfRecorder} from '../../../perf';
2222
import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost, reflectObjectLiteral} from '../../../reflection';
@@ -26,7 +26,7 @@ import {TypeCheckContext} from '../../../typecheck/api';
2626
import {ExtendedTemplateChecker} from '../../../typecheck/extended/api';
2727
import {getSourceFile} from '../../../util/src/typescript';
2828
import {Xi18nContext} from '../../../xi18n';
29-
import {compileDeclareFactory, compileNgFactoryDefField, compileResults, extractClassMetadata, extractSchemas, findAngularDecorator, getDirectiveDiagnostics, getProviderDiagnostics, isExpressionForwardReference, readBaseClass, resolveEnumValue, resolveImportedFile, resolveLiteral, resolveProvidersRequiringFactory, ResourceLoader, toFactoryMetadata, wrapFunctionExpressionsInParens} from '../../common';
29+
import {compileDeclareFactory, compileNgFactoryDefField, compileResults, extractClassMetadata, extractSchemas, findAngularDecorator, getDirectiveDiagnostics, getProviderDiagnostics, InjectableClassRegistry, isExpressionForwardReference, readBaseClass, resolveEnumValue, resolveImportedFile, resolveLiteral, resolveProvidersRequiringFactory, ResourceLoader, toFactoryMetadata, wrapFunctionExpressionsInParens} from '../../common';
3030
import {extractDirectiveMetadata, parseFieldArrayValue} from '../../directive';
3131
import {NgModuleSymbol} from '../../ng_module';
3232

@@ -51,12 +51,13 @@ export class ComponentDecoratorHandler implements
5151
private scopeRegistry: LocalModuleScopeRegistry,
5252
private typeCheckScopeRegistry: TypeCheckScopeRegistry,
5353
private resourceRegistry: ResourceRegistry, private isCore: boolean,
54-
private resourceLoader: ResourceLoader, private rootDirs: ReadonlyArray<string>,
55-
private defaultPreserveWhitespaces: boolean, private i18nUseExternalIds: boolean,
56-
private enableI18nLegacyMessageIdFormat: boolean, private usePoisonedData: boolean,
57-
private i18nNormalizeLineEndingsInICUs: boolean, private moduleResolver: ModuleResolver,
58-
private cycleAnalyzer: CycleAnalyzer, private cycleHandlingStrategy: CycleHandlingStrategy,
59-
private refEmitter: ReferenceEmitter, private depTracker: DependencyTracker|null,
54+
private strictCtorDeps: boolean, private resourceLoader: ResourceLoader,
55+
private rootDirs: ReadonlyArray<string>, private defaultPreserveWhitespaces: boolean,
56+
private i18nUseExternalIds: boolean, private enableI18nLegacyMessageIdFormat: boolean,
57+
private usePoisonedData: boolean, private i18nNormalizeLineEndingsInICUs: boolean,
58+
private moduleResolver: ModuleResolver, private cycleAnalyzer: CycleAnalyzer,
59+
private cycleHandlingStrategy: CycleHandlingStrategy, private refEmitter: ReferenceEmitter,
60+
private depTracker: DependencyTracker|null,
6061
private injectableRegistry: InjectableClassRegistry,
6162
private semanticDepGraphUpdater: SemanticDepGraphUpdater|null,
6263
private annotateForClosureCompiler: boolean, private perf: PerfRecorder) {}
@@ -477,7 +478,9 @@ export class ComponentDecoratorHandler implements
477478
});
478479

479480
this.resourceRegistry.registerResources(analysis.resources, node);
480-
this.injectableRegistry.registerInjectable(node);
481+
this.injectableRegistry.registerInjectable(node, {
482+
ctorDeps: analysis.meta.deps,
483+
});
481484
}
482485

483486
index(
@@ -814,7 +817,8 @@ export class ComponentDecoratorHandler implements
814817
}
815818

816819
const directiveDiagnostics = getDirectiveDiagnostics(
817-
node, this.metaReader, this.evaluator, this.reflector, this.scopeRegistry, 'Component');
820+
node, this.injectableRegistry, this.evaluator, this.reflector, this.scopeRegistry,
821+
this.strictCtorDeps, 'Component');
818822
if (directiveDiagnostics !== null) {
819823
diagnostics.push(...directiveDiagnostics);
820824
}

0 commit comments

Comments
 (0)