Copied from old branch

1. Everything explodes! Out of stack space!
2. Results aren't used yet.
3. But call and construct use the new getSignatureFromCalls, so I expect
some baseline changes after I get the infinite recursion fixed.
This commit is contained in:
Nathan Shively-Sanders 2019-08-27 13:13:58 -07:00
parent 0f215fd233
commit d347b08a42
3 changed files with 75 additions and 14 deletions

View file

@ -524,6 +524,9 @@ namespace ts {
},
getApparentType,
getUnionType,
isTypeAssignableTo: (source, target) => {
return isTypeAssignableTo(source, target);
},
createAnonymousType,
createSignature,
createSymbol,

View file

@ -3286,6 +3286,7 @@ namespace ts {
/* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined;
/* @internal */ createPromiseType(type: Type): Type;
/* @internal */ isTypeAssignableTo(source: Type, target: Type): boolean;
/* @internal */ createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): Type;
/* @internal */ createSignature(
declaration: SignatureDeclaration,

View file

@ -401,7 +401,7 @@ namespace ts.codefix {
interface CallUsage {
argumentTypes: Type[];
returnType: Usage;
return_: Usage;
}
interface Usage {
@ -671,16 +671,17 @@ namespace ts.codefix {
function inferTypeFromCallExpression(parent: CallExpression | NewExpression, usage: Usage): void {
const call: CallUsage = {
argumentTypes: [],
returnType: {}
return_: {}
};
if (parent.arguments) {
for (const argument of parent.arguments) {
// TODO: should recursively infer a usage here, right?
call.argumentTypes.push(checker.getTypeAtLocation(argument));
}
}
calculateUsageOfNode(parent, call.returnType);
calculateUsageOfNode(parent, call.return_);
if (parent.kind === SyntaxKind.CallExpression) {
(usage.calls || (usage.calls = [])).push(call);
}
@ -830,6 +831,7 @@ namespace ts.codefix {
}
types.push(...(usage.candidateTypes || []).map(t => checker.getBaseTypeOfLiteralType(t)));
types.push(...findBuiltinType(usage));
if (usage.properties && hasCalls(usage.properties.get("then" as __String))) {
const paramType = getParameterTypeFromCalls(0, usage.properties.get("then" as __String)!.calls!, /*isRestParameter*/ false)!; // TODO: GH#18217
@ -858,15 +860,11 @@ namespace ts.codefix {
}
if (usage.calls) {
for (const call of usage.calls) {
callSignatures.push(getSignatureFromCall(call));
}
callSignatures.push(getSignatureFromCalls(usage.calls));
}
if (usage.constructs) {
for (const construct of usage.constructs) {
constructSignatures.push(getSignatureFromCall(construct));
}
constructSignatures.push(getSignatureFromCalls(usage.constructs));
}
if (usage.stringIndex) {
@ -882,6 +880,61 @@ namespace ts.codefix {
}
}
function combineUsages(usages: Usage[]): Usage {
return {
isNumber: usages.some(u => u.isNumber),
isString: usages.some(u => u.isString),
isNumberOrString: usages.some(u => u.isNumberOrString),
candidateTypes: flatMap(usages, u => u.candidateTypes) as Type[],
properties: undefined, // TODO
calls: flatMap(usages, u => u.calls) as CallUsage[],
constructs: flatMap(usages, u => u.constructs) as CallUsage[],
numberIndex: forEach(usages, u => u.numberIndex),
stringIndex: forEach(usages, u => u.stringIndex),
candidateThisTypes: flatMap(usages, u => u.candidateThisTypes) as Type[],
}
}
function findBuiltinType(usage: Usage): Type[] {
const builtins = [
checker.getStringType(),
checker.getNumberType(),
checker.createArrayType(checker.getAnyType()),
checker.createPromiseType(checker.getAnyType()),
// checker.getFunctionType() // not sure what this was supposed to be good for.
];
const matches = builtins.filter(t => matchesAllPropertiesOf(t, usage));
if (false && 0 < matches.length && matches.length < 3) {
return matches;
}
return [];
}
function matchesAllPropertiesOf(type: Type, usage: Usage) {
if (!usage.properties) return false;
let result = true;
usage.properties.forEach((prop, name) => {
const source = checker.getUnionType(inferFromUsage(prop));
const target = checker.getTypeOfPropertyOfType(type, name as string);
if (target && prop.calls) {
const sigs = checker.getSignaturesOfType(target, ts.SignatureKind.Call);
result = result && !!sigs.length && sigs.some(
sig => checker.isTypeAssignableTo(
getFunctionFromCalls(prop.calls!),
checker.createAnonymousType(undefined!, createSymbolTable(), [sig], emptyArray, undefined, undefined)));
}
else {
result = result && !!source && !!target && checker.isTypeAssignableTo(source, target);
}
});
return result;
}
function getFunctionFromCalls(calls: CallUsage[]) {
return checker.createAnonymousType(undefined!, createSymbolTable(), [getSignatureFromCalls(calls)], emptyArray, undefined, undefined);
}
function getParameterTypeFromCalls(parameterIndex: number, calls: CallUsage[], isRestParameter: boolean) {
let types: Type[] = [];
if (calls) {
@ -904,16 +957,20 @@ namespace ts.codefix {
return undefined;
}
function getSignatureFromCall(call: CallUsage): Signature {
function getSignatureFromCalls(calls: CallUsage[]): Signature {
const parameters: Symbol[] = [];
for (let i = 0; i < call.argumentTypes.length; i++) {
const length = Math.max(...calls.map(c => c.argumentTypes.length));
for (let i = 0; i < length; i++) {
const symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, escapeLeadingUnderscores(`arg${i}`));
symbol.type = checker.getWidenedType(checker.getBaseTypeOfLiteralType(call.argumentTypes[i]));
symbol.type = unifyFromUsage(calls.map(call => call.argumentTypes[i] || checker.getUndefinedType()));
if (calls.some(call => call.argumentTypes[i] === undefined)) {
symbol.flags |= SymbolFlags.Optional;
}
parameters.push(symbol);
}
const returnType = unifyFromUsage(inferFromUsage(call.returnType), checker.getVoidType());
const returnType = unifyFromUsage(inferFromUsage(combineUsages(calls.map(call => call.return_))), checker.getVoidType());
// TODO: GH#18217
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, call.argumentTypes.length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
}
function addCandidateType(usage: Usage, type: Type | undefined) {