eslint 7.3.0 → 7.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -0
- package/README.md +5 -3
- package/lib/cli-engine/config-array-factory.js +1 -28
- package/lib/cli-engine/formatters/checkstyle.js +2 -2
- package/lib/linter/code-path-analysis/code-path-analyzer.js +38 -0
- package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
- package/lib/linter/code-path-analysis/code-path-state.js +59 -0
- package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
- package/lib/rule-tester/rule-tester.js +10 -0
- package/lib/rules/accessor-pairs.js +1 -14
- package/lib/rules/array-callback-return.js +26 -17
- package/lib/rules/arrow-body-style.js +43 -8
- package/lib/rules/arrow-parens.js +91 -108
- package/lib/rules/camelcase.js +47 -0
- package/lib/rules/consistent-return.js +1 -12
- package/lib/rules/constructor-super.js +1 -0
- package/lib/rules/dot-location.js +20 -14
- package/lib/rules/dot-notation.js +36 -33
- package/lib/rules/func-call-spacing.js +42 -6
- package/lib/rules/func-name-matching.js +1 -4
- package/lib/rules/global-require.js +2 -1
- package/lib/rules/id-blacklist.js +14 -11
- package/lib/rules/id-denylist.js +230 -0
- package/lib/rules/indent.js +19 -0
- package/lib/rules/index.js +1 -0
- package/lib/rules/keyword-spacing.js +2 -2
- package/lib/rules/max-len.js +13 -2
- package/lib/rules/new-cap.js +10 -14
- package/lib/rules/newline-per-chained-call.js +15 -5
- package/lib/rules/no-alert.js +10 -3
- package/lib/rules/no-duplicate-case.js +23 -4
- package/lib/rules/no-eval.js +8 -38
- package/lib/rules/no-extend-native.js +37 -40
- package/lib/rules/no-extra-bind.js +57 -17
- package/lib/rules/no-extra-boolean-cast.js +7 -0
- package/lib/rules/no-extra-parens.js +48 -10
- package/lib/rules/no-implicit-coercion.js +11 -6
- package/lib/rules/no-implied-eval.js +7 -28
- package/lib/rules/no-import-assign.js +33 -32
- package/lib/rules/no-irregular-whitespace.js +22 -12
- package/lib/rules/no-magic-numbers.js +4 -8
- package/lib/rules/no-obj-calls.js +7 -4
- package/lib/rules/no-prototype-builtins.js +13 -3
- package/lib/rules/no-self-assign.js +3 -53
- package/lib/rules/no-setter-return.js +5 -8
- package/lib/rules/no-unexpected-multiline.js +2 -2
- package/lib/rules/no-unneeded-ternary.js +0 -2
- package/lib/rules/no-unused-expressions.js +55 -23
- package/lib/rules/no-useless-call.js +10 -7
- package/lib/rules/no-whitespace-before-property.js +16 -4
- package/lib/rules/object-curly-newline.js +4 -4
- package/lib/rules/operator-assignment.js +3 -42
- package/lib/rules/padding-line-between-statements.js +2 -2
- package/lib/rules/prefer-arrow-callback.js +90 -25
- package/lib/rules/prefer-exponentiation-operator.js +1 -1
- package/lib/rules/prefer-numeric-literals.js +4 -13
- package/lib/rules/prefer-promise-reject-errors.js +1 -3
- package/lib/rules/prefer-regex-literals.js +68 -13
- package/lib/rules/prefer-spread.js +2 -6
- package/lib/rules/radix.js +5 -2
- package/lib/rules/sort-imports.js +28 -0
- package/lib/rules/use-isnan.js +1 -1
- package/lib/rules/utils/ast-utils.js +317 -153
- package/lib/rules/wrap-iife.js +9 -2
- package/lib/rules/yoda.js +2 -55
- package/messages/extend-config-missing.txt +1 -1
- package/messages/no-config-found.txt +1 -1
- package/messages/plugin-conflict.txt +1 -1
- package/messages/plugin-missing.txt +1 -1
- package/messages/whitespace-found.txt +1 -1
- package/package.json +6 -6
@@ -12,12 +12,6 @@
|
|
12
12
|
const astUtils = require("./utils/ast-utils");
|
13
13
|
const globals = require("globals");
|
14
14
|
|
15
|
-
//------------------------------------------------------------------------------
|
16
|
-
// Helpers
|
17
|
-
//------------------------------------------------------------------------------
|
18
|
-
|
19
|
-
const propertyDefinitionMethods = new Set(["defineProperty", "defineProperties"]);
|
20
|
-
|
21
15
|
//------------------------------------------------------------------------------
|
22
16
|
// Rule Definition
|
23
17
|
//------------------------------------------------------------------------------
|
@@ -100,40 +94,30 @@ module.exports = {
|
|
100
94
|
}
|
101
95
|
|
102
96
|
/**
|
103
|
-
*
|
104
|
-
*
|
105
|
-
*
|
106
|
-
* @
|
107
|
-
* @returns {boolean} True if the identifier's prototype is modified.
|
97
|
+
* Check if it's an assignment to the property of the given node.
|
98
|
+
* Example: `*.prop = 0` // the `*` is the given node.
|
99
|
+
* @param {ASTNode} node The node to check.
|
100
|
+
* @returns {boolean} True if an assignment to the property of the node.
|
108
101
|
*/
|
109
|
-
function
|
110
|
-
return
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
102
|
+
function isAssigningToPropertyOf(node) {
|
103
|
+
return (
|
104
|
+
node.parent.type === "MemberExpression" &&
|
105
|
+
node.parent.object === node &&
|
106
|
+
node.parent.parent.type === "AssignmentExpression" &&
|
107
|
+
node.parent.parent.left === node.parent
|
115
108
|
);
|
116
109
|
}
|
117
110
|
|
118
111
|
/**
|
119
|
-
* Checks
|
120
|
-
*
|
121
|
-
* Object.
|
122
|
-
* Example: Object.defineProperty(Array.prototype, "foo", ...)
|
123
|
-
* Example: Object.defineProperties(Array.prototype, ...)
|
124
|
-
* @param {ASTNode} identifierNode The identifier to check.
|
125
|
-
* @returns {boolean} True if the identifier's prototype is modified.
|
112
|
+
* Checks if the given node is at the first argument of the method call of `Object.defineProperty()` or `Object.defineProperties()`.
|
113
|
+
* @param {ASTNode} node The node to check.
|
114
|
+
* @returns {boolean} True if the node is at the first argument of the method call of `Object.defineProperty()` or `Object.defineProperties()`.
|
126
115
|
*/
|
127
|
-
function isInDefinePropertyCall(
|
128
|
-
return
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
identifierNode.parent.parent.callee.type === "MemberExpression" &&
|
133
|
-
identifierNode.parent.parent.callee.object.type === "Identifier" &&
|
134
|
-
identifierNode.parent.parent.callee.object.name === "Object" &&
|
135
|
-
identifierNode.parent.parent.callee.property.type === "Identifier" &&
|
136
|
-
propertyDefinitionMethods.has(identifierNode.parent.parent.callee.property.name)
|
116
|
+
function isInDefinePropertyCall(node) {
|
117
|
+
return (
|
118
|
+
node.parent.type === "CallExpression" &&
|
119
|
+
node.parent.arguments[0] === node &&
|
120
|
+
astUtils.isSpecificMemberAccess(node.parent.callee, "Object", /^definePropert(?:y|ies)$/u)
|
137
121
|
);
|
138
122
|
}
|
139
123
|
|
@@ -149,14 +133,27 @@ module.exports = {
|
|
149
133
|
* @returns {void}
|
150
134
|
*/
|
151
135
|
function checkAndReportPrototypeExtension(identifierNode) {
|
152
|
-
if (
|
136
|
+
if (!isPrototypePropertyAccessed(identifierNode)) {
|
137
|
+
return; // This is not `*.prototype` access.
|
138
|
+
}
|
139
|
+
|
140
|
+
/*
|
141
|
+
* `identifierNode.parent` is a MamberExpression `*.prototype`.
|
142
|
+
* If it's an optional member access, it may be wrapped by a `ChainExpression` node.
|
143
|
+
*/
|
144
|
+
const prototypeNode =
|
145
|
+
identifierNode.parent.parent.type === "ChainExpression"
|
146
|
+
? identifierNode.parent.parent
|
147
|
+
: identifierNode.parent;
|
148
|
+
|
149
|
+
if (isAssigningToPropertyOf(prototypeNode)) {
|
153
150
|
|
154
|
-
//
|
155
|
-
reportNode(
|
156
|
-
} else if (isInDefinePropertyCall(
|
151
|
+
// `*.prototype` -> MemberExpression -> AssignmentExpression
|
152
|
+
reportNode(prototypeNode.parent.parent, identifierNode.name);
|
153
|
+
} else if (isInDefinePropertyCall(prototypeNode)) {
|
157
154
|
|
158
|
-
//
|
159
|
-
reportNode(
|
155
|
+
// `*.prototype` -> CallExpression
|
156
|
+
reportNode(prototypeNode.parent, identifierNode.name);
|
160
157
|
}
|
161
158
|
}
|
162
159
|
|
@@ -61,24 +61,62 @@ module.exports = {
|
|
61
61
|
* @returns {void}
|
62
62
|
*/
|
63
63
|
function report(node) {
|
64
|
+
const memberNode = node.parent;
|
65
|
+
const callNode = memberNode.parent.type === "ChainExpression"
|
66
|
+
? memberNode.parent.parent
|
67
|
+
: memberNode.parent;
|
68
|
+
|
64
69
|
context.report({
|
65
|
-
node:
|
70
|
+
node: callNode,
|
66
71
|
messageId: "unexpected",
|
67
|
-
loc:
|
72
|
+
loc: memberNode.property.loc,
|
73
|
+
|
68
74
|
fix(fixer) {
|
69
|
-
if (
|
75
|
+
if (!isSideEffectFree(callNode.arguments[0])) {
|
70
76
|
return null;
|
71
77
|
}
|
72
78
|
|
73
|
-
|
74
|
-
|
75
|
-
|
79
|
+
/*
|
80
|
+
* The list of the first/last token pair of a removal range.
|
81
|
+
* This is two parts because closing parentheses may exist between the method name and arguments.
|
82
|
+
* E.g. `(function(){}.bind ) (obj)`
|
83
|
+
* ^^^^^ ^^^^^ < removal ranges
|
84
|
+
* E.g. `(function(){}?.['bind'] ) ?.(obj)`
|
85
|
+
* ^^^^^^^^^^ ^^^^^^^ < removal ranges
|
86
|
+
*/
|
87
|
+
const tokenPairs = [
|
88
|
+
[
|
89
|
+
|
90
|
+
// `.`, `?.`, or `[` token.
|
91
|
+
sourceCode.getTokenAfter(
|
92
|
+
memberNode.object,
|
93
|
+
astUtils.isNotClosingParenToken
|
94
|
+
),
|
95
|
+
|
96
|
+
// property name or `]` token.
|
97
|
+
sourceCode.getLastToken(memberNode)
|
98
|
+
],
|
99
|
+
[
|
100
|
+
|
101
|
+
// `?.` or `(` token of arguments.
|
102
|
+
sourceCode.getTokenAfter(
|
103
|
+
memberNode,
|
104
|
+
astUtils.isNotClosingParenToken
|
105
|
+
),
|
106
|
+
|
107
|
+
// `)` token of arguments.
|
108
|
+
sourceCode.getLastToken(callNode)
|
109
|
+
]
|
110
|
+
];
|
111
|
+
const firstTokenToRemove = tokenPairs[0][0];
|
112
|
+
const lastTokenToRemove = tokenPairs[1][1];
|
76
113
|
|
77
114
|
if (sourceCode.commentsExistBetween(firstTokenToRemove, lastTokenToRemove)) {
|
78
115
|
return null;
|
79
116
|
}
|
80
117
|
|
81
|
-
return
|
118
|
+
return tokenPairs.map(([start, end]) =>
|
119
|
+
fixer.removeRange([start.range[0], end.range[1]]));
|
82
120
|
}
|
83
121
|
});
|
84
122
|
}
|
@@ -93,18 +131,20 @@ module.exports = {
|
|
93
131
|
* @returns {boolean} `true` if the node is the callee of `.bind()` method.
|
94
132
|
*/
|
95
133
|
function isCalleeOfBindMethod(node) {
|
96
|
-
|
97
|
-
|
134
|
+
if (!astUtils.isSpecificMemberAccess(node.parent, null, "bind")) {
|
135
|
+
return false;
|
136
|
+
}
|
137
|
+
|
138
|
+
// The node of `*.bind` member access.
|
139
|
+
const bindNode = node.parent.parent.type === "ChainExpression"
|
140
|
+
? node.parent.parent
|
141
|
+
: node.parent;
|
98
142
|
|
99
143
|
return (
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
grandparent.arguments[0].type !== "SpreadElement" &&
|
105
|
-
parent.type === "MemberExpression" &&
|
106
|
-
parent.object === node &&
|
107
|
-
astUtils.getStaticPropertyName(parent) === "bind"
|
144
|
+
bindNode.parent.type === "CallExpression" &&
|
145
|
+
bindNode.parent.callee === bindNode &&
|
146
|
+
bindNode.parent.arguments.length === 1 &&
|
147
|
+
bindNode.parent.arguments[0].type !== "SpreadElement"
|
108
148
|
);
|
109
149
|
}
|
110
150
|
|
@@ -111,6 +111,10 @@ module.exports = {
|
|
111
111
|
* @returns {boolean} If the node is in one of the flagged contexts
|
112
112
|
*/
|
113
113
|
function isInFlaggedContext(node) {
|
114
|
+
if (node.parent.type === "ChainExpression") {
|
115
|
+
return isInFlaggedContext(node.parent);
|
116
|
+
}
|
117
|
+
|
114
118
|
return isInBooleanContext(node) ||
|
115
119
|
(isLogicalContext(node.parent) &&
|
116
120
|
|
@@ -149,6 +153,9 @@ module.exports = {
|
|
149
153
|
* @returns {boolean} `true` if the node needs to be parenthesized.
|
150
154
|
*/
|
151
155
|
function needsParens(previousNode, node) {
|
156
|
+
if (previousNode.parent.type === "ChainExpression") {
|
157
|
+
return needsParens(previousNode.parent, node);
|
158
|
+
}
|
152
159
|
if (isParenthesized(previousNode)) {
|
153
160
|
|
154
161
|
// parentheses around the previous node will stay, so there is no need for an additional pair
|
@@ -100,10 +100,18 @@ module.exports = {
|
|
100
100
|
* @private
|
101
101
|
*/
|
102
102
|
function isImmediateFunctionPrototypeMethodCall(node) {
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
103
|
+
const callNode = astUtils.skipChainExpression(node);
|
104
|
+
|
105
|
+
if (callNode.type !== "CallExpression") {
|
106
|
+
return false;
|
107
|
+
}
|
108
|
+
const callee = astUtils.skipChainExpression(callNode.callee);
|
109
|
+
|
110
|
+
return (
|
111
|
+
callee.type === "MemberExpression" &&
|
112
|
+
callee.object.type === "FunctionExpression" &&
|
113
|
+
["call", "apply"].includes(astUtils.getStaticPropertyName(callee))
|
114
|
+
);
|
107
115
|
}
|
108
116
|
|
109
117
|
/**
|
@@ -360,7 +368,9 @@ module.exports = {
|
|
360
368
|
* @returns {boolean} `true` if the given node is an IIFE
|
361
369
|
*/
|
362
370
|
function isIIFE(node) {
|
363
|
-
|
371
|
+
const maybeCallNode = astUtils.skipChainExpression(node);
|
372
|
+
|
373
|
+
return maybeCallNode.type === "CallExpression" && maybeCallNode.callee.type === "FunctionExpression";
|
364
374
|
}
|
365
375
|
|
366
376
|
/**
|
@@ -466,13 +476,16 @@ module.exports = {
|
|
466
476
|
|
467
477
|
if (
|
468
478
|
hasDoubleExcessParens(callee) ||
|
469
|
-
!isIIFE(node) &&
|
479
|
+
!isIIFE(node) &&
|
480
|
+
!hasNewParensException &&
|
481
|
+
!(
|
470
482
|
|
471
483
|
// Allow extra parens around a new expression if they are intervening parentheses.
|
472
484
|
node.type === "NewExpression" &&
|
473
485
|
callee.type === "MemberExpression" &&
|
474
486
|
doesMemberExpressionContainCallExpression(callee)
|
475
|
-
)
|
487
|
+
) &&
|
488
|
+
!(!node.optional && callee.type === "ChainExpression")
|
476
489
|
) {
|
477
490
|
report(node.callee);
|
478
491
|
}
|
@@ -710,6 +723,20 @@ module.exports = {
|
|
710
723
|
reportsBuffer.reports = reportsBuffer.reports.filter(r => r.node !== node);
|
711
724
|
}
|
712
725
|
|
726
|
+
/**
|
727
|
+
* Checks whether a node is a MemberExpression at NewExpression's callee.
|
728
|
+
* @param {ASTNode} node node to check.
|
729
|
+
* @returns {boolean} True if the node is a MemberExpression at NewExpression's callee. false otherwise.
|
730
|
+
*/
|
731
|
+
function isMemberExpInNewCallee(node) {
|
732
|
+
if (node.type === "MemberExpression") {
|
733
|
+
return node.parent.type === "NewExpression" && node.parent.callee === node
|
734
|
+
? true
|
735
|
+
: node.parent.object === node && isMemberExpInNewCallee(node.parent);
|
736
|
+
}
|
737
|
+
return false;
|
738
|
+
}
|
739
|
+
|
713
740
|
return {
|
714
741
|
ArrayExpression(node) {
|
715
742
|
node.elements
|
@@ -950,7 +977,11 @@ module.exports = {
|
|
950
977
|
LogicalExpression: checkBinaryLogical,
|
951
978
|
|
952
979
|
MemberExpression(node) {
|
953
|
-
const
|
980
|
+
const shouldAllowWrapOnce = isMemberExpInNewCallee(node) &&
|
981
|
+
doesMemberExpressionContainCallExpression(node);
|
982
|
+
const nodeObjHasExcessParens = shouldAllowWrapOnce
|
983
|
+
? hasDoubleExcessParens(node.object)
|
984
|
+
: hasExcessParens(node.object) &&
|
954
985
|
!(
|
955
986
|
isImmediateFunctionPrototypeMethodCall(node.parent) &&
|
956
987
|
node.parent.callee === node &&
|
@@ -974,8 +1005,8 @@ module.exports = {
|
|
974
1005
|
}
|
975
1006
|
|
976
1007
|
if (nodeObjHasExcessParens &&
|
977
|
-
node.object.type === "CallExpression"
|
978
|
-
|
1008
|
+
node.object.type === "CallExpression"
|
1009
|
+
) {
|
979
1010
|
report(node.object);
|
980
1011
|
}
|
981
1012
|
|
@@ -986,6 +1017,13 @@ module.exports = {
|
|
986
1017
|
report(node.object);
|
987
1018
|
}
|
988
1019
|
|
1020
|
+
if (nodeObjHasExcessParens &&
|
1021
|
+
node.optional &&
|
1022
|
+
node.object.type === "ChainExpression"
|
1023
|
+
) {
|
1024
|
+
report(node.object);
|
1025
|
+
}
|
1026
|
+
|
989
1027
|
if (node.computed && hasExcessParens(node.property)) {
|
990
1028
|
report(node.property);
|
991
1029
|
}
|
@@ -47,12 +47,14 @@ function isDoubleLogicalNegating(node) {
|
|
47
47
|
* @returns {boolean} Whether or not the node is a binary negating of `.indexOf()` method calling.
|
48
48
|
*/
|
49
49
|
function isBinaryNegatingOfIndexOf(node) {
|
50
|
+
if (node.operator !== "~") {
|
51
|
+
return false;
|
52
|
+
}
|
53
|
+
const callNode = astUtils.skipChainExpression(node.argument);
|
54
|
+
|
50
55
|
return (
|
51
|
-
|
52
|
-
|
53
|
-
node.argument.callee.type === "MemberExpression" &&
|
54
|
-
node.argument.callee.property.type === "Identifier" &&
|
55
|
-
INDEX_OF_PATTERN.test(node.argument.callee.property.name)
|
56
|
+
callNode.type === "CallExpression" &&
|
57
|
+
astUtils.isSpecificMemberAccess(callNode.callee, null, INDEX_OF_PATTERN)
|
56
58
|
);
|
57
59
|
}
|
58
60
|
|
@@ -246,7 +248,10 @@ module.exports = {
|
|
246
248
|
// ~foo.indexOf(bar)
|
247
249
|
operatorAllowed = options.allow.indexOf("~") >= 0;
|
248
250
|
if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) {
|
249
|
-
|
251
|
+
|
252
|
+
// `foo?.indexOf(bar) !== -1` will be true (== found) if the `foo` is nullish. So use `>= 0` in that case.
|
253
|
+
const comparison = node.argument.type === "ChainExpression" ? ">= 0" : "!== -1";
|
254
|
+
const recommendation = `${sourceCode.getText(node.argument)} ${comparison}`;
|
250
255
|
|
251
256
|
report(node, recommendation, false);
|
252
257
|
}
|
@@ -35,8 +35,8 @@ module.exports = {
|
|
35
35
|
},
|
36
36
|
|
37
37
|
create(context) {
|
38
|
-
const EVAL_LIKE_FUNCS = Object.freeze(["setTimeout", "execScript", "setInterval"]);
|
39
38
|
const GLOBAL_CANDIDATES = Object.freeze(["global", "window", "globalThis"]);
|
39
|
+
const EVAL_LIKE_FUNC_PATTERN = /^(?:set(?:Interval|Timeout)|execScript)$/u;
|
40
40
|
|
41
41
|
/**
|
42
42
|
* Checks whether a node is evaluated as a string or not.
|
@@ -56,28 +56,6 @@ module.exports = {
|
|
56
56
|
return false;
|
57
57
|
}
|
58
58
|
|
59
|
-
/**
|
60
|
-
* Checks whether a node is an Identifier node named one of the specified names.
|
61
|
-
* @param {ASTNode} node A node to check.
|
62
|
-
* @param {string[]} specifiers Array of specified name.
|
63
|
-
* @returns {boolean} True if the node is a Identifier node which has specified name.
|
64
|
-
*/
|
65
|
-
function isSpecifiedIdentifier(node, specifiers) {
|
66
|
-
return node.type === "Identifier" && specifiers.includes(node.name);
|
67
|
-
}
|
68
|
-
|
69
|
-
/**
|
70
|
-
* Checks a given node is a MemberExpression node which has the specified name's
|
71
|
-
* property.
|
72
|
-
* @param {ASTNode} node A node to check.
|
73
|
-
* @param {string[]} specifiers Array of specified name.
|
74
|
-
* @returns {boolean} `true` if the node is a MemberExpression node which has
|
75
|
-
* the specified name's property
|
76
|
-
*/
|
77
|
-
function isSpecifiedMember(node, specifiers) {
|
78
|
-
return node.type === "MemberExpression" && specifiers.includes(astUtils.getStaticPropertyName(node));
|
79
|
-
}
|
80
|
-
|
81
59
|
/**
|
82
60
|
* Reports if the `CallExpression` node has evaluated argument.
|
83
61
|
* @param {ASTNode} node A CallExpression to check.
|
@@ -114,14 +92,15 @@ module.exports = {
|
|
114
92
|
const identifier = ref.identifier;
|
115
93
|
let node = identifier.parent;
|
116
94
|
|
117
|
-
while (
|
95
|
+
while (astUtils.isSpecificMemberAccess(node, null, name)) {
|
118
96
|
node = node.parent;
|
119
97
|
}
|
120
98
|
|
121
|
-
if (
|
122
|
-
const
|
99
|
+
if (astUtils.isSpecificMemberAccess(node, null, EVAL_LIKE_FUNC_PATTERN)) {
|
100
|
+
const calleeNode = node.parent.type === "ChainExpression" ? node.parent : node;
|
101
|
+
const parent = calleeNode.parent;
|
123
102
|
|
124
|
-
if (parent.type === "CallExpression" && parent.callee ===
|
103
|
+
if (parent.type === "CallExpression" && parent.callee === calleeNode) {
|
125
104
|
reportImpliedEvalCallExpression(parent);
|
126
105
|
}
|
127
106
|
}
|
@@ -134,7 +113,7 @@ module.exports = {
|
|
134
113
|
|
135
114
|
return {
|
136
115
|
CallExpression(node) {
|
137
|
-
if (
|
116
|
+
if (astUtils.isSpecificId(node.callee, EVAL_LIKE_FUNC_PATTERN)) {
|
138
117
|
reportImpliedEvalCallExpression(node);
|
139
118
|
}
|
140
119
|
},
|
@@ -9,16 +9,12 @@
|
|
9
9
|
// Helpers
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
|
-
const { findVariable
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
]),
|
19
|
-
Reflect: new Set([
|
20
|
-
"defineProperty", "deleteProperty", "set", "setPrototypeOf"
|
21
|
-
])
|
12
|
+
const { findVariable } = require("eslint-utils");
|
13
|
+
const astUtils = require("./utils/ast-utils");
|
14
|
+
|
15
|
+
const WellKnownMutationFunctions = {
|
16
|
+
Object: /^(?:assign|definePropert(?:y|ies)|freeze|setPrototypeOf)$/u,
|
17
|
+
Reflect: /^(?:(?:define|delete)Property|set(?:PrototypeOf)?)$/u
|
22
18
|
};
|
23
19
|
|
24
20
|
/**
|
@@ -56,17 +52,20 @@ function isAssignmentLeft(node) {
|
|
56
52
|
* @returns {boolean} `true` if the node is the operand of mutation unary operator.
|
57
53
|
*/
|
58
54
|
function isOperandOfMutationUnaryOperator(node) {
|
59
|
-
const
|
55
|
+
const argumentNode = node.parent.type === "ChainExpression"
|
56
|
+
? node.parent
|
57
|
+
: node;
|
58
|
+
const { parent } = argumentNode;
|
60
59
|
|
61
60
|
return (
|
62
61
|
(
|
63
62
|
parent.type === "UpdateExpression" &&
|
64
|
-
parent.argument ===
|
63
|
+
parent.argument === argumentNode
|
65
64
|
) ||
|
66
65
|
(
|
67
66
|
parent.type === "UnaryExpression" &&
|
68
67
|
parent.operator === "delete" &&
|
69
|
-
parent.argument ===
|
68
|
+
parent.argument === argumentNode
|
70
69
|
)
|
71
70
|
);
|
72
71
|
}
|
@@ -92,35 +91,37 @@ function isIterationVariable(node) {
|
|
92
91
|
}
|
93
92
|
|
94
93
|
/**
|
95
|
-
* Check if a given node is the
|
94
|
+
* Check if a given node is at the first argument of a well-known mutation function.
|
95
|
+
* - `Object.assign`
|
96
|
+
* - `Object.defineProperty`
|
97
|
+
* - `Object.defineProperties`
|
98
|
+
* - `Object.freeze`
|
99
|
+
* - `Object.setPrototypeOf`
|
100
|
+
* - `Refrect.defineProperty`
|
101
|
+
* - `Refrect.deleteProperty`
|
102
|
+
* - `Refrect.set`
|
103
|
+
* - `Refrect.setPrototypeOf`
|
96
104
|
* @param {ASTNode} node The node to check.
|
97
105
|
* @param {Scope} scope A `escope.Scope` object to find variable (whichever).
|
98
|
-
* @returns {boolean} `true` if the node is the
|
106
|
+
* @returns {boolean} `true` if the node is at the first argument of a well-known mutation function.
|
99
107
|
*/
|
100
108
|
function isArgumentOfWellKnownMutationFunction(node, scope) {
|
101
109
|
const { parent } = node;
|
102
110
|
|
111
|
+
if (parent.type !== "CallExpression" || parent.arguments[0] !== node) {
|
112
|
+
return false;
|
113
|
+
}
|
114
|
+
const callee = astUtils.skipChainExpression(parent.callee);
|
115
|
+
|
103
116
|
if (
|
104
|
-
|
105
|
-
|
106
|
-
parent.callee.type === "MemberExpression" &&
|
107
|
-
parent.callee.object.type === "Identifier"
|
117
|
+
!astUtils.isSpecificMemberAccess(callee, "Object", WellKnownMutationFunctions.Object) &&
|
118
|
+
!astUtils.isSpecificMemberAccess(callee, "Reflect", WellKnownMutationFunctions.Reflect)
|
108
119
|
) {
|
109
|
-
|
110
|
-
const { object } = callee;
|
111
|
-
|
112
|
-
if (Object.keys(MutationMethods).includes(object.name)) {
|
113
|
-
const variable = findVariable(scope, object);
|
114
|
-
|
115
|
-
return (
|
116
|
-
variable !== null &&
|
117
|
-
variable.scope.type === "global" &&
|
118
|
-
MutationMethods[object.name].has(getPropertyName(callee, scope))
|
119
|
-
);
|
120
|
-
}
|
120
|
+
return false;
|
121
121
|
}
|
122
|
+
const variable = findVariable(scope, callee.object);
|
122
123
|
|
123
|
-
return
|
124
|
+
return variable !== null && variable.scope.type === "global";
|
124
125
|
}
|
125
126
|
|
126
127
|
/**
|
@@ -91,7 +91,7 @@ module.exports = {
|
|
91
91
|
const locStart = node.loc.start;
|
92
92
|
const locEnd = node.loc.end;
|
93
93
|
|
94
|
-
errors = errors.filter(({ loc: errorLoc }) => {
|
94
|
+
errors = errors.filter(({ loc: { start: errorLoc } }) => {
|
95
95
|
if (errorLoc.line >= locStart.line && errorLoc.line <= locEnd.line) {
|
96
96
|
if (errorLoc.column >= locStart.column && (errorLoc.column <= locEnd.column || errorLoc.line < locEnd.line)) {
|
97
97
|
return false;
|
@@ -160,15 +160,19 @@ module.exports = {
|
|
160
160
|
let match;
|
161
161
|
|
162
162
|
while ((match = IRREGULAR_WHITESPACE.exec(sourceLine)) !== null) {
|
163
|
-
const location = {
|
164
|
-
line: lineNumber,
|
165
|
-
column: match.index
|
166
|
-
};
|
167
|
-
|
168
163
|
errors.push({
|
169
164
|
node,
|
170
165
|
messageId: "noIrregularWhitespace",
|
171
|
-
loc:
|
166
|
+
loc: {
|
167
|
+
start: {
|
168
|
+
line: lineNumber,
|
169
|
+
column: match.index
|
170
|
+
},
|
171
|
+
end: {
|
172
|
+
line: lineNumber,
|
173
|
+
column: match.index + match[0].length
|
174
|
+
}
|
175
|
+
}
|
172
176
|
});
|
173
177
|
}
|
174
178
|
});
|
@@ -189,16 +193,22 @@ module.exports = {
|
|
189
193
|
|
190
194
|
while ((match = IRREGULAR_LINE_TERMINATORS.exec(source)) !== null) {
|
191
195
|
const lineIndex = linebreaks.indexOf(match[0], lastLineIndex + 1) || 0;
|
192
|
-
const location = {
|
193
|
-
line: lineIndex + 1,
|
194
|
-
column: sourceLines[lineIndex].length
|
195
|
-
};
|
196
196
|
|
197
197
|
errors.push({
|
198
198
|
node,
|
199
199
|
messageId: "noIrregularWhitespace",
|
200
|
-
loc:
|
200
|
+
loc: {
|
201
|
+
start: {
|
202
|
+
line: lineIndex + 1,
|
203
|
+
column: sourceLines[lineIndex].length
|
204
|
+
},
|
205
|
+
end: {
|
206
|
+
line: lineIndex + 2,
|
207
|
+
column: 0
|
208
|
+
}
|
209
|
+
}
|
201
210
|
});
|
211
|
+
|
202
212
|
lastLineIndex = lineIndex;
|
203
213
|
}
|
204
214
|
}
|
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
-
const
|
8
|
+
const astUtils = require("./utils/ast-utils");
|
9
9
|
|
10
10
|
// Maximum array length by the ECMAScript Specification.
|
11
11
|
const MAX_ARRAY_LENGTH = 2 ** 32 - 1;
|
@@ -100,12 +100,8 @@ module.exports = {
|
|
100
100
|
|
101
101
|
return parent.type === "CallExpression" && fullNumberNode === parent.arguments[1] &&
|
102
102
|
(
|
103
|
-
parent.callee
|
104
|
-
(
|
105
|
-
parent.callee.type === "MemberExpression" &&
|
106
|
-
parent.callee.object.name === "Number" &&
|
107
|
-
parent.callee.property.name === "parseInt"
|
108
|
-
)
|
103
|
+
astUtils.isSpecificId(parent.callee, "parseInt") ||
|
104
|
+
astUtils.isSpecificMemberAccess(parent.callee, "Number", "parseInt")
|
109
105
|
);
|
110
106
|
}
|
111
107
|
|
@@ -157,7 +153,7 @@ module.exports = {
|
|
157
153
|
|
158
154
|
return {
|
159
155
|
Literal(node) {
|
160
|
-
if (!isNumericLiteral(node)) {
|
156
|
+
if (!astUtils.isNumericLiteral(node)) {
|
161
157
|
return;
|
162
158
|
}
|
163
159
|
|