diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1845f2b601..02e20b0fb7 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4748,7 +4748,6 @@ "category": "Message", "code": 95062 }, - "Add missing enum member '{0}'": { "category": "Message", "code": 95063 @@ -4780,5 +4779,13 @@ "Add 'unknown' to all conversions of non-overlapping types": { "category": "Message", "code": 95070 + }, + "Add missing 'new' operator to call": { + "category": "Message", + "code": 95071 + }, + "Add missing 'new' operator to all calls": { + "category": "Message", + "code": 95072 } } diff --git a/src/services/codefixes/fixAddMissingNewOperator.ts b/src/services/codefixes/fixAddMissingNewOperator.ts new file mode 100644 index 0000000000..5acaf6eb82 --- /dev/null +++ b/src/services/codefixes/fixAddMissingNewOperator.ts @@ -0,0 +1,32 @@ +/* @internal */ +namespace ts.codefix { + const fixId = "addMissingNewOperator"; + const errorCodes = [Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new.code]; + registerCodeFix({ + errorCodes, + getCodeActions(context) { + const { sourceFile, span } = context; + const changes = textChanges.ChangeTracker.with(context, t => addMissingNewOperator(t, sourceFile, span)); + return [createCodeFixAction(fixId, changes, Diagnostics.Add_missing_new_operator_to_call, fixId, Diagnostics.Add_missing_new_operator_to_all_calls)]; + }, + fixIds: [fixId], + getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => + addMissingNewOperator(changes, context.sourceFile, diag)), + }); + + function addMissingNewOperator(changes: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan): void { + const call = cast(findAncestorMatchingSpan(sourceFile, span), isCallExpression); + const newExpression = createNew(call.expression, call.typeArguments, call.arguments); + + changes.replaceNode(sourceFile, call, newExpression); + } + + function findAncestorMatchingSpan(sourceFile: SourceFile, span: TextSpan): Node { + let token = getTokenAtPosition(sourceFile, span.start); + const end = textSpanEnd(span); + while (token.end < end) { + token = token.parent; + } + return token; + } +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 4c2147cf38..f59d273d7b 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -55,6 +55,7 @@ "codefixes/importFixes.ts", "codefixes/fixSpelling.ts", "codefixes/fixAddMissingMember.ts", + "codefixes/fixAddMissingNewOperator.ts", "codefixes/fixCannotFindModule.ts", "codefixes/fixClassDoesntImplementInheritedAbstractMember.ts", "codefixes/fixClassSuperMustPrecedeThisAccess.ts", diff --git a/tests/cases/fourslash/codeFixAddMissingNew.ts b/tests/cases/fourslash/codeFixAddMissingNew.ts new file mode 100644 index 0000000000..ff1ce125c9 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingNew.ts @@ -0,0 +1,14 @@ +/// + +////class C { +////} +////var c = C(); + +verify.codeFix({ + description: "Add missing 'new' operator to call", + index: 0, + newFileContent: +`class C { +} +var c = new C();` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingNew2.ts b/tests/cases/fourslash/codeFixAddMissingNew2.ts new file mode 100644 index 0000000000..a90b1c580d --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingNew2.ts @@ -0,0 +1,14 @@ +/// + +////class C { +////} +////let x = (() => C)()(); + +verify.codeFix({ + description: "Add missing 'new' operator to call", + index: 0, + newFileContent: +`class C { +} +let x = new ((() => C)())();` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingNew3.ts b/tests/cases/fourslash/codeFixAddMissingNew3.ts new file mode 100644 index 0000000000..c420d0446b --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingNew3.ts @@ -0,0 +1,16 @@ +/// + +////class C { +////} +////let x = [C]; +////let a = x[0](); + +verify.codeFix({ + description: "Add missing 'new' operator to call", + index: 0, + newFileContent: +`class C { +} +let x = [C]; +let a = new x[0]();` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingNew4.ts b/tests/cases/fourslash/codeFixAddMissingNew4.ts new file mode 100644 index 0000000000..e216b28bf5 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingNew4.ts @@ -0,0 +1,18 @@ +/// + +////class C { +////} +////class D { +////} +////let x = (true ? C : D)(); + +verify.codeFix({ + description: "Add missing 'new' operator to call", + index: 0, + newFileContent: +`class C { +} +class D { +} +let x = new (true ? C : D)();` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingNew5.ts b/tests/cases/fourslash/codeFixAddMissingNew5.ts new file mode 100644 index 0000000000..ecc5509a18 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingNew5.ts @@ -0,0 +1,25 @@ +/// + +////class C { +////} +//// +////function foo() { +//// return C; +////} +//// +////foo()!(); + + +verify.codeFix({ + description: "Add missing 'new' operator to call", + index: 0, + newFileContent: +`class C { +} + +function foo() { + return C; +} + +new (foo()!)();` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingNew_all.ts b/tests/cases/fourslash/codeFixAddMissingNew_all.ts new file mode 100644 index 0000000000..019f5b5a5a --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingNew_all.ts @@ -0,0 +1,18 @@ +/// + +////class C { +//// constructor(num?: number) {} +////} +////var a = C(); +////var b = C(3); + +verify.codeFixAll({ + fixId: "addMissingNewOperator", + fixAllDescription: "Add missing 'new' operator to all calls", + newFileContent: +`class C { + constructor(num?: number) {} +} +var a = new C(); +var b = new C(3);` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingNew_all_arguments.ts b/tests/cases/fourslash/codeFixAddMissingNew_all_arguments.ts new file mode 100644 index 0000000000..130dfd28fd --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingNew_all_arguments.ts @@ -0,0 +1,22 @@ +/// + +////class C { +//// x?: T; +//// constructor(x: T) { this.x = x; } +////} +////let a = C(1, 2, 3); +////let b = C("hello"); +////let c = C(); + +verify.codeFixAll({ + fixId: "addMissingNewOperator", + fixAllDescription: "Add missing 'new' operator to all calls", + newFileContent: +`class C { + x?: T; + constructor(x: T) { this.x = x; } +} +let a = new C(1, 2, 3); +let b = new C("hello"); +let c = new C();` +});