Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
a4ce5e6
Infer from usage quick fix
mhegazy Feb 26, 2017
d79fbc0
Change full function singature
mhegazy Feb 28, 2017
69a4b8c
Add property/element access support
mhegazy Feb 28, 2017
33467cf
Fix a few issues
mhegazy Mar 2, 2017
ab39813
Merge branch 'master' into inferFromUsage
mhegazy Mar 16, 2017
d87eae9
Some cleanup
mhegazy Mar 16, 2017
a18a950
Expose getArrayType and getPromiseType
mhegazy Mar 16, 2017
bea4591
Switch to collecting all usage before infering
mhegazy Mar 16, 2017
55ffe2d
Infer array and promise type arguments
mhegazy Mar 16, 2017
b82dc8d
Handel enums in binary operators
mhegazy Mar 16, 2017
7a52545
consolidate usage of addCandidateTypes
mhegazy Mar 16, 2017
8088415
Handel rest paramters
mhegazy Mar 18, 2017
0413165
Properly handel `+=` and `+` inference for numbers and strings
mhegazy Mar 18, 2017
ea483a7
Add print quickfixes debug helper
mhegazy Mar 18, 2017
ea344b2
Add rest param tests
mhegazy Mar 18, 2017
a1efc1a
Add optional paramter tests
mhegazy Mar 18, 2017
ff15dc6
Handel set accessors
mhegazy Mar 18, 2017
e2e43d1
Support getters
mhegazy Mar 20, 2017
2929af4
Support no implicit any error for variable at use site
mhegazy Mar 21, 2017
b395057
Support properties
mhegazy Mar 21, 2017
15eb7de
Only offer quick fix if an infered type other than any is available
mhegazy Mar 21, 2017
2a9032b
Rename functions
mhegazy Mar 21, 2017
057016b
Move to a separate namespace
mhegazy Mar 21, 2017
32aa07d
Check cancellation token
mhegazy Mar 21, 2017
5da31bb
Cleanup
mhegazy Mar 21, 2017
dc431e4
Check for accesibile symbols where serializing types
mhegazy Mar 22, 2017
561cfe6
Remove JS support
mhegazy Mar 22, 2017
002e8ad
Reorganize functions
mhegazy Mar 22, 2017
3822d8d
Mark APIs as internal
mhegazy Mar 22, 2017
41ed553
Fix lint errors
mhegazy Mar 22, 2017
c3071a8
Merge remote-tracking branch 'origin/master' into inferFromUsage
mhegazy Mar 22, 2017
a12dc45
Merge branch 'master' into inferFromUsage
mhegazy May 8, 2017
791bb4a
Merge branch 'master' into inferFromUsage
DanielRosenwasser Aug 1, 2017
0047ae3
Removed conflict markers.
DanielRosenwasser Aug 1, 2017
5a15e93
Update 'createSymbol' to use '__String'.
DanielRosenwasser Sep 19, 2017
9daaffa
Fixed most problems relating to '__String' and 'includeJsDocComments'…
DanielRosenwasser Sep 19, 2017
b5e94a7
Addressed most API changes.
DanielRosenwasser Sep 19, 2017
3b49776
Merge remote-tracking branch 'origin/master' into inferFromUsage
DanielRosenwasser Sep 19, 2017
3f488ce
Merge branch 'master' into inferFromUsage
mhegazy Oct 5, 2017
6bdaf8a
Make all helpers internal
mhegazy Oct 6, 2017
25f8aa6
Use a diffrent writer and not the built-in single line write
mhegazy Oct 6, 2017
761bd7b
Infer types for all parameters in a parameter list instead of one at …
mhegazy Oct 7, 2017
ffa8272
Accept baselines
mhegazy Oct 7, 2017
5abd72d
Code review commments
mhegazy Oct 7, 2017
1e39d48
Respond to code review comments
mhegazy Oct 12, 2017
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Handel rest paramters
  • Loading branch information
mhegazy committed Mar 18, 2017
commit 80884152bcfd43c8009ada930ee51d28a10a7829
63 changes: 39 additions & 24 deletions src/services/codefixes/inferFromUsage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace ts.codefix {
function getActionsForAddExplicitTypeAnnotation({ sourceFile, program, span: { start }, errorCode, cancellationToken, newLineCharacter}: CodeFixContext): CodeAction[] | undefined {
const token = getTokenAtPosition(sourceFile, start);

if (!isIdentifier(token)) {
if (!isIdentifier(token) && token.kind !== SyntaxKind.DotDotDotToken) {
return undefined;
}

Expand All @@ -42,8 +42,9 @@ namespace ts.codefix {
case Diagnostics.Member_0_implicitly_has_an_1_type.code:
return getCodeActionForVariable();
case Diagnostics.Parameter_0_implicitly_has_an_1_type.code:
return getCodeActionForParameter(/*isRestParam*/ false);
case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code:
return getCodeActionForParameter();
return getCodeActionForParameter(/*isRestParam*/ true);
}
}

Expand All @@ -54,29 +55,34 @@ namespace ts.codefix {
const declaration = getAncestor(token, SyntaxKind.VariableStatement);
if (!declaration.jsDoc) {
const newText = `/** @type {${typeString}} */${newLineCharacter}`;
return createCodeActions(declaration.getStart(), newText);
return createCodeActions(token.getText(), declaration.getStart(), newText);
}
}
else {
return createCodeActions(token.getEnd(), `: ${typeString}`);
return createCodeActions(token.getText(), token.getEnd(), `: ${typeString}`);
}
}

function getCodeActionForParameter() {
let type = inferTypeForParameterFromUsage(<Identifier>token);
function getCodeActionForParameter(isRestParameter: boolean) {
const parameterDeclaration = <ParameterDeclaration>getAncestor(token, SyntaxKind.Parameter);
if (!isIdentifier(parameterDeclaration.name)) {
return undefined;
}

let type = inferTypeForParameterFromUsage(parameterDeclaration.name, isRestParameter);
if (!isValidInference(type)) {
type = inferTypeForVariableFromUsage(<Identifier>token);
type = inferTypeForVariableFromUsage(parameterDeclaration.name);
}
const typeString = checker.typeToString(type || checker.getAnyType(), token, TypeFormatFlags.NoTruncation);
if (isInJavaScriptFile(sourceFile)) {
const declaration = getContainingFunction(token);
if (!declaration.jsDoc) {
const newText = `/** @param {${typeString}} ${token.getText()} */${newLineCharacter}`;
return createCodeActions(declaration.getStart(), newText);
return createCodeActions(parameterDeclaration.name.getText(), declaration.getStart(), newText);
}
}
else {
return createCodeActions(token.getEnd(), `: ${typeString}`);
return createCodeActions(parameterDeclaration.name.getText(), parameterDeclaration.getEnd(), `: ${typeString}`);
}
}

Expand Down Expand Up @@ -146,9 +152,9 @@ namespace ts.codefix {
// }
//}

function createCodeActions(start: number, typeString: string) {
function createCodeActions(name: string, start: number, typeString: string) {
return [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Infer_type_of_0), [token.getText()]),
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Infer_type_of_0), [name]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
Expand Down Expand Up @@ -185,7 +191,7 @@ namespace ts.codefix {
}
}

function inferTypeForParameterFromUsage(token: Identifier) {
function inferTypeForParameterFromUsage(token: Identifier, isRestParameter: boolean) {
const containingFunction = getContainingFunction(token);

if (containingFunction.kind === SyntaxKind.SetAccessor) {
Expand All @@ -202,7 +208,7 @@ namespace ts.codefix {
if (searchToken) {
const parameterIndex = getParameterIndexInList(token, containingFunction.parameters);
const references = getReferences(searchToken).map(r => <Identifier>getTokenAtPosition(program.getSourceFile(r.fileName), r.textSpan.start));
return inferTypeForParameterFromReferences(references, parameterIndex, isConstructor, checker);
return inferTypeForParameterFromReferences(references, parameterIndex, isConstructor, isRestParameter, checker);
}
}
else {
Expand Down Expand Up @@ -236,12 +242,12 @@ namespace ts.codefix {
return getTypeFromUsageContext(usageContext, checker);
}

function inferTypeForParameterFromReferences(references: Identifier[], parameterIndex: number, isConstructor: boolean, checker: TypeChecker) {
function inferTypeForParameterFromReferences(references: Identifier[], parameterIndex: number, isConstructor: boolean, isRestParameter: boolean, checker: TypeChecker) {
const usageContext: UsageContext = {};
for (const reference of references) {
getTypeFromContext(reference, checker, usageContext);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explicit return undefined

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

return getParameterTypeFromCallContexts(parameterIndex, isConstructor ? usageContext.constructContexts : usageContext.callContexts, checker);
return getParameterTypeFromCallContexts(parameterIndex, isConstructor ? usageContext.constructContexts : usageContext.callContexts, isRestParameter, checker);
}

function getTypeFromUsageContext(usageContext: UsageContext, checker: TypeChecker): Type {
Expand All @@ -258,12 +264,12 @@ namespace ts.codefix {
return checker.getWidenedType(checker.getUnionType(map(usageContext.candidateTypes, t => checker.getBaseTypeOfLiteralType(t)), true));
}
else if (usageContext.properties && hasCallContext(usageContext.properties.get("then"))) {
const paramType = getParameterTypeFromCallContexts(0, usageContext.properties.get("then").callContexts, checker);
const paramType = getParameterTypeFromCallContexts(0, usageContext.properties.get("then").callContexts, /*isRestParameter*/ false, checker);
const types = paramType.getCallSignatures().map(c => c.getReturnType());
return checker.createPromiseType(types.length ? checker.getUnionType(types, true) : checker.getAnyType());
}
else if (usageContext.properties && hasCallContext(usageContext.properties.get("push"))) {
return checker.createArrayType(getParameterTypeFromCallContexts(0, usageContext.properties.get("push").callContexts, checker));
return checker.createArrayType(getParameterTypeFromCallContexts(0, usageContext.properties.get("push").callContexts, /*isRestParameter*/ false, checker));
}
else if (usageContext.properties || usageContext.callContexts || usageContext.constructContexts || usageContext.numberIndexContext || usageContext.stringIndexContext) {
const members = createMap<Symbol>();
Expand All @@ -283,12 +289,14 @@ namespace ts.codefix {
if (usageContext.callContexts) {
for (const callConext of usageContext.callContexts) {
callSignatures.push(getSignatureFromCallContext(callConext, checker));
} }
}
}

if (usageContext.constructContexts) {
for (const constructContext of usageContext.constructContexts) {
constructSignatures.push(getSignatureFromCallContext(constructContext, checker));
} }
}
}

if (usageContext.numberIndexContext) {
numberIndexInfo = checker.createIndexInfo(getTypeFromUsageContext(usageContext.numberIndexContext, checker), /*isReadonly*/ false);
Expand All @@ -305,24 +313,31 @@ namespace ts.codefix {
}
}

function getParameterTypeFromCallContexts(parameterIndex: number, callContexts: CallContext[], checker: TypeChecker) {
const types = [];
function getParameterTypeFromCallContexts(parameterIndex: number, callContexts: CallContext[], isRestParameter: boolean, checker: TypeChecker) {
let types: Type[] = [];
if (callContexts) {
for (const callContext of callContexts) {
if (callContext.argumentTypes.length > parameterIndex) {
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[parameterIndex]));
if (isRestParameter) {
types = concatenate(types, map(callContext.argumentTypes.slice(parameterIndex), a => checker.getBaseTypeOfLiteralType(a)));
}
else {
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[parameterIndex]));
}
}
}
}
return types.length ? checker.getWidenedType(checker.getUnionType(types, true)) : checker.getAnyType();
const type = types.length ? checker.getWidenedType(checker.getUnionType(types, true)) : checker.getAnyType();
return isRestParameter ? checker.createArrayType(type) : type;
}

function getSignatureFromCallContext(callContext: CallContext, checker: TypeChecker): Signature {
const parameters: Symbol[] = [];
for (let i = 0; i < callContext.argumentTypes.length; i++) {
const symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, `p${i}`);
symbol.type = checker.getWidenedType(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[i]));
parameters.push(symbol); }
parameters.push(symbol);
}
const returnType = getTypeFromUsageContext(callContext.returnType, checker);
return checker.createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, callContext.argumentTypes.length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
}
Expand Down
11 changes: 11 additions & 0 deletions tests/cases/fourslash/codeFixInferFromUsageRestParam.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// <reference path='fourslash.ts' />

// @noImplicitAny: true
////function f(a: number, [|...rest |]){
////}
////f(1);
////f(2, "s1");
////f(3, "s1", "s2");
////f(3, "s1", "s2", "s3", "s4");

verify.rangeAfterCodeFix("...rest: string[]");