Skip to content

Commit 3b31c70

Browse files
committed
feat(51870): add a quick fix to add an additional parameters
1 parent ca7a3af commit 3b31c70

18 files changed

+353
-2
lines changed

src/compiler/diagnosticMessages.json

+12
Original file line numberDiff line numberDiff line change
@@ -7744,6 +7744,18 @@
77447744
"category": "Message",
77457745
"code": 95187
77467746
},
7747+
"Add missing parameter to '{0}'": {
7748+
"category": "Message",
7749+
"code": 95188
7750+
},
7751+
"Add missing parameters to '{0}'": {
7752+
"category": "Message",
7753+
"code": 95189
7754+
},
7755+
"Add all missing parameters": {
7756+
"category": "Message",
7757+
"code": 95190
7758+
},
77477759

77487760
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
77497761
"category": "Error",

src/services/_namespaces/ts.codefix.ts

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export * from "../codefixes/fixSpelling";
3131
export * from "../codefixes/returnValueCorrect";
3232
export * from "../codefixes/fixAddMissingMember";
3333
export * from "../codefixes/fixAddMissingNewOperator";
34+
export * from "../codefixes/fixAddMissingParam";
3435
export * from "../codefixes/fixCannotFindModule";
3536
export * from "../codefixes/fixClassDoesntImplementInheritedAbstractMember";
3637
export * from "../codefixes/fixClassSuperMustPrecedeThisAccess";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import {
2+
append,
3+
declarationNameToString,
4+
Diagnostics,
5+
factory,
6+
findAncestor,
7+
firstOrUndefined,
8+
forEach,
9+
FunctionLikeDeclaration,
10+
getNameOfAccessExpression,
11+
getNameOfDeclaration,
12+
getTokenAtPosition,
13+
isAccessExpression,
14+
isCallExpression,
15+
isFunctionLikeDeclaration,
16+
isIdentifier,
17+
isParameter,
18+
isPropertyDeclaration,
19+
isVariableDeclaration,
20+
length,
21+
Node,
22+
NodeBuilderFlags,
23+
SignatureDeclaration,
24+
SignatureKind,
25+
SourceFile,
26+
SyntaxKind,
27+
textChanges,
28+
TypeChecker,
29+
TypeNode,
30+
} from "../_namespaces/ts";
31+
import {
32+
codeFixAll,
33+
createCodeFixAction,
34+
registerCodeFix,
35+
} from "../_namespaces/ts.codefix";
36+
37+
const fixId = "addMissingParam";
38+
const errorCodes = [Diagnostics.Expected_0_arguments_but_got_1.code];
39+
40+
registerCodeFix({
41+
errorCodes,
42+
fixIds: [fixId],
43+
getCodeActions(context) {
44+
const checker = context.program.getTypeChecker();
45+
const info = getInfo(context.sourceFile, checker, context.span.start);
46+
if (info === undefined) {
47+
return undefined;
48+
}
49+
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, info));
50+
return [createCodeFixAction(fixId, changes, [length(info.parameters) === 1 ? Diagnostics.Add_missing_parameter_to_0 : Diagnostics.Add_missing_parameters_to_0, info.name], fixId, Diagnostics.Add_all_missing_parameters)];
51+
},
52+
getAllCodeActions: context =>
53+
codeFixAll(context, errorCodes, (changes, diag) => {
54+
const checker = context.program.getTypeChecker();
55+
const info = getInfo(context.sourceFile, checker, diag.start);
56+
if (info) {
57+
doChange(changes, context.sourceFile, info);
58+
}
59+
}),
60+
});
61+
62+
interface Parameter {
63+
readonly name: string;
64+
readonly type: TypeNode;
65+
}
66+
67+
interface Info {
68+
readonly signature: SignatureDeclaration;
69+
readonly name: string;
70+
readonly parameters: Parameter[];
71+
}
72+
73+
function getInfo(sourceFile: SourceFile, checker: TypeChecker, pos: number): Info | undefined {
74+
const token = getTokenAtPosition(sourceFile, pos);
75+
const callExpression = findAncestor(token, isCallExpression);
76+
if (callExpression === undefined || length(callExpression.arguments) === 0) {
77+
return undefined;
78+
}
79+
80+
const type = checker.getTypeAtLocation(callExpression.expression);
81+
const signature = firstOrUndefined(checker.getSignaturesOfType(type, SignatureKind.Call));
82+
if (signature && signature.declaration && isFunctionLikeDeclaration(signature.declaration)) {
83+
const name = tryGetName(signature.declaration);
84+
if (name === undefined) {
85+
return undefined;
86+
}
87+
88+
const parameters: Parameter[] = [];
89+
const parametersLength = length(signature.declaration.parameters);
90+
for (let i = parametersLength, index = 0; i < length(callExpression.arguments); i++) {
91+
const arg = callExpression.arguments[i];
92+
const expr = isAccessExpression(arg) ? getNameOfAccessExpression(arg) : arg;
93+
append(parameters, {
94+
name: expr && isIdentifier(expr) ? expr.text : `param${index++}`,
95+
type: getArgumentType(checker, expr, signature.declaration),
96+
});
97+
}
98+
99+
return {
100+
signature: signature.declaration,
101+
name: declarationNameToString(name),
102+
parameters,
103+
};
104+
}
105+
return undefined;
106+
}
107+
108+
function tryGetName(node: FunctionLikeDeclaration) {
109+
const name = getNameOfDeclaration(node);
110+
if (name) {
111+
return name;
112+
}
113+
114+
if (
115+
isVariableDeclaration(node.parent) && isIdentifier(node.parent.name) ||
116+
isPropertyDeclaration(node.parent) ||
117+
isParameter(node.parent)
118+
) {
119+
return node.parent.name;
120+
}
121+
}
122+
123+
function getArgumentType(checker: TypeChecker, node: Node, enclosingDeclaration: Node) {
124+
if (node) {
125+
const typeNode = checker.typeToTypeNode(checker.getWidenedType(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(node))), enclosingDeclaration, NodeBuilderFlags.NoTruncation);
126+
if (typeNode) {
127+
return typeNode;
128+
}
129+
}
130+
return factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword);
131+
}
132+
133+
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { signature, parameters }: Info) {
134+
forEach(parameters, ({ name, type }, index) => {
135+
const param = factory.createParameterDeclaration(
136+
/*modifiers*/ undefined,
137+
/*dotDotDotToken*/ undefined,
138+
name,
139+
/*questionToken*/ undefined,
140+
type,
141+
/*initializer*/ undefined,
142+
);
143+
if (length(signature.parameters) === 0 && index === 0) {
144+
changes.insertNodeAt(sourceFile, signature.parameters.end, param);
145+
}
146+
else {
147+
changes.insertNodeAtEndOfList(sourceFile, signature.parameters, param);
148+
}
149+
});
150+
}

tests/cases/fourslash/arityErrorAfterSignatureHelp.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ verify.signatureHelp({
1818
kind: "retrigger"
1919
}
2020
})
21-
verify.not.codeFixAvailable() // trigger typecheck
21+
verify.codeFixAvailable("addMissingParam") // trigger typecheck
2222
verify.errorExistsBetweenMarkers("1", "3");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////[|function f() {}|]
4+
////
5+
////const a = 1;
6+
////f(a);
7+
8+
verify.codeFix({
9+
description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "f"],
10+
index: 0,
11+
newRangeContent: "function f(a: number) {}"
12+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////[|function f() {}|]
4+
////
5+
////const a = 1;
6+
////const b = "";
7+
////f(a, b, true);
8+
9+
verify.codeFix({
10+
description: [ts.Diagnostics.Add_missing_parameters_to_0.message, "f"],
11+
index: 0,
12+
newRangeContent: "function f(a: number, b: string, param0: boolean) {}"
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////class C {
4+
//// [|private p = () => {}|]
5+
//// m(a: boolean) {
6+
//// this.p(a);
7+
//// }
8+
////}
9+
10+
verify.codeFix({
11+
description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "p"],
12+
index: 0,
13+
newRangeContent: "private p = (a: boolean) => {}"
14+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////function f([|cb = () => {}|]) {
4+
//// cb("");
5+
////}
6+
7+
verify.codeFix({
8+
description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "cb"],
9+
index: 0,
10+
newRangeContent: "cb = (param0: string) => {}"
11+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////[|function f(a: number) {}|]
4+
////
5+
////const a = 1;
6+
////const b = 1;
7+
////f(a, b);
8+
9+
verify.codeFix({
10+
description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "f"],
11+
index: 0,
12+
newRangeContent: "function f(a: number, b: number) {}"
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////class C {
4+
//// private a = 1;
5+
////
6+
//// [|m1() {}|]
7+
//// m2() {
8+
//// this.m1(this.a);
9+
//// }
10+
////}
11+
12+
verify.codeFix({
13+
description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "m1"],
14+
index: 0,
15+
newRangeContent: "m1(a: number) {}"
16+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////[|function f() {}|]
4+
////
5+
////f("");
6+
7+
verify.codeFix({
8+
description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "f"],
9+
index: 0,
10+
newRangeContent: "function f(param0: string) {}"
11+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////[|const f = function () {}|]
4+
////
5+
////f("");
6+
7+
verify.codeFix({
8+
description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "f"],
9+
index: 0,
10+
newRangeContent: "const f = function (param0: string) {}"
11+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////[|const f = () => {}|]
4+
////
5+
////f("");
6+
7+
verify.codeFix({
8+
description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "f"],
9+
index: 0,
10+
newRangeContent: "const f = (param0: string) => {}"
11+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////class C {
4+
//// [|m1() {}|]
5+
//// m2(a: boolean) {
6+
//// this.m1(a);
7+
//// }
8+
////}
9+
10+
verify.codeFix({
11+
description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "m1"],
12+
index: 0,
13+
newRangeContent: "m1(a: boolean) {}"
14+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////[|function f(a: number) {}|]
4+
////
5+
////const a = 1;
6+
////const b = 1;
7+
////const c = 1;
8+
////f(a, b, c);
9+
10+
verify.codeFix({
11+
description: [ts.Diagnostics.Add_missing_parameters_to_0.message, "f"],
12+
index: 0,
13+
newRangeContent: "function f(a: number, b: number, c: number) {}"
14+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////[|function f() {}|]
4+
////f("", { x: 1 }, [ "" ], true);
5+
6+
verify.codeFix({
7+
description: [ts.Diagnostics.Add_missing_parameters_to_0.message, "f"],
8+
index: 0,
9+
newRangeContent: "function f(param0: string, param1: { x: number; }, param2: string[], param3: boolean) {}"
10+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////[|function f1() {}|]
4+
////
5+
////const a = 1;
6+
////const b = "";
7+
////f1(a, b, true);
8+
////
9+
////function f2() {}
10+
////f2("", { x: 1 }, [ "" ], true);
11+
////
12+
////class C {
13+
//// [|m1() {}|]
14+
//// m2(a: boolean) {
15+
//// this.m1(a);
16+
//// }
17+
////}
18+
19+
verify.codeFixAll({
20+
fixId: "addMissingParam",
21+
fixAllDescription: ts.Diagnostics.Add_all_missing_parameters.message,
22+
newFileContent:
23+
`function f1(a: number, b: string, param0: boolean) {}
24+
25+
const a = 1;
26+
const b = "";
27+
f1(a, b, true);
28+
29+
function f2(param0: string, param1: { x: number; }, param2: string[], param3: boolean) {}
30+
f2("", { x: 1 }, [ "" ], true);
31+
32+
class C {
33+
m1(a: boolean) {}
34+
m2(a: boolean) {
35+
this.m1(a);
36+
}
37+
}`
38+
});

tests/cases/fourslash/codeFixInferFromUsageCallBodyBoth.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
////f(new C())
1414

1515

16-
verify.rangeAfterCodeFix("x: number | C, y: undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0);
16+
verify.rangeAfterCodeFix("x: number | C, y: undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 1);

0 commit comments

Comments
 (0)