From 1811cf5750e3e635f5c6fb397f82985d34e3989e Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Mon, 13 Nov 2023 22:02:50 +0200 Subject: [PATCH] feat(51870): add a quick fix to add an additional parameters --- src/compiler/diagnosticMessages.json | 12 ++ src/services/_namespaces/ts.codefix.ts | 1 + src/services/codefixes/fixAddMissingParam.ts | 150 ++++++++++++++++++ .../fourslash/codeFixAddMissingParam1.ts | 12 ++ .../fourslash/codeFixAddMissingParam10.ts | 13 ++ .../fourslash/codeFixAddMissingParam11.ts | 14 ++ .../fourslash/codeFixAddMissingParam12.ts | 11 ++ .../fourslash/codeFixAddMissingParam2.ts | 13 ++ .../fourslash/codeFixAddMissingParam3.ts | 16 ++ .../fourslash/codeFixAddMissingParam4.ts | 11 ++ .../fourslash/codeFixAddMissingParam5.ts | 11 ++ .../fourslash/codeFixAddMissingParam6.ts | 11 ++ .../fourslash/codeFixAddMissingParam7.ts | 14 ++ .../fourslash/codeFixAddMissingParam8.ts | 14 ++ .../fourslash/codeFixAddMissingParam9.ts | 10 ++ .../fourslash/codeFixAddMissingParam_all.ts | 38 +++++ 16 files changed, 351 insertions(+) create mode 100644 src/services/codefixes/fixAddMissingParam.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam1.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam10.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam11.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam12.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam2.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam3.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam4.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam5.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam6.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam7.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam8.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam9.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingParam_all.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 81fd5981ac8a4..d789bc36f76fb 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -7744,6 +7744,18 @@ "category": "Message", "code": 95187 }, + "Add missing parameter to '{0}'": { + "category": "Message", + "code": 95188 + }, + "Add missing parameters to '{0}'": { + "category": "Message", + "code": 95189 + }, + "Add all missing parameters": { + "category": "Message", + "code": 95190 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/services/_namespaces/ts.codefix.ts b/src/services/_namespaces/ts.codefix.ts index 3bf0c18cb9e5a..72b1cb87c50f7 100644 --- a/src/services/_namespaces/ts.codefix.ts +++ b/src/services/_namespaces/ts.codefix.ts @@ -31,6 +31,7 @@ export * from "../codefixes/fixSpelling"; export * from "../codefixes/returnValueCorrect"; export * from "../codefixes/fixAddMissingMember"; export * from "../codefixes/fixAddMissingNewOperator"; +export * from "../codefixes/fixAddMissingParam"; export * from "../codefixes/fixCannotFindModule"; export * from "../codefixes/fixClassDoesntImplementInheritedAbstractMember"; export * from "../codefixes/fixClassSuperMustPrecedeThisAccess"; diff --git a/src/services/codefixes/fixAddMissingParam.ts b/src/services/codefixes/fixAddMissingParam.ts new file mode 100644 index 0000000000000..db3c5c228c295 --- /dev/null +++ b/src/services/codefixes/fixAddMissingParam.ts @@ -0,0 +1,150 @@ +import { + append, + declarationNameToString, + Diagnostics, + factory, + findAncestor, + firstOrUndefined, + forEach, + FunctionLikeDeclaration, + getNameOfAccessExpression, + getNameOfDeclaration, + getTokenAtPosition, + isAccessExpression, + isCallExpression, + isFunctionLikeDeclaration, + isIdentifier, + isParameter, + isPropertyDeclaration, + isVariableDeclaration, + length, + Node, + NodeBuilderFlags, + SignatureDeclaration, + SignatureKind, + SourceFile, + SyntaxKind, + textChanges, + TypeChecker, + TypeNode, +} from "../_namespaces/ts"; +import { + codeFixAll, + createCodeFixAction, + registerCodeFix, +} from "../_namespaces/ts.codefix"; + +const fixId = "addMissingParam"; +const errorCodes = [Diagnostics.Expected_0_arguments_but_got_1.code]; + +registerCodeFix({ + errorCodes, + fixIds: [fixId], + getCodeActions(context) { + const checker = context.program.getTypeChecker(); + const info = getInfo(context.sourceFile, checker, context.span.start); + if (info === undefined) { + return undefined; + } + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, info)); + 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)]; + }, + getAllCodeActions: context => + codeFixAll(context, errorCodes, (changes, diag) => { + const checker = context.program.getTypeChecker(); + const info = getInfo(context.sourceFile, checker, diag.start); + if (info) { + doChange(changes, context.sourceFile, info); + } + }), +}); + +interface Parameter { + readonly name: string; + readonly type: TypeNode; +} + +interface Info { + readonly signature: SignatureDeclaration; + readonly name: string; + readonly parameters: Parameter[]; +} + +function getInfo(sourceFile: SourceFile, checker: TypeChecker, pos: number): Info | undefined { + const token = getTokenAtPosition(sourceFile, pos); + const callExpression = findAncestor(token, isCallExpression); + if (callExpression === undefined) { + return undefined; + } + + const type = checker.getTypeAtLocation(callExpression.expression); + const signature = firstOrUndefined(checker.getSignaturesOfType(type, SignatureKind.Call)); + if (signature && signature.declaration && isFunctionLikeDeclaration(signature.declaration)) { + const name = tryGetName(signature.declaration); + if (name === undefined) { + return undefined; + } + + const parameters: Parameter[] = []; + const parametersLength = length(signature.declaration.parameters); + for (let i = parametersLength, index = 0; i < length(callExpression.arguments); i++) { + const arg = callExpression.arguments[i]; + const expr = isAccessExpression(arg) ? getNameOfAccessExpression(arg) : arg; + append(parameters, { + name: expr && isIdentifier(expr) ? expr.text : `param${index++}`, + type: getArgumentType(checker, expr, signature.declaration), + }); + } + + return { + signature: signature.declaration, + name: declarationNameToString(name), + parameters, + }; + } + return undefined; +} + +function tryGetName(node: FunctionLikeDeclaration) { + const name = getNameOfDeclaration(node); + if (name) { + return name; + } + + if ( + isVariableDeclaration(node.parent) && isIdentifier(node.parent.name) || + isPropertyDeclaration(node.parent) || + isParameter(node.parent) + ) { + return node.parent.name; + } +} + +function getArgumentType(checker: TypeChecker, node: Node, enclosingDeclaration: Node) { + if (node) { + const typeNode = checker.typeToTypeNode(checker.getWidenedType(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(node))), enclosingDeclaration, NodeBuilderFlags.NoTruncation); + if (typeNode) { + return typeNode; + } + } + return factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword); +} + +function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { signature, parameters }: Info) { + forEach(parameters, ({ name, type }, index) => { + const param = factory.createParameterDeclaration( + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + name, + /*questionToken*/ undefined, + type, + /*initializer*/ undefined, + ); + if (length(signature.parameters) === 0 && index === 0) { + changes.insertNodeAt(sourceFile, signature.parameters.end, param); + } + else { + changes.insertNodeAtEndOfList(sourceFile, signature.parameters, param); + } + }); +} diff --git a/tests/cases/fourslash/codeFixAddMissingParam1.ts b/tests/cases/fourslash/codeFixAddMissingParam1.ts new file mode 100644 index 0000000000000..2a340f6677a6c --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam1.ts @@ -0,0 +1,12 @@ +/// + +////[|function f() {}|] +//// +////const a = 1; +////f(a); + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "f"], + index: 0, + newRangeContent: "function f(a: number) {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam10.ts b/tests/cases/fourslash/codeFixAddMissingParam10.ts new file mode 100644 index 0000000000000..b300f9d5641d8 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam10.ts @@ -0,0 +1,13 @@ +/// + +////[|function f() {}|] +//// +////const a = 1; +////const b = ""; +////f(a, b, true); + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameters_to_0.message, "f"], + index: 0, + newRangeContent: "function f(a: number, b: string, param0: boolean) {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam11.ts b/tests/cases/fourslash/codeFixAddMissingParam11.ts new file mode 100644 index 0000000000000..d930c95fcdf4b --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam11.ts @@ -0,0 +1,14 @@ +/// + +////class C { +//// [|private p = () => {}|] +//// m(a: boolean) { +//// this.p(a); +//// } +////} + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "p"], + index: 0, + newRangeContent: "private p = (a: boolean) => {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam12.ts b/tests/cases/fourslash/codeFixAddMissingParam12.ts new file mode 100644 index 0000000000000..db66172de06fd --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam12.ts @@ -0,0 +1,11 @@ +/// + +////function f([|cb = () => {}|]) { +//// cb(""); +////} + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "cb"], + index: 0, + newRangeContent: "cb = (param0: string) => {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam2.ts b/tests/cases/fourslash/codeFixAddMissingParam2.ts new file mode 100644 index 0000000000000..0c8d07b119070 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam2.ts @@ -0,0 +1,13 @@ +/// + +////[|function f(a: number) {}|] +//// +////const a = 1; +////const b = 1; +////f(a, b); + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "f"], + index: 0, + newRangeContent: "function f(a: number, b: number) {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam3.ts b/tests/cases/fourslash/codeFixAddMissingParam3.ts new file mode 100644 index 0000000000000..632c56edaf35d --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam3.ts @@ -0,0 +1,16 @@ +/// + +////class C { +//// private a = 1; +//// +//// [|m1() {}|] +//// m2() { +//// this.m1(this.a); +//// } +////} + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "m1"], + index: 0, + newRangeContent: "m1(a: number) {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam4.ts b/tests/cases/fourslash/codeFixAddMissingParam4.ts new file mode 100644 index 0000000000000..2576007fdf244 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam4.ts @@ -0,0 +1,11 @@ +/// + +////[|function f() {}|] +//// +////f(""); + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "f"], + index: 0, + newRangeContent: "function f(param0: string) {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam5.ts b/tests/cases/fourslash/codeFixAddMissingParam5.ts new file mode 100644 index 0000000000000..bcb3ae9b669bc --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam5.ts @@ -0,0 +1,11 @@ +/// + +////[|const f = function () {}|] +//// +////f(""); + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "f"], + index: 0, + newRangeContent: "const f = function (param0: string) {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam6.ts b/tests/cases/fourslash/codeFixAddMissingParam6.ts new file mode 100644 index 0000000000000..758ad29d471fa --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam6.ts @@ -0,0 +1,11 @@ +/// + +////[|const f = () => {}|] +//// +////f(""); + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "f"], + index: 0, + newRangeContent: "const f = (param0: string) => {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam7.ts b/tests/cases/fourslash/codeFixAddMissingParam7.ts new file mode 100644 index 0000000000000..c9d14dcb211e5 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam7.ts @@ -0,0 +1,14 @@ +/// + +////class C { +//// [|m1() {}|] +//// m2(a: boolean) { +//// this.m1(a); +//// } +////} + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameter_to_0.message, "m1"], + index: 0, + newRangeContent: "m1(a: boolean) {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam8.ts b/tests/cases/fourslash/codeFixAddMissingParam8.ts new file mode 100644 index 0000000000000..55e3931a49b25 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam8.ts @@ -0,0 +1,14 @@ +/// + +////[|function f(a: number) {}|] +//// +////const a = 1; +////const b = 1; +////const c = 1; +////f(a, b, c); + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameters_to_0.message, "f"], + index: 0, + newRangeContent: "function f(a: number, b: number, c: number) {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam9.ts b/tests/cases/fourslash/codeFixAddMissingParam9.ts new file mode 100644 index 0000000000000..e65d37ffcbc85 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam9.ts @@ -0,0 +1,10 @@ +/// + +////[|function f() {}|] +////f("", { x: 1 }, [ "" ], true); + +verify.codeFix({ + description: [ts.Diagnostics.Add_missing_parameters_to_0.message, "f"], + index: 0, + newRangeContent: "function f(param0: string, param1: { x: number; }, param2: string[], param3: boolean) {}" +}); diff --git a/tests/cases/fourslash/codeFixAddMissingParam_all.ts b/tests/cases/fourslash/codeFixAddMissingParam_all.ts new file mode 100644 index 0000000000000..12ed799d30664 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingParam_all.ts @@ -0,0 +1,38 @@ +/// + +////[|function f1() {}|] +//// +////const a = 1; +////const b = ""; +////f1(a, b, true); +//// +////function f2() {} +////f2("", { x: 1 }, [ "" ], true); +//// +////class C { +//// [|m1() {}|] +//// m2(a: boolean) { +//// this.m1(a); +//// } +////} + +verify.codeFixAll({ + fixId: "addMissingParam", + fixAllDescription: ts.Diagnostics.Add_all_missing_parameters.message, + newFileContent: +`function f1(a: number, b: string, param0: boolean) {} + +const a = 1; +const b = ""; +f1(a, b, true); + +function f2(param0: string, param1: { x: number; }, param2: string[], param3: boolean) {} +f2("", { x: 1 }, [ "" ], true); + +class C { + m1(a: boolean) {} + m2(a: boolean) { + this.m1(a); + } +}` +});