GeezerVison is showing github.com — fix(compiler-cli): track poisoned scopes with a flag (#39967) · angular/angular@178cc51
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -97,6 +97,7 @@ export class DecorationAnalyzer { | 97 | 97 | this.scopeRegistry, this.scopeRegistry, new TemplateMapping(), this.isCore, | 98 | 98 | this.resourceManager, this.rootDirs, !!this.compilerOptions.preserveWhitespaces, | 99 | 99 | /* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat, | | 100 | + /* usePoisonedData */ false, | 100 | 101 | /* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer, | 101 | 102 | this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER, NOOP_DEPENDENCY_TRACKER, | 102 | 103 | this.injectableRegistry, !!this.compilerOptions.annotateForClosureCompiler), | |
Collapse file Copy file name to clipboardExpand all lines: packages/compiler-cli/src/ngtsc/annotations/src/component.ts +25-9Lines changed: 25 additions & 9 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -70,6 +70,8 @@ export interface ComponentAnalysisData { | 70 | 70 | * require an Angular factory definition at runtime. | 71 | 71 | */ | 72 | 72 | viewProvidersRequiringFactory: Set<Reference<ClassDeclaration>>|null; | | 73 | + | | 74 | + isPoisoned: boolean; | 73 | 75 | } | 74 | 76 |
| 75 | 77 | export type ComponentResolutionData = Pick<R3ComponentMetadata, ComponentMetadataResolvedFields>; | @@ -86,7 +88,7 @@ export class ComponentDecoratorHandler implements | 86 | 88 | private templateMapping: TemplateMapping, private isCore: boolean, | 87 | 89 | private resourceLoader: ResourceLoader, private rootDirs: ReadonlyArray<string>, | 88 | 90 | private defaultPreserveWhitespaces: boolean, private i18nUseExternalIds: boolean, | 89 | | - private enableI18nLegacyMessageIdFormat: boolean, | | 91 | + private enableI18nLegacyMessageIdFormat: boolean, private usePoisonedData: boolean, | 90 | 92 | private i18nNormalizeLineEndingsInICUs: boolean|undefined, | 91 | 93 | private moduleResolver: ModuleResolver, private cycleAnalyzer: CycleAnalyzer, | 92 | 94 | private refEmitter: ReferenceEmitter, private defaultImportRecorder: DefaultImportRecorder, | @@ -359,6 +361,7 @@ export class ComponentDecoratorHandler implements | 359 | 361 | template, | 360 | 362 | providersRequiringFactory, | 361 | 363 | viewProvidersRequiringFactory, | | 364 | + isPoisoned: diagnostics !== undefined && diagnostics.length > 0, | 362 | 365 | }, | 363 | 366 | diagnostics, | 364 | 367 | }; | @@ -383,6 +386,7 @@ export class ComponentDecoratorHandler implements | 383 | 386 | isComponent: true, | 384 | 387 | baseClass: analysis.baseClass, | 385 | 388 | ...analysis.typeCheckMeta, | | 389 | + isPoisoned: analysis.isPoisoned, | 386 | 390 | }); | 387 | 391 |
| 388 | 392 | if (!analysis.template.isInline) { | @@ -394,15 +398,19 @@ export class ComponentDecoratorHandler implements | 394 | 398 |
| 395 | 399 | index( | 396 | 400 | context: IndexingContext, node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>) { | | 401 | + if (analysis.isPoisoned && !this.usePoisonedData) { | | 402 | + return null; | | 403 | + } | 397 | 404 | const scope = this.scopeReader.getScopeForComponent(node); | 398 | 405 | const selector = analysis.meta.selector; | 399 | 406 | const matcher = new SelectorMatcher<DirectiveMeta>(); | 400 | | - if (scope === 'error') { | 401 | | - // Don't bother indexing components which had erroneous scopes. | 402 | | - return null; | 403 | | - } | 404 | | - | 405 | 407 | if (scope !== null) { | | 408 | + if ((scope.compilation.isPoisoned || scope.exported.isPoisoned) && !this.usePoisonedData) { | | 409 | + // Don't bother indexing components which had erroneous scopes, unless specifically | | 410 | + // requested. | | 411 | + return null; | | 412 | + } | | 413 | + | 406 | 414 | for (const directive of scope.compilation.directives) { | 407 | 415 | if (directive.selector !== null) { | 408 | 416 | matcher.addSelectables(CssSelector.parse(directive.selector), directive); | @@ -429,9 +437,13 @@ export class ComponentDecoratorHandler implements | 429 | 437 | return; | 430 | 438 | } | 431 | 439 |
| | 440 | + if (meta.isPoisoned && !this.usePoisonedData) { | | 441 | + return; | | 442 | + } | | 443 | + | 432 | 444 | const scope = this.typeCheckScopes.getTypeCheckScope(node); | 433 | | - if (scope === 'error') { | 434 | | - // Don't type-check components that had errors in their scopes. | | 445 | + if (scope.isPoisoned && !this.usePoisonedData) { | | 446 | + // Don't type-check components that had errors in their scopes, unless requested. | 435 | 447 | return; | 436 | 448 | } | 437 | 449 |
| @@ -443,6 +455,10 @@ export class ComponentDecoratorHandler implements | 443 | 455 |
| 444 | 456 | resolve(node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>): | 445 | 457 | ResolveResult<ComponentResolutionData> { | | 458 | + if (analysis.isPoisoned && !this.usePoisonedData) { | | 459 | + return {}; | | 460 | + } | | 461 | + | 446 | 462 | const context = node.getSourceFile(); | 447 | 463 | // Check whether this component was registered with an NgModule. If so, it should be compiled | 448 | 464 | // under that module's compilation scope. | @@ -455,7 +471,7 @@ export class ComponentDecoratorHandler implements | 455 | 471 | wrapDirectivesAndPipesInClosure: false, | 456 | 472 | }; | 457 | 473 |
| 458 | | - if (scope !== null && scope !== 'error') { | | 474 | + if (scope !== null && (!scope.compilation.isPoisoned || this.usePoisonedData)) { | 459 | 475 | // Replace the empty components and directives from the analyze() step with a fully expanded | 460 | 476 | // scope. This is possible now because during resolve() the whole compilation unit has been | 461 | 477 | // fully analyzed. | |
Collapse file Copy file name to clipboardExpand all lines: packages/compiler-cli/src/ngtsc/annotations/src/directive.ts +4-1Lines changed: 4 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -41,6 +41,7 @@ export interface DirectiveHandlerData { | 41 | 41 | providersRequiringFactory: Set<Reference<ClassDeclaration>>|null; | 42 | 42 | inputs: ClassPropertyMapping; | 43 | 43 | outputs: ClassPropertyMapping; | | 44 | + isPoisoned: boolean; | 44 | 45 | } | 45 | 46 |
| 46 | 47 | export class DirectiveDecoratorHandler implements | @@ -106,7 +107,8 @@ export class DirectiveDecoratorHandler implements | 106 | 107 | this.annotateForClosureCompiler), | 107 | 108 | baseClass: readBaseClass(node, this.reflector, this.evaluator), | 108 | 109 | typeCheckMeta: extractDirectiveTypeCheckMeta(node, directiveResult.inputs, this.reflector), | 109 | | - providersRequiringFactory | | 110 | + providersRequiringFactory, | | 111 | + isPoisoned: false, | 110 | 112 | } | 111 | 113 | }; | 112 | 114 | } | @@ -126,6 +128,7 @@ export class DirectiveDecoratorHandler implements | 126 | 128 | isComponent: false, | 127 | 129 | baseClass: analysis.baseClass, | 128 | 130 | ...analysis.typeCheckMeta, | | 131 | + isPoisoned: analysis.isPoisoned, | 129 | 132 | }); | 130 | 133 |
| 131 | 134 | this.injectableRegistry.registerInjectable(node); | |
Collapse file Copy file name to clipboardExpand all lines: packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +3-2Lines changed: 3 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -336,7 +336,7 @@ export class NgModuleDecoratorHandler implements | 336 | 336 | injectorImports: [], | 337 | 337 | }; | 338 | 338 |
| 339 | | - if (scope !== null && scope !== 'error') { | | 339 | + if (scope !== null && !scope.compilation.isPoisoned) { | 340 | 340 | // Using the scope information, extend the injector's imports using the modules that are | 341 | 341 | // specified as module exports. | 342 | 342 | const context = getSourceFile(node); | @@ -361,7 +361,8 @@ export class NgModuleDecoratorHandler implements | 361 | 361 | return {diagnostics}; | 362 | 362 | } | 363 | 363 |
| 364 | | - if (scope === null || scope === 'error' || scope.reexports === null) { | | 364 | + if (scope === null || scope.compilation.isPoisoned || scope.exported.isPoisoned || | | 365 | + scope.reexports === null) { | 365 | 366 | return {data}; | 366 | 367 | } else { | 367 | 368 | return { | |
Collapse file Copy file name to clipboardExpand all lines: packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts +19-5Lines changed: 19 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -33,6 +33,12 @@ export interface TypeCheckScope { | 33 | 33 | * The schemas that are used in this scope. | 34 | 34 | */ | 35 | 35 | schemas: SchemaMetadata[]; | | 36 | + | | 37 | + /** | | 38 | + * Whether the original compilation scope which produced this `TypeCheckScope` was itself poisoned | | 39 | + * (contained semantic errors during its production). | | 40 | + */ | | 41 | + isPoisoned: boolean; | 36 | 42 | } | 37 | 43 |
| 38 | 44 | /** | @@ -57,15 +63,18 @@ export class TypeCheckScopes { | 57 | 63 | * contains an error, then 'error' is returned. If the component is not declared in any NgModule, | 58 | 64 | * an empty type-check scope is returned. | 59 | 65 | */ | 60 | | - getTypeCheckScope(node: ClassDeclaration): TypeCheckScope|'error' { | | 66 | + getTypeCheckScope(node: ClassDeclaration): TypeCheckScope { | 61 | 67 | const matcher = new SelectorMatcher<DirectiveMeta>(); | 62 | 68 | const pipes = new Map<string, Reference<ClassDeclaration<ts.ClassDeclaration>>>(); | 63 | 69 |
| 64 | 70 | const scope = this.scopeReader.getScopeForComponent(node); | 65 | 71 | if (scope === null) { | 66 | | - return {matcher, pipes, schemas: []}; | 67 | | - } else if (scope === 'error') { | 68 | | - return scope; | | 72 | + return { | | 73 | + matcher, | | 74 | + pipes, | | 75 | + schemas: [], | | 76 | + isPoisoned: false, | | 77 | + }; | 69 | 78 | } | 70 | 79 |
| 71 | 80 | if (this.scopeCache.has(scope.ngModule)) { | @@ -87,7 +96,12 @@ export class TypeCheckScopes { | 87 | 96 | pipes.set(name, ref as Reference<ClassDeclaration<ts.ClassDeclaration>>); | 88 | 97 | } | 89 | 98 |
| 90 | | - const typeCheckScope: TypeCheckScope = {matcher, pipes, schemas: scope.schemas}; | | 99 | + const typeCheckScope: TypeCheckScope = { | | 100 | + matcher, | | 101 | + pipes, | | 102 | + schemas: scope.schemas, | | 103 | + isPoisoned: scope.compilation.isPoisoned || scope.exported.isPoisoned, | | 104 | + }; | 91 | 105 | this.scopeCache.set(scope.ngModule, typeCheckScope); | 92 | 106 | return typeCheckScope; | 93 | 107 | } | |
Collapse file Copy file name to clipboardExpand all lines: packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -73,6 +73,7 @@ runInEachFileSystem(() => { | 73 | 73 | /* isCore */ false, new NoopResourceLoader(), /* rootDirs */[''], | 74 | 74 | /* defaultPreserveWhitespaces */ false, /* i18nUseExternalIds */ true, | 75 | 75 | /* enableI18nLegacyMessageIdFormat */ false, | | 76 | + /* usePoisonedData */ false, | 76 | 77 | /* i18nNormalizeLineEndingsInICUs */ undefined, moduleResolver, cycleAnalyzer, refEmitter, | 77 | 78 | NOOP_DEFAULT_IMPORT_RECORDER, /* depTracker */ null, injectableRegistry, | 78 | 79 | /* annotateForClosureCompiler */ false); | |
Collapse file Copy file name to clipboardExpand all lines: packages/compiler-cli/src/ngtsc/core/src/compiler.ts +2-1Lines changed: 2 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -106,6 +106,7 @@ export class NgCompiler { | 106 | 106 | private typeCheckingProgramStrategy: TypeCheckingProgramStrategy, | 107 | 107 | private incrementalStrategy: IncrementalBuildStrategy, | 108 | 108 | private enableTemplateTypeChecker: boolean, | | 109 | + private usePoisonedData: boolean, | 109 | 110 | oldProgram: ts.Program|null = null, | 110 | 111 | private perfRecorder: PerfRecorder = NOOP_PERF_RECORDER, | 111 | 112 | ) { | @@ -742,7 +743,7 @@ export class NgCompiler { | 742 | 743 | reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, | 743 | 744 | templateMapping, isCore, this.resourceManager, this.adapter.rootDirs, | 744 | 745 | this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, | 745 | | - this.options.enableI18nLegacyMessageIdFormat !== false, | | 746 | + this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, | 746 | 747 | this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, | 747 | 748 | refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, injectableRegistry, | 748 | 749 | this.closureCompilerEnabled), | |
Collapse file Copy file name to clipboardExpand all lines: packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts +6-3Lines changed: 6 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -51,7 +51,8 @@ runInEachFileSystem(() => { | 51 | 51 | const program = ts.createProgram({host, options, rootNames: host.inputFiles}); | 52 | 52 | const compiler = new NgCompiler( | 53 | 53 | host, options, program, new ReusedProgramStrategy(program, host, options, []), | 54 | | - new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); | | 54 | + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, | | 55 | + /* usePoisonedData */ false); | 55 | 56 |
| 56 | 57 | const diags = compiler.getDiagnostics(getSourceFileOrError(program, COMPONENT)); | 57 | 58 | expect(diags.length).toBe(1); | @@ -100,7 +101,8 @@ runInEachFileSystem(() => { | 100 | 101 | const CmpC = getClass(getSourceFileOrError(program, cmpCFile), 'CmpC'); | 101 | 102 | const compiler = new NgCompiler( | 102 | 103 | host, options, program, new ReusedProgramStrategy(program, host, options, []), | 103 | | - new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); | | 104 | + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, | | 105 | + /* usePoisonedData */ false); | 104 | 106 | const components = compiler.getComponentsWithTemplateFile(templateFile); | 105 | 107 | expect(components).toEqual(new Set([CmpA, CmpC])); | 106 | 108 | }); | @@ -129,7 +131,8 @@ runInEachFileSystem(() => { | 129 | 131 | const program = ts.createProgram({host, options, rootNames: host.inputFiles}); | 130 | 132 | const compiler = new NgCompiler( | 131 | 133 | host, options, program, new ReusedProgramStrategy(program, host, options, []), | 132 | | - new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); | | 134 | + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, | | 135 | + /* usePoisonedData */ false); | 133 | 136 |
| 134 | 137 | const deps = compiler.getResourceDependencies(getSourceFileOrError(program, COMPONENT)); | 135 | 138 | expect(deps.length).toBe(2); | |
Collapse file Copy file name to clipboardExpand all lines: packages/compiler-cli/src/ngtsc/metadata/src/api.ts +6Lines changed: 6 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -108,6 +108,12 @@ export interface DirectiveMeta extends T2DirectiveMeta, DirectiveTypeCheckMeta { | 108 | 108 | * another type, it could not statically determine the base class. | 109 | 109 | */ | 110 | 110 | baseClass: Reference<ClassDeclaration>|'dynamic'|null; | | 111 | + | | 112 | + /** | | 113 | + * Whether the directive had some issue with its declaration that means it might not have complete | | 114 | + * and reliable metadata. | | 115 | + */ | | 116 | + isPoisoned: boolean; | 111 | 117 | } | 112 | 118 |
| 113 | 119 | /** | |
Collapse file Copy file name to clipboardExpand all lines: packages/compiler-cli/src/ngtsc/metadata/src/dts.ts +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -92,6 +92,7 @@ export class DtsMetadataReader implements MetadataReader { | 92 | 92 | queries: readStringArrayType(def.type.typeArguments[5]), | 93 | 93 | ...extractDirectiveTypeCheckMeta(clazz, inputs, this.reflector), | 94 | 94 | baseClass: readBaseClass(clazz, this.checker, this.reflector), | | 95 | + isPoisoned: false, | 95 | 96 | }; | 96 | 97 | } | 97 | 98 |
| |
|