7edcd40d700358797ff085e1728881261e830524
[platform/framework/web/crosswalk-tizen.git] /
1 /**
2  * @fileoverview Rule to flag use of comma operator
3  * @author Brandon Mills
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Rule Definition
10 //------------------------------------------------------------------------------
11
12 module.exports = function(context) {
13
14     /**
15      * Parts of the grammar that are required to have parens.
16      */
17     var parenthesized = {
18         "DoWhileStatement": "test",
19         "IfStatement": "test",
20         "SwitchStatement": "discriminant",
21         "WhileStatement": "test",
22         "WithStatement": "object"
23
24         // Omitting CallExpression - commas are parsed as argument separators
25         // Omitting NewExpression - commas are parsed as argument separators
26         // Omitting ForInStatement - parts aren't individually parenthesised
27         // Omitting ForStatement - parts aren't individually parenthesised
28     };
29
30     /**
31      * Determines whether a node is required by the grammar to be wrapped in
32      * parens, e.g. the test of an if statement.
33      * @param {ASTNode} node - The AST node
34      * @returns {boolean} True if parens around node belong to parent node.
35      */
36     function requiresExtraParens(node) {
37         return node.parent && parenthesized[node.parent.type] != null &&
38                 node === node.parent[parenthesized[node.parent.type]];
39     }
40
41     /**
42      * Check if a node is wrapped in parens.
43      * @param {ASTNode} node - The AST node
44      * @returns {boolean} True if the node has a paren on each side.
45      */
46     function isParenthesised(node) {
47         var previousToken = context.getTokenBefore(node),
48             nextToken = context.getTokenAfter(node);
49
50         return previousToken && nextToken &&
51             previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
52             nextToken.value === ")" && nextToken.range[0] >= node.range[1];
53     }
54
55     /**
56      * Check if a node is wrapped in two levels of parens.
57      * @param {ASTNode} node - The AST node
58      * @returns {boolean} True if two parens surround the node on each side.
59      */
60     function isParenthesisedTwice(node) {
61         var previousToken = context.getTokenBefore(node, 1),
62             nextToken = context.getTokenAfter(node, 1);
63
64         return isParenthesised(node) && previousToken && nextToken &&
65             previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
66             nextToken.value === ")" && nextToken.range[0] >= node.range[1];
67     }
68
69     return {
70         "SequenceExpression": function(node) {
71             // Always allow sequences in for statement update
72             if (node.parent.type === "ForStatement" &&
73                     (node === node.parent.init || node === node.parent.update)) {
74                 return;
75             }
76
77             // Wrapping a sequence in extra parens indicates intent
78             if (requiresExtraParens(node)) {
79                 if (isParenthesisedTwice(node)) {
80                     return;
81                 }
82             } else {
83                 if (isParenthesised(node)) {
84                     return;
85                 }
86             }
87
88             context.report(node, "Unexpected use of comma operator.");
89         }
90     };
91
92 };