Skip to content

Commit 46c9ead

Browse files
Refactor to extract existing inference logic
1 parent 40850fe commit 46c9ead

File tree

1 file changed

+128
-93
lines changed

1 file changed

+128
-93
lines changed

src/resolver.ts

Lines changed: 128 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -731,110 +731,145 @@ export class Resolver extends DiagnosticEmitter {
731731

732732
// infer generic call if type arguments have been omitted
733733
if (prototype.is(CommonFlags.Generic)) {
734-
let contextualTypeArguments = cloneMap(ctxFlow.contextualTypeArguments);
734+
let resolvedTypeArguments = this.inferGenericTypesArguments(
735+
node,
736+
prototype,
737+
prototype.typeParameterNodes,
738+
ctxFlow,
739+
reportMode,
740+
);
741+
742+
return this.resolveFunction(
743+
prototype,
744+
resolvedTypeArguments,
745+
cloneMap(ctxFlow.contextualTypeArguments),
746+
reportMode
747+
);
748+
}
735749

736-
// fill up contextual types with auto for each generic component
737-
let typeParameterNodes = assert(prototype.typeParameterNodes);
738-
let numTypeParameters = typeParameterNodes.length;
739-
let typeParameterNames = new Set<string>();
740-
for (let i = 0; i < numTypeParameters; ++i) {
741-
let name = typeParameterNodes[i].name.text;
742-
contextualTypeArguments.set(name, Type.auto);
743-
typeParameterNames.add(name);
744-
}
745-
746-
let parameterNodes = prototype.functionTypeNode.parameters;
747-
let numParameters = parameterNodes.length;
748-
let argumentNodes = node.args;
749-
let numArguments = argumentNodes.length;
750-
751-
// infer types with generic components while updating contextual types
752-
for (let i = 0; i < numParameters; ++i) {
753-
let argumentExpression = i < numArguments
754-
? argumentNodes[i]
755-
: parameterNodes[i].initializer;
756-
if (!argumentExpression) {
757-
// optional but not have initializer should be handled in the other place
758-
if (parameterNodes[i].parameterKind == ParameterKind.Optional) {
759-
continue;
760-
}
761-
// missing initializer -> too few arguments
762-
if (reportMode == ReportMode.Report) {
763-
this.error(
764-
DiagnosticCode.Expected_0_arguments_but_got_1,
765-
node.range, numParameters.toString(), numArguments.toString()
766-
);
767-
}
768-
return null;
750+
// otherwise resolve the non-generic call as usual
751+
return this.resolveFunction(prototype, null, new Map(), reportMode);
752+
}
753+
754+
inferGenericTypesArguments(
755+
node: CallExpression | NewExpression,
756+
prototype: FunctionPrototype,
757+
typeParameterNodes: TypeParameterNode[] | null,
758+
ctxFlow: Flow,
759+
reportMode: ReportMode = ReportMode.Report,
760+
): Type[] | null {
761+
762+
if (!typeParameterNodes) {
763+
return null;
764+
}
765+
766+
let contextualTypeArguments = cloneMap(ctxFlow.contextualTypeArguments);
767+
768+
// fill up contextual types with auto for each generic component
769+
let numTypeParameters = typeParameterNodes.length;
770+
let typeParameterNames = new Set<string>();
771+
for (let i = 0; i < numTypeParameters; ++i) {
772+
let name = typeParameterNodes[i].name.text;
773+
contextualTypeArguments.set(name, Type.auto);
774+
typeParameterNames.add(name);
775+
}
776+
777+
let parameterNodes = prototype.functionTypeNode.parameters;
778+
let numParameters = parameterNodes.length;
779+
let argumentNodes = node.args;
780+
let numArguments = argumentNodes.length;
781+
782+
// infer types with generic components while updating contextual types
783+
for (let i = 0; i < numParameters; ++i) {
784+
let argumentExpression = i < numArguments
785+
? argumentNodes[i]
786+
: parameterNodes[i].initializer;
787+
if (!argumentExpression) {
788+
// optional but not have initializer should be handled in the other place
789+
if (parameterNodes[i].parameterKind == ParameterKind.Optional) {
790+
continue;
791+
}
792+
// missing initializer -> too few arguments
793+
if (reportMode == ReportMode.Report) {
794+
this.error(
795+
DiagnosticCode.Expected_0_arguments_but_got_1,
796+
node.range, numParameters.toString(), numArguments.toString()
797+
);
769798
}
770-
let typeNode = parameterNodes[i].type;
771-
if (typeNode.hasGenericComponent(typeParameterNodes)) {
772-
let type = this.resolveExpression(argumentExpression, ctxFlow, Type.auto, ReportMode.Swallow);
773-
if (type) {
774-
this.propagateInferredGenericTypes(
775-
typeNode,
776-
type,
777-
prototype,
778-
contextualTypeArguments,
779-
typeParameterNames
780-
);
781-
}
799+
return null;
800+
}
801+
let typeNode = parameterNodes[i].type;
802+
if (typeNode.hasGenericComponent(typeParameterNodes)) {
803+
let type = this.resolveExpression(argumentExpression, ctxFlow, Type.auto, ReportMode.Swallow);
804+
if (type) {
805+
this.propagateInferredGenericTypes(
806+
typeNode,
807+
type,
808+
prototype,
809+
contextualTypeArguments,
810+
typeParameterNames
811+
);
782812
}
783813
}
814+
}
784815

785-
// apply concrete types to the generic function signature
786-
let resolvedTypeArguments = new Array<Type>(numTypeParameters);
787-
for (let i = 0; i < numTypeParameters; ++i) {
788-
let typeParameterNode = typeParameterNodes[i];
789-
let name = typeParameterNode.name.text;
790-
if (contextualTypeArguments.has(name)) {
791-
let inferredType = assert(contextualTypeArguments.get(name));
792-
if (inferredType != Type.auto) {
793-
resolvedTypeArguments[i] = inferredType;
794-
continue;
816+
// apply concrete types to the generic function signature
817+
let resolvedTypeArguments = new Array<Type>(numTypeParameters);
818+
for (let i = 0; i < numTypeParameters; ++i) {
819+
let typeParameterNode = typeParameterNodes[i];
820+
let name = typeParameterNode.name.text;
821+
if (contextualTypeArguments.has(name)) {
822+
let inferredType = assert(contextualTypeArguments.get(name));
823+
if (inferredType != Type.auto) {
824+
resolvedTypeArguments[i] = inferredType;
825+
continue;
826+
}
827+
let defaultType = typeParameterNode.defaultType;
828+
if (defaultType) {
829+
// Default parameters are resolved in context of the called function, not the calling function
830+
let parent = prototype.parent;
831+
let defaultTypeContextualTypeArguments: Map<string, Type> | null = null;
832+
if (parent.kind == ElementKind.Class) {
833+
defaultTypeContextualTypeArguments = (<Class>parent).contextualTypeArguments;
834+
} else if (parent.kind == ElementKind.Function) {
835+
defaultTypeContextualTypeArguments = (<Function>parent).contextualTypeArguments;
795836
}
796-
let defaultType = typeParameterNode.defaultType;
797-
if (defaultType) {
798-
// Default parameters are resolved in context of the called function, not the calling function
799-
let parent = prototype.parent;
800-
let defaultTypeContextualTypeArguments: Map<string, Type> | null = null;
801-
if (parent.kind == ElementKind.Class) {
802-
defaultTypeContextualTypeArguments = (<Class>parent).contextualTypeArguments;
803-
} else if (parent.kind == ElementKind.Function) {
804-
defaultTypeContextualTypeArguments = (<Function>parent).contextualTypeArguments;
805-
}
806-
let resolvedDefaultType = this.resolveType(
807-
defaultType,
808-
null,
809-
prototype,
810-
defaultTypeContextualTypeArguments,
811-
reportMode
812-
);
813-
if (!resolvedDefaultType) return null;
814-
resolvedTypeArguments[i] = resolvedDefaultType;
815-
continue;
816-
}
817-
}
818-
// unused template, e.g. `function test<T>(): void {...}` called as `test()`
819-
// invalid because the type is effectively unknown inside the function body
820-
if (reportMode == ReportMode.Report) {
821-
this.error(
822-
DiagnosticCode.Type_argument_expected,
823-
node.expression.range.atEnd
837+
let resolvedDefaultType = this.resolveType(
838+
defaultType,
839+
null,
840+
prototype,
841+
defaultTypeContextualTypeArguments,
842+
reportMode
824843
);
844+
if (!resolvedDefaultType) return null;
845+
resolvedTypeArguments[i] = resolvedDefaultType;
846+
continue;
825847
}
826-
return null;
827848
}
828-
return this.resolveFunction(
829-
prototype,
830-
resolvedTypeArguments,
831-
cloneMap(ctxFlow.contextualTypeArguments),
832-
reportMode
833-
);
849+
// unused template, e.g. `function test<T>(): void {...}` called as `test()`
850+
// invalid because the type is effectively unknown inside the function body
851+
if (reportMode == ReportMode.Report) {
852+
let range: Range;
853+
switch (node.kind) {
854+
case NodeKind.Call:
855+
range = (<CallExpression>node).expression.range;
856+
break;
857+
case NodeKind.New:
858+
range = (<NewExpression>node).typeName.range;
859+
break;
860+
default:
861+
assert(false);
862+
return null;
863+
}
864+
this.error(
865+
DiagnosticCode.Type_argument_expected,
866+
range.atEnd
867+
);
868+
}
869+
return null;
834870
}
835871

836-
// otherwise resolve the non-generic call as usual
837-
return this.resolveFunction(prototype, null, new Map(), reportMode);
872+
return resolvedTypeArguments;
838873
}
839874

840875
/** Updates contextual types with a possibly encapsulated inferred type. */

0 commit comments

Comments
 (0)