3efb2cd372c0f52d46deefe5e3c4c0d05ada0bce
[platform/framework/web/crosswalk-tizen.git] /
1 /**
2  * @fileoverview Rule to enforce consistent naming of "this" context variables
3  * @author Raphael Pigulla
4  * @copyright 2015 Timothy Jones. All rights reserved.
5  */
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Rule Definition
10 //------------------------------------------------------------------------------
11
12 module.exports = function(context) {
13     var alias = context.options[0];
14
15     /**
16      * Reports that a variable declarator or assignment expression is assigning
17      * a non-'this' value to the specified alias.
18      * @param {ASTNode} node - The assigning node.
19      * @returns {void}
20      */
21     function reportBadAssignment(node) {
22         context.report(node,
23             "Designated alias '{{alias}}' is not assigned to 'this'.",
24             { alias: alias });
25     }
26
27     /**
28      * Checks that an assignment to an identifier only assigns 'this' to the
29      * appropriate alias, and the alias is only assigned to 'this'.
30      * @param {ASTNode} node - The assigning node.
31      * @param {Identifier} name - The name of the variable assigned to.
32      * @param {Expression} value - The value of the assignment.
33      * @returns {void}
34      */
35     function checkAssignment(node, name, value) {
36         var isThis = value.type === "ThisExpression";
37
38         if (name === alias) {
39             if (!isThis || node.operator && node.operator !== "=") {
40                 reportBadAssignment(node);
41             }
42         } else if (isThis) {
43             context.report(node,
44                 "Unexpected alias '{{name}}' for 'this'.", { name: name });
45         }
46     }
47
48     /**
49      * Ensures that a variable declaration of the alias in a program or function
50      * is assigned to the correct value.
51      * @returns {void}
52      */
53     function ensureWasAssigned() {
54         var scope = context.getScope();
55
56         scope.variables.some(function (variable) {
57             var lookup;
58
59             if (variable.name === alias) {
60                 if (variable.defs.some(function (def) {
61                     return def.node.type === "VariableDeclarator" &&
62                         def.node.init !== null;
63                 })) {
64                     return true;
65                 }
66
67                 lookup = scope.type === "global" ? scope : variable;
68
69                 // The alias has been declared and not assigned: check it was
70                 // assigned later in the same scope.
71                 if (!lookup.references.some(function (reference) {
72                     var write = reference.writeExpr;
73
74                     if (reference.from === scope &&
75                             write && write.type === "ThisExpression" &&
76                             write.parent.operator === "=") {
77                         return true;
78                     }
79                 })) {
80                     variable.defs.map(function (def) {
81                         return def.node;
82                     }).forEach(reportBadAssignment);
83                 }
84
85                 return true;
86             }
87         });
88     }
89
90     return {
91         "Program:exit": ensureWasAssigned,
92         "FunctionExpression:exit": ensureWasAssigned,
93         "FunctionDeclaration:exit": ensureWasAssigned,
94
95         "VariableDeclarator": function (node) {
96             if (node.init !== null) {
97                 checkAssignment(node, node.id.name, node.init);
98             }
99         },
100
101         "AssignmentExpression": function (node) {
102             if (node.left.type === "Identifier") {
103                 checkAssignment(node, node.left.name, node.right);
104             }
105         }
106     };
107
108 };