Skip to content

Commit 90f78f4

Browse files
mysticateagyandeeps
authored andcommitted
Update: add props option to no-self-assign rule (fixes #6718) (#6721)
* Chore: add `getStaticPropertyName` to ast-utils * Update: add `props` option to `no-self-assign` rule (fixes #6718) * Chore: use `astUtils.getStaticProperty` for some places.
1 parent 30d71d6 commit 90f78f4

File tree

8 files changed

+384
-91
lines changed

8 files changed

+384
-91
lines changed

docs/rules/no-self-assign.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,52 @@ let foo = foo;
4141
[foo = 1] = [foo];
4242
```
4343

44+
## Options
45+
46+
This rule has the option to check properties as well.
47+
48+
```json
49+
{
50+
"no-self-assign": ["error", {"props": false}]
51+
}
52+
```
53+
54+
- `props` - if this is `true`, `no-self-assign` rule warns self-assignments of properties. Default is `false`.
55+
56+
### props
57+
58+
Examples of **incorrect** code for the `{ "props": true }` option:
59+
60+
```js
61+
/*eslint no-self-assign: [error, {props: true}]*/
62+
63+
// self-assignments with properties.
64+
obj.a = obj.a;
65+
obj.a.b = obj.a.b;
66+
obj["a"] = obj["a"];
67+
obj[a] = obj[a];
68+
```
69+
70+
Examples of **correct** code for the `{ "props": true }` option:
71+
72+
```js
73+
/*eslint no-self-assign: [error, {props: true}]*/
74+
75+
// non-self-assignments with properties.
76+
obj.a = obj.b;
77+
obj.a.b = obj.c.b;
78+
obj.a.b = obj.a.c;
79+
obj[a] = obj["a"]
80+
81+
// This ignores if there is a function call.
82+
obj.a().b = obj.a().b
83+
a().b = a().b
84+
85+
// Known limitation: this does not support computed properties except single literal or single identifier.
86+
obj[a + b] = obj[a + b];
87+
obj["a" + "b"] = obj["a" + "b"];
88+
```
89+
4490
## When Not To Use It
4591

4692
If you don't want to notify about self assignments, then it's safe to disable this rule.

lib/ast-utils.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,5 +584,74 @@ module.exports = {
584584
*/
585585
isFunction: function(node) {
586586
return Boolean(node && anyFunctionPattern.test(node.type));
587+
},
588+
589+
/**
590+
* Gets the property name of a given node.
591+
* The node can be a MemberExpression, a Property, or a MethodDefinition.
592+
*
593+
* If the name is dynamic, this returns `null`.
594+
*
595+
* For examples:
596+
*
597+
* a.b // => "b"
598+
* a["b"] // => "b"
599+
* a['b'] // => "b"
600+
* a[`b`] // => "b"
601+
* a[100] // => "100"
602+
* a[b] // => null
603+
* a["a" + "b"] // => null
604+
* a[tag`b`] // => null
605+
* a[`${b}`] // => null
606+
*
607+
* let a = {b: 1} // => "b"
608+
* let a = {["b"]: 1} // => "b"
609+
* let a = {['b']: 1} // => "b"
610+
* let a = {[`b`]: 1} // => "b"
611+
* let a = {[100]: 1} // => "100"
612+
* let a = {[b]: 1} // => null
613+
* let a = {["a" + "b"]: 1} // => null
614+
* let a = {[tag`b`]: 1} // => null
615+
* let a = {[`${b}`]: 1} // => null
616+
*
617+
* @param {ASTNode} node - The node to get.
618+
* @returns {string|null} The property name if static. Otherwise, null.
619+
*/
620+
getStaticPropertyName(node) {
621+
let prop;
622+
623+
switch (node && node.type) {
624+
case "Property":
625+
case "MethodDefinition":
626+
prop = node.key;
627+
break;
628+
629+
case "MemberExpression":
630+
prop = node.property;
631+
break;
632+
633+
// no default
634+
}
635+
636+
switch (prop && prop.type) {
637+
case "Literal":
638+
return String(prop.value);
639+
640+
case "TemplateLiteral":
641+
if (prop.expressions.length === 0 && prop.quasis.length === 1) {
642+
return prop.quasis[0].value.cooked;
643+
}
644+
break;
645+
646+
case "Identifier":
647+
if (!node.computed) {
648+
return prop.name;
649+
}
650+
break;
651+
652+
// no default
653+
}
654+
655+
return null;
587656
}
588657
};

lib/rules/array-callback-return.js

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -45,38 +45,6 @@ function getLocation(node, sourceCode) {
4545
return node.id || node;
4646
}
4747

48-
/**
49-
* Gets the name of a given node if the node is a Identifier node.
50-
*
51-
* @param {ASTNode} node - A node to get.
52-
* @returns {string} The name of the node, or an empty string.
53-
*/
54-
function getIdentifierName(node) {
55-
return node.type === "Identifier" ? node.name : "";
56-
}
57-
58-
/**
59-
* Gets the value of a given node if the node is a Literal node or a
60-
* TemplateLiteral node.
61-
*
62-
* @param {ASTNode} node - A node to get.
63-
* @returns {string} The value of the node, or an empty string.
64-
*/
65-
function getConstantStringValue(node) {
66-
switch (node.type) {
67-
case "Literal":
68-
return String(node.value);
69-
70-
case "TemplateLiteral":
71-
return node.expressions.length === 0
72-
? node.quasis[0].value.cooked
73-
: "";
74-
75-
default:
76-
return "";
77-
}
78-
}
79-
8048
/**
8149
* Checks a given node is a MemberExpression node which has the specified name's
8250
* property.
@@ -88,9 +56,7 @@ function getConstantStringValue(node) {
8856
function isTargetMethod(node) {
8957
return (
9058
node.type === "MemberExpression" &&
91-
TARGET_METHODS.test(
92-
(node.computed ? getConstantStringValue : getIdentifierName)(node.property)
93-
)
59+
TARGET_METHODS.test(astUtils.getStaticPropertyName(node) || "")
9460
);
9561
}
9662

lib/rules/no-alert.js

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
*/
55
"use strict";
66

7+
//------------------------------------------------------------------------------
8+
// Requirements
9+
//------------------------------------------------------------------------------
10+
11+
const getPropertyName = require("../ast-utils").getStaticPropertyName;
12+
713
//------------------------------------------------------------------------------
814
// Helpers
915
//------------------------------------------------------------------------------
@@ -28,22 +34,6 @@ function report(context, node, identifierName) {
2834
context.report(node, "Unexpected {{name}}.", { name: identifierName });
2935
}
3036

31-
/**
32-
* Returns the property name of a MemberExpression.
33-
* @param {ASTNode} memberExpressionNode The MemberExpression node.
34-
* @returns {string|null} Returns the property name if available, null else.
35-
*/
36-
function getPropertyName(memberExpressionNode) {
37-
if (memberExpressionNode.computed) {
38-
if (memberExpressionNode.property.type === "Literal") {
39-
return memberExpressionNode.property.value;
40-
}
41-
} else {
42-
return memberExpressionNode.property.name;
43-
}
44-
return null;
45-
}
46-
4737
/**
4838
* Finds the escope reference in the given scope.
4939
* @param {Object} scope The scope to search.

lib/rules/no-extra-bind.js

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
*/
55
"use strict";
66

7+
//------------------------------------------------------------------------------
8+
// Requirements
9+
//------------------------------------------------------------------------------
10+
11+
const getPropertyName = require("../ast-utils").getStaticPropertyName;
12+
713
//------------------------------------------------------------------------------
814
// Rule Definition
915
//------------------------------------------------------------------------------
@@ -37,31 +43,6 @@ module.exports = {
3743
});
3844
}
3945

40-
/**
41-
* Gets the property name of a given node.
42-
* If the property name is dynamic, this returns an empty string.
43-
*
44-
* @param {ASTNode} node - A node to check. This is a MemberExpression.
45-
* @returns {string} The property name of the node.
46-
*/
47-
function getPropertyName(node) {
48-
if (node.computed) {
49-
switch (node.property.type) {
50-
case "Literal":
51-
return String(node.property.value);
52-
case "TemplateLiteral":
53-
if (node.property.expressions.length === 0) {
54-
return node.property.quasis[0].value.cooked;
55-
}
56-
57-
// fallthrough
58-
default:
59-
return false;
60-
}
61-
}
62-
return node.property.name;
63-
}
64-
6546
/**
6647
* Checks whether or not a given function node is the callee of `.bind()`
6748
* method.

0 commit comments

Comments
 (0)