2 * @fileoverview Rule to flag `else` after a `return` in `if`
3 * @author Ian Christian Myers
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 module.exports = function(context) {
14 //--------------------------------------------------------------------------
16 //--------------------------------------------------------------------------
19 * Display the context report if rule is violated
21 * @param {Node} node The 'else' node
24 function displayReport(node) {
25 context.report(node, "Unexpected 'else' after 'return'.");
29 * Check to see if the node is a ReturnStatement
31 * @param {Node} node The node being evaluated
32 * @returns {boolean} True if node is a return
34 function checkForReturn(node) {
35 return node.type === "ReturnStatement";
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.
43 * @param {Node} node The consequent/alternate node
44 * @returns {boolean} True if it has a return
46 function naiveHasReturn(node) {
47 if (node.type === "BlockStatement") {
49 lastChildNode = body[body.length - 1];
51 return lastChildNode && checkForReturn(lastChildNode);
53 return checkForReturn(node);
57 * Check to see if the node is valid for evaluation,
58 * meaning it has an else and not an else-if
60 * @param {Node} node The node being evaluated
61 * @returns {boolean} True if the node is valid
63 function hasElse(node) {
64 return node.alternate && node.consequent && node.alternate.type !== "IfStatement";
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.
72 * @param {Node} node The consequent node
73 * @returns {boolean} True if this is a nested rule violation
75 function checkForIf(node) {
76 return node.type === "IfStatement" && hasElse(node) &&
77 naiveHasReturn(node.alternate) && naiveHasReturn(node.consequent);
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.
85 * @param {Node} node The consequent or body node
86 * @param {Node} alternate The alternate node
89 function checkForReturnOrIf(node, alternate) {
90 if (checkForReturn(node) || checkForIf(node)) {
91 displayReport(alternate);
95 //--------------------------------------------------------------------------
97 //--------------------------------------------------------------------------
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).
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);
113 // If not a block statement, make sure the consequent isn't a ReturnStatement
114 // or an IfStatement with returns on both paths
116 checkForReturnOrIf(consequent, alternate);