From 305baebc837af3984428723b20c0b3b1d5abbc77 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sun, 17 Jul 2022 12:54:37 +0300 Subject: [PATCH 1/3] feat(49928): provide quick fix for a missing callback function --- src/services/codefixes/fixAddMissingMember.ts | 57 ++++++++++++++++--- src/services/codefixes/helpers.ts | 2 +- .../codeFixAddMissingFunctionDeclaration19.ts | 27 +++++++++ .../codeFixAddMissingFunctionDeclaration20.ts | 23 ++++++++ .../codeFixAddMissingFunctionDeclaration21.ts | 39 +++++++++++++ .../codeFixAddMissingFunctionDeclaration22.ts | 34 +++++++++++ ...odeFixAddMissingFunctionDeclaration_all.ts | 29 +++++++--- ...ixAddMissingFunctionDeclaration_jsx_all.ts | 53 +++++++++++++++++ 8 files changed, 247 insertions(+), 17 deletions(-) create mode 100644 tests/cases/fourslash/codeFixAddMissingFunctionDeclaration19.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingFunctionDeclaration20.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingFunctionDeclaration21.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingFunctionDeclaration22.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingFunctionDeclaration_jsx_all.ts diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 3c09c850ce502..55ee314822ff9 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -31,8 +31,9 @@ namespace ts.codefix { const changes = textChanges.ChangeTracker.with(context, t => addJsxAttributes(t, context, info)); return [createCodeFixAction(fixMissingAttributes, changes, Diagnostics.Add_missing_attributes, fixMissingAttributes, Diagnostics.Add_all_missing_attributes)]; } - if (info.kind === InfoKind.Function) { - const changes = textChanges.ChangeTracker.with(context, t => addFunctionDeclaration(t, context, info)); + if (info.kind === InfoKind.Function || info.kind === InfoKind.Signature) { + const changes = textChanges.ChangeTracker.with(context, t => + info.kind === InfoKind.Signature ? addFunctionDeclarationFromSignature(t, context, info) : addFunctionDeclaration(t, context, info)); return [createCodeFixAction(fixMissingFunctionDeclaration, changes, [Diagnostics.Add_missing_function_declaration_0, info.token.text], fixMissingFunctionDeclaration, Diagnostics.Add_all_missing_function_declarations)]; } if (info.kind === InfoKind.Enum) { @@ -54,9 +55,13 @@ namespace ts.codefix { if (!info || !addToSeen(seen, getNodeId(info.parentDeclaration) + "#" + info.token.text)) { return; } - - if (fixId === fixMissingFunctionDeclaration && info.kind === InfoKind.Function) { - addFunctionDeclaration(changes, context, info); + if (fixId === fixMissingFunctionDeclaration && (info.kind === InfoKind.Function || info.kind === InfoKind.Signature)) { + if (info.kind === InfoKind.Signature) { + addFunctionDeclarationFromSignature(changes, context, info); + } + else { + addFunctionDeclaration(changes, context, info); + } } else if (fixId === fixMissingProperties && info.kind === InfoKind.ObjectLiteral) { addObjectLiteralProperties(changes, context, info); @@ -107,8 +112,8 @@ namespace ts.codefix { }, }); - const enum InfoKind { TypeLikeDeclaration, Enum, Function, ObjectLiteral, JsxAttributes } - type Info = TypeLikeDeclarationInfo | EnumInfo | FunctionInfo | ObjectLiteralInfo | JsxAttributesInfo; + const enum InfoKind { TypeLikeDeclaration, Enum, Function, ObjectLiteral, JsxAttributes, Signature } + type Info = TypeLikeDeclarationInfo | EnumInfo | FunctionInfo | ObjectLiteralInfo | JsxAttributesInfo | SignatureInfo; interface EnumInfo { readonly kind: InfoKind.Enum; @@ -150,6 +155,14 @@ namespace ts.codefix { readonly parentDeclaration: JsxOpeningLikeElement; } + interface SignatureInfo { + readonly kind: InfoKind.Signature; + readonly token: Identifier; + readonly signature: Signature; + readonly sourceFile: SourceFile; + readonly parentDeclaration: Node; + } + function getInfo(sourceFile: SourceFile, tokenPos: number, errorCode: number, checker: TypeChecker, program: Program): Info | undefined { // The identifier of the missing property. eg: // this.missing = 1; @@ -190,8 +203,16 @@ namespace ts.codefix { return { kind: InfoKind.JsxAttributes, token, attributes, parentDeclaration: token.parent }; } - if (isIdentifier(token) && isCallExpression(parent)) { - return { kind: InfoKind.Function, token, call: parent, sourceFile, modifierFlags: ModifierFlags.None, parentDeclaration: sourceFile }; + if (isIdentifier(token)) { + const type = checker.getContextualType(token); + if (type && getObjectFlags(type) & ObjectFlags.Anonymous) { + const signature = firstOrUndefined(checker.getSignaturesOfType(type, SignatureKind.Call)); + if (signature === undefined) return undefined; + return { kind: InfoKind.Signature, token, signature, sourceFile, parentDeclaration: token.parent }; + } + if (isCallExpression(parent)) { + return { kind: InfoKind.Function, token, call: parent, sourceFile, modifierFlags: ModifierFlags.None, parentDeclaration: sourceFile }; + } } if (!isPropertyAccessExpression(parent)) return undefined; @@ -458,6 +479,24 @@ namespace ts.codefix { importAdder.writeFixes(changes); } + function addFunctionDeclarationFromSignature(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: SignatureInfo) { + const quotePreference = getQuotePreference(context.sourceFile, context.preferences); + const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host); + const functionDeclaration = createSignatureDeclarationFromSignature(SyntaxKind.FunctionExpression, context, quotePreference, info.signature, + createStubbedBody(Diagnostics.Function_not_implemented.message, quotePreference), info.token, /*modifiers*/ undefined, /*optional*/ undefined, /*enclosingDeclaration*/ undefined, importAdder); + if (functionDeclaration === undefined) { + Debug.fail("fixMissingFunctionDeclaration codefix got unexpected error."); + } + const returnStatement = isJsxExpression(info.parentDeclaration) ? findAncestor(info.parentDeclaration, isReturnStatement) : undefined; + if (returnStatement) { + changes.insertNodeBefore(info.sourceFile, returnStatement, functionDeclaration, /*blankLineBetween*/ true); + } + else { + changes.insertNodeAtEndOfScope(info.sourceFile, info.sourceFile, functionDeclaration); + } + importAdder.writeFixes(changes); + } + function addJsxAttributes(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: JsxAttributesInfo) { const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host); const quotePreference = getQuotePreference(context.sourceFile, context.preferences); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 94b64e57ac934..13b3daa7847d2 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -185,7 +185,7 @@ namespace ts.codefix { optional: boolean | undefined, enclosingDeclaration: Node | undefined, importAdder: ImportAdder | undefined - ) { + ) { const program = context.program; const checker = program.getTypeChecker(); const scriptTarget = getEmitScriptTarget(program.getCompilerOptions()); diff --git a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration19.ts b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration19.ts new file mode 100644 index 0000000000000..f14ed8cb950dc --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration19.ts @@ -0,0 +1,27 @@ +/// + +////interface Foo { +//// a: (e: any) => void; +////} +//// +////const foo: Foo = { +//// a: fn +////} + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "fn"], + newFileContent: +`interface Foo { + a: (e: any) => void; +} + +const foo: Foo = { + a: fn +} + +function fn(e: any): void { + throw new Error("Function not implemented."); +} +` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration20.ts b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration20.ts new file mode 100644 index 0000000000000..c575e80159396 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration20.ts @@ -0,0 +1,23 @@ +/// + +////interface Foo { +//// f(type: string, listener: (this: object, type: string) => any): void; +////} +////declare let foo: Foo; +////foo.f("test", fn); + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "fn"], + newFileContent: +`interface Foo { + f(type: string, listener: (this: object, type: string) => any): void; +} +declare let foo: Foo; +foo.f("test", fn); + +function fn(this: object, type: string) { + throw new Error("Function not implemented."); +} +` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration21.ts b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration21.ts new file mode 100644 index 0000000000000..42ff7c6cd73f2 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration21.ts @@ -0,0 +1,39 @@ +/// + +// @jsx: preserve +// @filename: foo.tsx + +////interface P { +//// onClick: (a: number, b: string) => void; +////} +//// +////const A = ({ onClick }: P) => +////
; +//// +////const B = () => { +//// return ( +//// +//// ); +////} + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "handleClick"], + newFileContent: +`interface P { + onClick: (a: number, b: string) => void; +} + +const A = ({ onClick }: P) => +
; + +const B = () => { + function handleClick(a: number, b: string): void { + throw new Error("Function not implemented."); + } + + return ( + + ); +}` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration22.ts b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration22.ts new file mode 100644 index 0000000000000..a20260a0072c0 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration22.ts @@ -0,0 +1,34 @@ +/// + +// @jsx: preserve +// @filename: foo.tsx + +////interface P { +//// onClick: (a: number, b: string) => void; +////} +//// +////const A = ({ onClick }: P) => +////
; +//// +////const B = () => +//// + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "handleClick"], + newFileContent: +`interface P { + onClick: (a: number, b: string) => void; +} + +const A = ({ onClick }: P) => +
; + +const B = () => + + +function handleClick(a: number, b: string): void { + throw new Error("Function not implemented."); +} +` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration_all.ts b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration_all.ts index 7f7e338dab577..1736613e98f7c 100644 --- a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration_all.ts +++ b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration_all.ts @@ -1,33 +1,38 @@ /// -// @filename: /test.ts +// @filename: /test1.ts ////export const x = 1; -// @filename: /foo.ts -////import * as test from "./test"; +// @filename: /test2.ts +////import * as test from "./test1"; //// ////namespace Foo { //// export const x = 0; ////} //// +////interface I { +//// a: (e: any) => void; +////} +//// ////test.f(); ////Foo.f(); ////f(); +////const t1: I = { a: fn } -goTo.file("/foo.ts"); +goTo.file("/test2.ts"); verify.codeFixAll({ fixId: "fixMissingFunctionDeclaration", fixAllDescription: ts.Diagnostics.Add_all_missing_function_declarations.message, newFileContent: { - "/test.ts": + "/test1.ts": `export const x = 1; export function f() { throw new Error("Function not implemented."); } `, - "/foo.ts": -`import * as test from "./test"; + "/test2.ts": +`import * as test from "./test1"; namespace Foo { export const x = 0; @@ -37,13 +42,23 @@ namespace Foo { } } +interface I { + a: (e: any) => void; +} + test.f(); Foo.f(); f(); +const t1: I = { a: fn } function f() { throw new Error("Function not implemented."); } + + +function fn(e: any): void { + throw new Error("Function not implemented."); +} ` } }); diff --git a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration_jsx_all.ts b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration_jsx_all.ts new file mode 100644 index 0000000000000..3bbf54c66fb0b --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration_jsx_all.ts @@ -0,0 +1,53 @@ +/// + +// @jsx: preserve +// @filename: /foo.tsx + +////interface P { +//// onClick: (a: number, b: string) => void; +////} +//// +////const A = ({ onClick }: P) => +////
; +//// +////const B = () => +//// +//// +////const C = () => { +//// return ( +//// +//// ); +////} + +goTo.file("/foo.tsx"); +verify.codeFixAll({ + fixId: "fixMissingFunctionDeclaration", + fixAllDescription: ts.Diagnostics.Add_all_missing_function_declarations.message, + newFileContent: { + "/foo.tsx": +`interface P { + onClick: (a: number, b: string) => void; +} + +const A = ({ onClick }: P) => +
; + +const B = () => + + +const C = () => { + function handleClick(a: number, b: string): void { + throw new Error("Function not implemented."); + } + + return ( + + ); +} + +function handleClick(a: number, b: string): void { + throw new Error("Function not implemented."); +} +` + } +}); From 51e766a03b4c6cbece228b107726e425a10868f7 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 29 Jul 2022 13:53:27 +0300 Subject: [PATCH 2/3] remove addFunctionDeclarationFromSignature. fix formatting --- src/services/codefixes/fixAddMissingMember.ts | 59 ++++++++++--------- src/services/codefixes/helpers.ts | 13 +++- .../codeFixAddMissingFunctionDeclaration21.ts | 2 +- .../codeFixAddMissingFunctionDeclaration23.ts | 21 +++++++ 4 files changed, 62 insertions(+), 33 deletions(-) create mode 100644 tests/cases/fourslash/codeFixAddMissingFunctionDeclaration23.ts diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 55ee314822ff9..fa0dbb6c2dd1e 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -15,6 +15,15 @@ namespace ts.codefix { Diagnostics.Cannot_find_name_0.code ]; + enum InfoKind { + TypeLikeDeclaration, + Enum, + Function, + ObjectLiteral, + JsxAttributes, + Signature, + } + registerCodeFix({ errorCodes, getCodeActions(context) { @@ -32,8 +41,7 @@ namespace ts.codefix { return [createCodeFixAction(fixMissingAttributes, changes, Diagnostics.Add_missing_attributes, fixMissingAttributes, Diagnostics.Add_all_missing_attributes)]; } if (info.kind === InfoKind.Function || info.kind === InfoKind.Signature) { - const changes = textChanges.ChangeTracker.with(context, t => - info.kind === InfoKind.Signature ? addFunctionDeclarationFromSignature(t, context, info) : addFunctionDeclaration(t, context, info)); + const changes = textChanges.ChangeTracker.with(context, t => addFunctionDeclaration(t, context, info)); return [createCodeFixAction(fixMissingFunctionDeclaration, changes, [Diagnostics.Add_missing_function_declaration_0, info.token.text], fixMissingFunctionDeclaration, Diagnostics.Add_all_missing_function_declarations)]; } if (info.kind === InfoKind.Enum) { @@ -56,12 +64,7 @@ namespace ts.codefix { return; } if (fixId === fixMissingFunctionDeclaration && (info.kind === InfoKind.Function || info.kind === InfoKind.Signature)) { - if (info.kind === InfoKind.Signature) { - addFunctionDeclarationFromSignature(changes, context, info); - } - else { - addFunctionDeclaration(changes, context, info); - } + addFunctionDeclaration(changes, context, info); } else if (fixId === fixMissingProperties && info.kind === InfoKind.ObjectLiteral) { addObjectLiteralProperties(changes, context, info); @@ -112,7 +115,6 @@ namespace ts.codefix { }, }); - const enum InfoKind { TypeLikeDeclaration, Enum, Function, ObjectLiteral, JsxAttributes, Signature } type Info = TypeLikeDeclarationInfo | EnumInfo | FunctionInfo | ObjectLiteralInfo | JsxAttributesInfo | SignatureInfo; interface EnumInfo { @@ -137,7 +139,7 @@ namespace ts.codefix { readonly token: Identifier; readonly sourceFile: SourceFile; readonly modifierFlags: ModifierFlags; - readonly parentDeclaration: SourceFile | ModuleDeclaration; + readonly parentDeclaration: SourceFile | ModuleDeclaration | ReturnStatement; } interface ObjectLiteralInfo { @@ -208,10 +210,10 @@ namespace ts.codefix { if (type && getObjectFlags(type) & ObjectFlags.Anonymous) { const signature = firstOrUndefined(checker.getSignaturesOfType(type, SignatureKind.Call)); if (signature === undefined) return undefined; - return { kind: InfoKind.Signature, token, signature, sourceFile, parentDeclaration: token.parent }; + return { kind: InfoKind.Signature, token, signature, sourceFile, parentDeclaration: findScope(token) }; } if (isCallExpression(parent)) { - return { kind: InfoKind.Function, token, call: parent, sourceFile, modifierFlags: ModifierFlags.None, parentDeclaration: sourceFile }; + return { kind: InfoKind.Function, token, call: parent, sourceFile, modifierFlags: ModifierFlags.None, parentDeclaration: findScope(token) }; } } @@ -472,28 +474,19 @@ namespace ts.codefix { }); } - function addFunctionDeclaration(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: FunctionInfo) { - const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host); - const functionDeclaration = createSignatureDeclarationFromCallExpression(SyntaxKind.FunctionDeclaration, context, importAdder, info.call, idText(info.token), info.modifierFlags, info.parentDeclaration) as FunctionDeclaration; - changes.insertNodeAtEndOfScope(info.sourceFile, info.parentDeclaration, functionDeclaration); - importAdder.writeFixes(changes); - } - - function addFunctionDeclarationFromSignature(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: SignatureInfo) { + function addFunctionDeclaration(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: FunctionInfo | SignatureInfo) { const quotePreference = getQuotePreference(context.sourceFile, context.preferences); const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host); - const functionDeclaration = createSignatureDeclarationFromSignature(SyntaxKind.FunctionExpression, context, quotePreference, info.signature, - createStubbedBody(Diagnostics.Function_not_implemented.message, quotePreference), info.token, /*modifiers*/ undefined, /*optional*/ undefined, /*enclosingDeclaration*/ undefined, importAdder); + const functionDeclaration = info.kind === InfoKind.Function + ? createSignatureDeclarationFromCallExpression(SyntaxKind.FunctionDeclaration, context, importAdder, info.call, idText(info.token), info.modifierFlags, info.parentDeclaration) + : createSignatureDeclarationFromSignature(SyntaxKind.FunctionDeclaration, context, quotePreference, info.signature, createStubbedBody(Diagnostics.Function_not_implemented.message, quotePreference), info.token, /*modifiers*/ undefined, /*optional*/ undefined, /*enclosingDeclaration*/ undefined, importAdder); if (functionDeclaration === undefined) { Debug.fail("fixMissingFunctionDeclaration codefix got unexpected error."); } - const returnStatement = isJsxExpression(info.parentDeclaration) ? findAncestor(info.parentDeclaration, isReturnStatement) : undefined; - if (returnStatement) { - changes.insertNodeBefore(info.sourceFile, returnStatement, functionDeclaration, /*blankLineBetween*/ true); - } - else { - changes.insertNodeAtEndOfScope(info.sourceFile, info.sourceFile, functionDeclaration); - } + + isReturnStatement(info.parentDeclaration) + ? changes.insertNodeBefore(info.sourceFile, info.parentDeclaration, functionDeclaration, /*blankLineBetween*/ true) + : changes.insertNodeAtEndOfScope(info.sourceFile, info.parentDeclaration, functionDeclaration); importAdder.writeFixes(changes); } @@ -657,4 +650,12 @@ namespace ts.codefix { } return createPropertyNameNodeForIdentifierOrLiteral(symbol.name, target, quotePreference === QuotePreference.Single); } + + function findScope(node: Node) { + if (findAncestor(node, isJsxExpression)) { + const returnStatement = findAncestor(node.parent, isReturnStatement); + if (returnStatement) return returnStatement; + } + return getSourceFileOfNode(node); + } } diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 13b3daa7847d2..c6e321153165f 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -169,13 +169,17 @@ namespace ts.codefix { } function outputMethod(quotePreference: QuotePreference, signature: Signature, modifiers: NodeArray | undefined, name: PropertyName, body?: Block): void { - const method = createSignatureDeclarationFromSignature(SyntaxKind.MethodDeclaration, context, quotePreference, signature, body, name, modifiers, optional && !!(preserveOptional & PreserveOptionalFlags.Method), enclosingDeclaration, importAdder); + const method = createSignatureDeclarationFromSignature(SyntaxKind.MethodDeclaration, context, quotePreference, signature, body, name, modifiers, optional && !!(preserveOptional & PreserveOptionalFlags.Method), enclosingDeclaration, importAdder) as MethodDeclaration; if (method) addClassElement(method); } } export function createSignatureDeclarationFromSignature( - kind: SyntaxKind.MethodDeclaration | SyntaxKind.FunctionExpression | SyntaxKind.ArrowFunction, + kind: + | SyntaxKind.MethodDeclaration + | SyntaxKind.FunctionExpression + | SyntaxKind.ArrowFunction + | SyntaxKind.FunctionDeclaration, context: TypeConstructionContext, quotePreference: QuotePreference, signature: Signature, @@ -194,7 +198,7 @@ namespace ts.codefix { | NodeBuilderFlags.SuppressAnyReturnType | NodeBuilderFlags.AllowEmptyTuple | (quotePreference === QuotePreference.Single ? NodeBuilderFlags.UseSingleQuotesForStringLiteralType : NodeBuilderFlags.None); - const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, kind, enclosingDeclaration, flags, getNoopSymbolTrackerWithResolver(context)) as ArrowFunction | FunctionExpression | MethodDeclaration; + const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, kind, enclosingDeclaration, flags, getNoopSymbolTrackerWithResolver(context)) as ArrowFunction | FunctionExpression | MethodDeclaration | FunctionDeclaration; if (!signatureDeclaration) { return undefined; } @@ -273,6 +277,9 @@ namespace ts.codefix { if (isMethodDeclaration(signatureDeclaration)) { return factory.updateMethodDeclaration(signatureDeclaration, modifiers, asteriskToken, name ?? factory.createIdentifier(""), questionToken, typeParameters, parameters, type, body); } + if (isFunctionDeclaration(signatureDeclaration)) { + return factory.updateFunctionDeclaration(signatureDeclaration, modifiers, signatureDeclaration.asteriskToken, tryCast(name, isIdentifier), typeParameters, parameters, type, body ?? signatureDeclaration.body); + } return undefined; } diff --git a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration21.ts b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration21.ts index 42ff7c6cd73f2..a5689161936bb 100644 --- a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration21.ts +++ b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration21.ts @@ -1,4 +1,4 @@ -/// +/// // @jsx: preserve // @filename: foo.tsx diff --git a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration23.ts b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration23.ts new file mode 100644 index 0000000000000..612e226f4d8dc --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration23.ts @@ -0,0 +1,21 @@ +/// + +// @jsx: preserve +// @filename: foo.tsx + +////const A = () => { +//// return (
handleClick()}>
); +////} + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "handleClick"], + newFileContent: +`const A = () => { + function handleClick() { + throw new Error("Function not implemented."); + } + + return (
handleClick()}>
); +}` +}); From 838354c0e71f08718bf4795672a36f85671c6bd9 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 5 Aug 2022 09:23:31 +0300 Subject: [PATCH 3/3] add tests --- .../codeFixAddMissingFunctionDeclaration24.ts | 27 +++++++++++++++++++ .../codeFixAddMissingFunctionDeclaration25.ts | 23 ++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/cases/fourslash/codeFixAddMissingFunctionDeclaration24.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingFunctionDeclaration25.ts diff --git a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration24.ts b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration24.ts new file mode 100644 index 0000000000000..f14ed8cb950dc --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration24.ts @@ -0,0 +1,27 @@ +/// + +////interface Foo { +//// a: (e: any) => void; +////} +//// +////const foo: Foo = { +//// a: fn +////} + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "fn"], + newFileContent: +`interface Foo { + a: (e: any) => void; +} + +const foo: Foo = { + a: fn +} + +function fn(e: any): void { + throw new Error("Function not implemented."); +} +` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration25.ts b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration25.ts new file mode 100644 index 0000000000000..c575e80159396 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingFunctionDeclaration25.ts @@ -0,0 +1,23 @@ +/// + +////interface Foo { +//// f(type: string, listener: (this: object, type: string) => any): void; +////} +////declare let foo: Foo; +////foo.f("test", fn); + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Add_missing_function_declaration_0.message, "fn"], + newFileContent: +`interface Foo { + f(type: string, listener: (this: object, type: string) => any): void; +} +declare let foo: Foo; +foo.f("test", fn); + +function fn(this: object, type: string) { + throw new Error("Function not implemented."); +} +` +});