mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-22 11:31:11 -05:00
Merge pull request #28290 from rflorian/add-codefix-cannot-find-name-in-for-loop
Add codefix for 'Cannot find name' diagnostic
This commit is contained in:
@@ -5079,6 +5079,14 @@
|
||||
"category": "Message",
|
||||
"code": 95080
|
||||
},
|
||||
"Add 'const' to unresolved variable": {
|
||||
"category": "Message",
|
||||
"code": 95081
|
||||
},
|
||||
"Add 'const' to all unresolved variables": {
|
||||
"category": "Message",
|
||||
"code": 95082
|
||||
},
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
"code": 18004
|
||||
|
||||
109
src/services/codefixes/addMissingConst.ts
Normal file
109
src/services/codefixes/addMissingConst.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
const fixId = "addMissingConst";
|
||||
const errorCodes = [
|
||||
Diagnostics.Cannot_find_name_0.code,
|
||||
Diagnostics.No_value_exists_in_scope_for_the_shorthand_property_0_Either_declare_one_or_provide_an_initializer.code
|
||||
];
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions: (context) => {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start, context.program));
|
||||
if (changes.length > 0) {
|
||||
return [createCodeFixAction(fixId, changes, Diagnostics.Add_const_to_unresolved_variable, fixId, Diagnostics.Add_const_to_all_unresolved_variables)];
|
||||
}
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => {
|
||||
const fixedNodes = new NodeSet();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag.start, context.program, fixedNodes));
|
||||
},
|
||||
});
|
||||
|
||||
function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number, program: Program, fixedNodes?: NodeSet<Node>) {
|
||||
const token = getTokenAtPosition(sourceFile, pos);
|
||||
const forInitializer = findAncestor(token, node =>
|
||||
isForInOrOfStatement(node.parent) ? node.parent.initializer === node :
|
||||
isPossiblyPartOfDestructuring(node) ? false : "quit"
|
||||
);
|
||||
if (forInitializer) return applyChange(changeTracker, forInitializer, sourceFile, fixedNodes);
|
||||
|
||||
const parent = token.parent;
|
||||
if (isBinaryExpression(parent) && isExpressionStatement(parent.parent)) {
|
||||
return applyChange(changeTracker, token, sourceFile, fixedNodes);
|
||||
}
|
||||
|
||||
if (isArrayLiteralExpression(parent)) {
|
||||
const checker = program.getTypeChecker();
|
||||
if (!every(parent.elements, element => arrayElementCouldBeVariableDeclaration(element, checker))) {
|
||||
return;
|
||||
}
|
||||
|
||||
return applyChange(changeTracker, parent, sourceFile, fixedNodes);
|
||||
}
|
||||
|
||||
const commaExpression = findAncestor(token, node =>
|
||||
isExpressionStatement(node.parent) ? true :
|
||||
isPossiblyPartOfCommaSeperatedInitializer(node) ? false : "quit"
|
||||
);
|
||||
if (commaExpression) {
|
||||
const checker = program.getTypeChecker();
|
||||
if (!expressionCouldBeVariableDeclaration(commaExpression, checker)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return applyChange(changeTracker, commaExpression, sourceFile, fixedNodes);
|
||||
}
|
||||
}
|
||||
|
||||
function applyChange(changeTracker: textChanges.ChangeTracker, initializer: Node, sourceFile: SourceFile, fixedNodes?: NodeSet<Node>) {
|
||||
if (!fixedNodes || fixedNodes.tryAdd(initializer)) {
|
||||
changeTracker.insertModifierBefore(sourceFile, SyntaxKind.ConstKeyword, initializer);
|
||||
}
|
||||
}
|
||||
|
||||
function isPossiblyPartOfDestructuring(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function arrayElementCouldBeVariableDeclaration(expression: Expression, checker: TypeChecker): boolean {
|
||||
const identifier =
|
||||
isIdentifier(expression) ? expression :
|
||||
isAssignmentExpression(expression, /*excludeCompoundAssignment*/ true) && isIdentifier(expression.left) ? expression.left :
|
||||
undefined;
|
||||
return !!identifier && !checker.getSymbolAtLocation(identifier);
|
||||
}
|
||||
|
||||
function isPossiblyPartOfCommaSeperatedInitializer(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.BinaryExpression:
|
||||
case SyntaxKind.CommaToken:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function expressionCouldBeVariableDeclaration(expression: Node, checker: TypeChecker): boolean {
|
||||
if (!isBinaryExpression(expression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expression.operatorToken.kind === SyntaxKind.CommaToken) {
|
||||
return every([expression.left, expression.right], expression => expressionCouldBeVariableDeclaration(expression, checker));
|
||||
}
|
||||
|
||||
return isIdentifier(expression.left) && !checker.getSymbolAtLocation(expression.left);
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@
|
||||
"codeFixProvider.ts",
|
||||
"refactorProvider.ts",
|
||||
"codefixes/addConvertToUnknownForNonOverlappingTypes.ts",
|
||||
"codefixes/addMissingConst.ts",
|
||||
"codefixes/addMissingInvocationForDecorator.ts",
|
||||
"codefixes/addNameToNamelessParameter.ts",
|
||||
"codefixes/annotateWithTypeFromJSDoc.ts",
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////for (x in []) {}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add 'const' to unresolved variable",
|
||||
newFileContent: "for (const x in []) {}"
|
||||
});
|
||||
12
tests/cases/fourslash/codeFixAddMissingConstInForInLoop2.ts
Normal file
12
tests/cases/fourslash/codeFixAddMissingConstInForInLoop2.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////for (x in []) {}
|
||||
////for (y in []) {}
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingConst",
|
||||
fixAllDescription: "Add 'const' to all unresolved variables",
|
||||
newFileContent:
|
||||
`for (const x in []) {}
|
||||
for (const y in []) {}`
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////for ([x] of [[1,2]]) {}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add 'const' to unresolved variable",
|
||||
newFileContent: "for (const [x] of [[1,2]]) {}"
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////for ([x, y] of [[1,2]]) {}
|
||||
////for ([x] of [[1,2]]) {}
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingConst",
|
||||
fixAllDescription: "Add 'const' to all unresolved variables",
|
||||
newFileContent:
|
||||
`for (const [x, y] of [[1,2]]) {}
|
||||
for (const [x] of [[1,2]]) {}`
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////for ({ x } of [{ x: 0 }]) { }
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add 'const' to unresolved variable",
|
||||
newFileContent: "for (const { x } of [{ x: 0 }]) { }"
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////for ({ x, y } of [{ x: 0, y: 1 }]) { }
|
||||
////for ({ x } of [{ x: 0 }]) { }
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingConst",
|
||||
fixAllDescription: "Add 'const' to all unresolved variables",
|
||||
newFileContent:
|
||||
`for (const { x, y } of [{ x: 0, y: 1 }]) { }
|
||||
for (const { x } of [{ x: 0 }]) { }`
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////for (x of []) {}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add 'const' to unresolved variable",
|
||||
newFileContent: "for (const x of []) {}"
|
||||
});
|
||||
12
tests/cases/fourslash/codeFixAddMissingConstInForOfLoop2.ts
Normal file
12
tests/cases/fourslash/codeFixAddMissingConstInForOfLoop2.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////for (x of []) {}
|
||||
////for (y of []) {}
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingConst",
|
||||
fixAllDescription: "Add 'const' to all unresolved variables",
|
||||
newFileContent:
|
||||
`for (const x of []) {}
|
||||
for (const y of []) {}`
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////a = () => {
|
||||
//// x = 0;
|
||||
//// [y] = [1];
|
||||
//// weirdlyIndented = 2;
|
||||
////};
|
||||
////b = 3;
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingConst",
|
||||
fixAllDescription: "Add 'const' to all unresolved variables",
|
||||
newFileContent: `const a = () => {
|
||||
const x = 0;
|
||||
const [y] = [1];
|
||||
const weirdlyIndented = 2;
|
||||
};
|
||||
const b = 3;`
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////a = () => {
|
||||
//// for (x in []) {
|
||||
//// y = 0;
|
||||
//// }
|
||||
////};
|
||||
////b = 3;
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingConst",
|
||||
fixAllDescription: "Add 'const' to all unresolved variables",
|
||||
newFileContent: `const a = () => {
|
||||
for (const x in []) {
|
||||
const y = 0;
|
||||
}
|
||||
};
|
||||
const b = 3;`
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////[x] = [0];
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add 'const' to unresolved variable",
|
||||
newFileContent: "const [x] = [0];"
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////[x, y] = [0, 1];
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingConst",
|
||||
fixAllDescription: "Add 'const' to all unresolved variables",
|
||||
newFileContent: "const [x, y] = [0, 1];"
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////let x: any;
|
||||
////[x, y] = [0, 1];
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
@@ -0,0 +1,5 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////[x, y()] = [0, () => 1];
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
@@ -0,0 +1,9 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////x = 0, y = 0;
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingConst",
|
||||
fixAllDescription: "Add 'const' to all unresolved variables",
|
||||
newFileContent: "const x = 0, y = 0;"
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////x = 0,
|
||||
////y = 0;
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingConst",
|
||||
fixAllDescription: "Add 'const' to all unresolved variables",
|
||||
newFileContent: `const x = 0,
|
||||
y = 0;`
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////function f() { return 42; }
|
||||
////x = 0, y = f()
|
||||
////
|
||||
////, z = 0;
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingConst",
|
||||
fixAllDescription: "Add 'const' to all unresolved variables",
|
||||
newFileContent: `function f() { return 42; }
|
||||
const x = 0, y = f()
|
||||
|
||||
, z = 0;`
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////let y: any;
|
||||
////x = 0, y = 0;
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
@@ -0,0 +1,8 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////x = 0;
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add 'const' to unresolved variable",
|
||||
newFileContent: "const x = 0;"
|
||||
});
|
||||
Reference in New Issue
Block a user