7ebb74d2bae24638789b064cdbef7af164021251
[platform/framework/web/crosswalk-tizen.git] /
1 /**
2  * @fileoverview Rule to replace assignment expressions with operator assignment
3  * @author Brandon Mills
4  * @copyright 2014 Brandon Mills. All rights reserved.
5  */
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Helpers
10 //------------------------------------------------------------------------------
11
12 /**
13  * Checks whether an operator is commutative and has an operator assignment
14  * shorthand form.
15  * @param   {string}  operator Operator to check.
16  * @returns {boolean}          True if the operator is commutative and has a
17  *     shorthand form.
18  */
19 function isCommutativeOperatorWithShorthand(operator) {
20     return ["*", "&", "^", "|"].indexOf(operator) >= 0;
21 }
22
23 /**
24  * Checks whether an operator is not commuatative and has an operator assignment
25  * shorthand form.
26  * @param   {string}  operator Operator to check.
27  * @returns {boolean}          True if the operator is not commuatative and has
28  *     a shorthand form.
29  */
30 function isNonCommutativeOperatorWithShorthand(operator) {
31     return ["+", "-", "/", "%", "<<", ">>", ">>>"].indexOf(operator) >= 0;
32 }
33
34 //------------------------------------------------------------------------------
35 // Rule Definition
36 //------------------------------------------------------------------------------
37
38 /**
39  * Checks whether two expressions reference the same value. For example:
40  *     a = a
41  *     a.b = a.b
42  *     a[0] = a[0]
43  *     a['b'] = a['b']
44  * @param   {ASTNode} a Left side of the comparison.
45  * @param   {ASTNode} b Right side of the comparison.
46  * @returns {boolean}   True if both sides match and reference the same value.
47  */
48 function same(a, b) {
49     if (a.type !== b.type) {
50         return false;
51     }
52
53     switch (a.type) {
54         case "Identifier":
55             return a.name === b.name;
56         case "Literal":
57             return a.value === b.value;
58         case "MemberExpression":
59             // x[0] = x[0]
60             // x[y] = x[y]
61             // x.y = x.y
62             return same(a.object, b.object) && same(a.property, b.property);
63         default:
64             return false;
65     }
66 }
67
68 module.exports = function(context) {
69
70     /**
71      * Ensures that an assignment uses the shorthand form where possible.
72      * @param   {ASTNode} node An AssignmentExpression node.
73      * @returns {void}
74      */
75     function verify(node) {
76         var expr, left, operator;
77
78         if (node.operator !== "=" || node.right.type !== "BinaryExpression") {
79             return;
80         }
81
82         left = node.left;
83         expr = node.right;
84         operator = expr.operator;
85
86         if (isCommutativeOperatorWithShorthand(operator)) {
87             if (same(left, expr.left) || same(left, expr.right)) {
88                 context.report(node, "Assignment can be replaced with operator assignment.");
89             }
90         } else if (isNonCommutativeOperatorWithShorthand(operator)) {
91             if (same(left, expr.left)) {
92                 context.report(node, "Assignment can be replaced with operator assignment.");
93             }
94         }
95     }
96
97     /**
98      * Warns if an assignment expression uses operator assignment shorthand.
99      * @param   {ASTNode} node An AssignmentExpression node.
100      * @returns {void}
101      */
102     function prohibit(node) {
103         if (node.operator !== "=") {
104             context.report(node, "Unexpected operator assignment shorthand.");
105         }
106     }
107
108     return {
109         "AssignmentExpression": context.options[0] !== "never" ? verify : prohibit
110     };
111
112 };