Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Codefix: add quick fix for missing 'new' operator #27019

15 changes: 11 additions & 4 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4608,7 +4608,6 @@
"category": "Message",
"code": 95062
},

"Add missing enum member '{0}'": {
"category": "Message",
"code": 95063
Expand All @@ -4619,10 +4618,18 @@
},
"Convert to async function":{
"category": "Message",
"code": 95065
"code": 95065
},
"Convert all to async functions": {
"category": "Message",
"code": 95066
"category": "Message",
"code": 95066
},
"Add missing 'new' operator to call": {
"category": "Message",
"code": 95067
},
"Add missing 'new' operator to all calls": {
"category": "Message",
"code": 95068
}
}
28 changes: 28 additions & 0 deletions src/services/codefixes/fixAddMissingNewOperator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* @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 missingNewExpression = getMissingNewExpression(sourceFile, span.start);
const changes = textChanges.ChangeTracker.with(context, t => addMissingNewOperator(t, sourceFile, missingNewExpression));
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, getMissingNewExpression(diag.file, diag.start))),
});

function getMissingNewExpression(sourceFile: SourceFile, pos: number): Expression {
const token = getTokenAtPosition(sourceFile, pos);
Debug.assert(isCallExpression(token.parent));
Copy link
Member

Choose a reason for hiding this comment

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

Debug.assertNode

return <Expression>token;
Copy link
Member

Choose a reason for hiding this comment

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

The token isn't necessarily an expression (for example, opening parentheses). I think you should be returning the call expression itself.

}

function addMissingNewOperator(changes: textChanges.ChangeTracker, sourceFile: SourceFile, missingNewExpression: Expression): void {
Copy link
Member

Choose a reason for hiding this comment

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

If you return a call expression above, you now have a CallExpression to work with. From there, you can pass along the (optionaly present) typeArguments and arguments.

const newTypeNode = createNew(missingNewExpression, /*typeArguments*/ undefined, /*argumentsArray*/ undefined);
changes.replaceNode(sourceFile, missingNewExpression, newTypeNode);
}
}
1 change: 1 addition & 0 deletions src/services/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"codefixes/importFixes.ts",
"codefixes/fixSpelling.ts",
"codefixes/fixAddMissingMember.ts",
"codefixes/fixAddMissingNewOperator.ts",
"codefixes/fixCannotFindModule.ts",
"codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
"codefixes/fixClassSuperMustPrecedeThisAccess.ts",
Expand Down
13 changes: 13 additions & 0 deletions tests/cases/fourslash/codeFixAddMissingNew.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />

////class C {
////}
////var c = C();

verify.codeFix({
description: "Add missing 'new' operator to call",
index: 0,
newFileContent: `class C {
}
var c = new C();`
});
18 changes: 18 additions & 0 deletions tests/cases/fourslash/codeFixAddMissingNew_all.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />

////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);`
});
22 changes: 22 additions & 0 deletions tests/cases/fourslash/codeFixAddMissingNew_all_arguments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// <reference path='fourslash.ts' />

////class C<T = number> {
//// x?: T;
//// constructor(x: T) { this.x = x; }
////}
////let a = C(1, 2, 3);
////let b = C<string>("hello");
////let c = C<boolean>();

verify.codeFixAll({
fixId: "addMissingNewOperator",
fixAllDescription: "Add missing 'new' operator to all calls",
newFileContent:
`class C<T = number> {
x?: T;
constructor(x: T) { this.x = x; }
}
let a = new C(1, 2, 3);
let b = new C<string>("hello");
let c = new C<boolean>();`
});