refactor(compiler-cli): introduce ResolvedModuleWithProviders (#45701)

This commit updates the `ForeignFunctionResolver` used by the NgModule
handler to resolve `ModuleWithProvider` types. Previously, this resolver
returned the NgModule `Reference` directly, but there are two problems with
this:

* It's not completely accurate, as the expression returned by the MWP call
  will not return the NgModule at runtime.
* We need the ability to distinguish the MWP call itself from an ordinary
  NgModule reference in future optimizations.

PR Close #45701
This commit is contained in:
Alex Rickabaugh 2022-04-20 11:03:34 -07:00 committed by Andrew Scott
parent c614f31fc1
commit 18db4ef7ff

View file

@ -13,7 +13,7 @@ import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation}
import {assertSuccessfulReferenceEmit, Reference, ReferenceEmitter} from '../../../imports';
import {isArrayEqual, isReferenceEqual, isSymbolEqual, SemanticReference, SemanticSymbol} from '../../../incremental/semantic_graph';
import {InjectableClassRegistry, MetadataReader, MetadataRegistry, MetaKind} from '../../../metadata';
import {DynamicValue, PartialEvaluator, ResolvedValue} from '../../../partial_evaluator';
import {DynamicValue, PartialEvaluator, ResolvedValue, SyntheticValue} from '../../../partial_evaluator';
import {PerfEvent, PerfRecorder} from '../../../perf';
import {ClassDeclaration, Decorator, isNamedClassDeclaration, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../../reflection';
import {LocalModuleScopeRegistry, ScopeData} from '../../../scope';
@ -606,7 +606,7 @@ export class NgModuleDecoratorHandler implements
private _extractModuleFromModuleWithProvidersFn(
fn: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>,
node: ts.CallExpression, resolve: (expr: ts.Expression) => ResolvedValue,
unresolvable: DynamicValue): Reference<ClassDeclaration>|DynamicValue {
unresolvable: DynamicValue): SyntheticValue<ResolvedModuleWithProviders>|DynamicValue {
const rawType = fn.node.type || null;
const type = rawType &&
@ -620,7 +620,10 @@ export class NgModuleDecoratorHandler implements
return unresolvable;
}
return ngModule as Reference<ClassDeclaration>;
return new SyntheticValue({
ngModule: ngModule as Reference<ClassDeclaration>,
mwpCall: node,
});
}
/**
@ -720,10 +723,13 @@ export class NgModuleDecoratorHandler implements
`Expected array when reading the NgModule.${arrayName} of ${className}`);
}
resolvedList.forEach((entry, idx) => {
for (let idx = 0; idx < resolvedList.length; idx++) {
let entry = resolvedList[idx];
// Unwrap ModuleWithProviders for modules that are locally declared (and thus static
// resolution was able to descend into the function and return an object literal, a Map).
if (entry instanceof Map && entry.has('ngModule')) {
if (entry instanceof SyntheticValue && isResolvedModuleWithProviders(entry)) {
entry = entry.value.ngModule;
} else if (entry instanceof Map && entry.has('ngModule')) {
entry = entry.get('ngModule')!;
}
@ -745,7 +751,7 @@ export class NgModuleDecoratorHandler implements
`Value at position ${idx} in the NgModule.${arrayName} of ${
className} is not a reference`);
}
});
}
return refList;
}
@ -762,3 +768,15 @@ function isModuleIdExpression(expr: ts.Expression): boolean {
return ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) &&
expr.expression.text === 'module' && expr.name.text === 'id';
}
interface ResolvedModuleWithProviders {
ngModule: Reference<ClassDeclaration>;
mwpCall: ts.CallExpression;
}
function isResolvedModuleWithProviders(sv: SyntheticValue<unknown>):
sv is SyntheticValue<ResolvedModuleWithProviders> {
return typeof sv.value === 'object' && sv.value != null &&
sv.value.hasOwnProperty('ngModule' as keyof ResolvedModuleWithProviders) &&
sv.value.hasOwnProperty('mwpCall' as keyof ResolvedModuleWithProviders);
}