forked from microsoft/TypeScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaddMissingConst.ts
More file actions
111 lines (97 loc) · 4.77 KB
/
addMissingConst.ts
File metadata and controls
111 lines (97 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/* @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 Set<Node>();
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?: Set<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) && parent.operatorToken.kind === SyntaxKind.EqualsToken && 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?: Set<Node>) {
if (!fixedNodes || tryAddToSet(fixedNodes, 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 expression.operatorToken.kind === SyntaxKind.EqualsToken
&& isIdentifier(expression.left)
&& !checker.getSymbolAtLocation(expression.left);
}
}