forked from eslint-community/eslint-plugin-eslint-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfixer-return.js
157 lines (139 loc) · 4.7 KB
/
fixer-return.js
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
* @fileoverview require fixer functions to return a fix
* @author 薛定谔的猫<[email protected]>
*/
'use strict';
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const utils = require('../utils');
const { getStaticValue } = require('eslint-utils');
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'require fixer functions to return a fix',
category: 'Possible Errors',
recommended: true,
},
fixable: null,
schema: [],
messages: {
missingFix: 'Fixer function never returned a fix.',
},
},
create (context) {
let funcInfo = {
upper: null,
codePath: null,
hasReturnWithFixer: false,
hasYieldWithFixer: false,
shouldCheck: false,
node: null,
};
let contextIdentifiers;
/**
* As we exit the fix() function, ensure we have returned or yielded a real fix by this point.
* If not, report the function as a violation.
*
* @param {ASTNode} node - A node to check.
* @param {Location} loc - Optional location to report violation on.
* @returns {void}
*/
function ensureFunctionReturnedFix (node, loc = (node.id || node).loc.start) {
if (
(node.generator && !funcInfo.hasYieldWithFixer) || // Generator function never yielded a fix
(!node.generator && !funcInfo.hasReturnWithFixer) // Non-generator function never returned a fix
) {
context.report({
node,
loc,
messageId: 'missingFix',
});
}
}
/**
* Check if a returned/yielded node is likely to be a fix or not.
* A fix is an object created by fixer.replaceText() for example and returned by the fix function.
* @param {ASTNode} node - node to check
* @param {Context} context
* @returns {boolean}
*/
function isFix (node) {
if (node.type === 'ArrayExpression' && node.elements.length === 0) {
// An empty array is not a fix.
return false;
}
const staticValue = getStaticValue(node, context.getScope());
if (!staticValue) {
// If we can't find a static value, assume it's a real fix value.
return true;
}
if (Array.isArray(staticValue.value)) {
// If the static value is an array, then consider it a fix since fixes could have been added to it after creation.
return true;
}
// Any other static values (booleans, numbers, etc) are not fixes.
return false;
}
return {
Program (node) {
contextIdentifiers = utils.getContextIdentifiers(context, node);
},
// Stacks this function's information.
onCodePathStart (codePath, node) {
funcInfo = {
upper: funcInfo,
codePath,
hasYieldWithFixer: false,
hasReturnWithFixer: false,
shouldCheck: utils.isAutoFixerFunction(node, contextIdentifiers) || utils.isSuggestionFixerFunction(node, contextIdentifiers),
node,
};
},
// Pops this function's information.
onCodePathEnd () {
funcInfo = funcInfo.upper;
},
// Yield in generators
YieldExpression (node) {
if (funcInfo.shouldCheck && node.argument && isFix(node.argument)) {
funcInfo.hasYieldWithFixer = true;
}
},
// Checks the return statement is valid.
ReturnStatement (node) {
if (funcInfo.shouldCheck && node.argument && isFix(node.argument)) {
funcInfo.hasReturnWithFixer = true;
}
},
// Ensure the current fixer function returned or yielded a fix.
'FunctionExpression:exit' (node) {
if (funcInfo.shouldCheck) {
ensureFunctionReturnedFix(node);
}
},
// Ensure the current (arrow) fixer function returned a fix.
'ArrowFunctionExpression:exit' (node) {
if (funcInfo.shouldCheck) {
const loc = context.getSourceCode().getTokenBefore(node.body).loc; // Show violation on arrow (=>).
if (node.expression) {
// When the return is implied (no curly braces around the body), we have to check the single body node directly.
if (!isFix(node.body)) {
context.report({
node,
loc,
messageId: 'missingFix',
});
}
} else {
ensureFunctionReturnedFix(node, loc);
}
}
},
};
},
};