2496095365781f97b0da913e786c2ae3f2b99f7b
[platform/framework/web/crosswalk-tizen.git] /
1 /**
2  * @fileoverview Rule to flag `else` after a `return` in `if`
3  * @author Ian Christian Myers
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Rule Definition
10 //------------------------------------------------------------------------------
11
12 module.exports = function(context) {
13
14     //--------------------------------------------------------------------------
15     // Helpers
16     //--------------------------------------------------------------------------
17
18     /**
19      * Display the context report if rule is violated
20      *
21      * @param {Node} node The 'else' node
22      * @returns {void}
23      */
24     function displayReport(node) {
25         context.report(node, "Unexpected 'else' after 'return'.");
26     }
27
28     /**
29      * Check to see if the node is a ReturnStatement
30      *
31      * @param {Node} node The node being evaluated
32      * @returns {boolean} True if node is a return
33      */
34     function checkForReturn(node) {
35         return node.type === "ReturnStatement";
36     }
37
38     /**
39      * Naive return checking, does not iterate through the whole
40      * BlockStatement because we make the assumption that the ReturnStatement
41      * will be the last node in the body of the BlockStatement.
42      *
43      * @param {Node} node The consequent/alternate node
44      * @returns {boolean} True if it has a return
45      */
46     function naiveHasReturn(node) {
47         if (node.type === "BlockStatement") {
48             var body = node.body,
49                 lastChildNode = body[body.length - 1];
50
51             return lastChildNode && checkForReturn(lastChildNode);
52         }
53         return checkForReturn(node);
54     }
55
56     /**
57      * Check to see if the node is valid for evaluation,
58      * meaning it has an else and not an else-if
59      *
60      * @param {Node} node The node being evaluated
61      * @returns {boolean} True if the node is valid
62      */
63     function hasElse(node) {
64         return node.alternate && node.consequent && node.alternate.type !== "IfStatement";
65     }
66
67     /**
68      * If the consequent is an IfStatement, check to see if it has an else
69      * and both its consequent and alternate path return, meaning this is
70      * a nested case of rule violation.  If-Else not considered currently.
71      *
72      * @param {Node} node The consequent node
73      * @returns {boolean} True if this is a nested rule violation
74      */
75     function checkForIf(node) {
76         return node.type === "IfStatement" && hasElse(node) &&
77             naiveHasReturn(node.alternate) && naiveHasReturn(node.consequent);
78     }
79
80     /**
81      * Check the consequent/body node to make sure it is not
82      * a ReturnStatement or an IfStatement that returns on both
83      * code paths.  If it is, display the context report.
84      *
85      * @param {Node} node The consequent or body node
86      * @param {Node} alternate The alternate node
87      * @returns {void}
88      */
89     function checkForReturnOrIf(node, alternate) {
90         if (checkForReturn(node) || checkForIf(node)) {
91             displayReport(alternate);
92         }
93     }
94
95     //--------------------------------------------------------------------------
96     // Public API
97     //--------------------------------------------------------------------------
98
99     return {
100
101         "IfStatement": function (node) {
102             // Don't bother finding a ReturnStatement, if there's no `else`
103             // or if the alternate is also an if (indicating an else if).
104             if (hasElse(node)) {
105                 var consequent = node.consequent,
106                     alternate = node.alternate;
107                 // If we have a BlockStatement, check each consequent body node.
108                 if (consequent.type === "BlockStatement") {
109                     var body = consequent.body;
110                     body.forEach(function (bodyNode) {
111                         checkForReturnOrIf(bodyNode, alternate);
112                     });
113                 // If not a block statement, make sure the consequent isn't a ReturnStatement
114                 // or an IfStatement with returns on both paths
115                 } else {
116                     checkForReturnOrIf(consequent, alternate);
117                 }
118             }
119         }
120
121     };
122
123 };