diff options
Diffstat (limited to 'tools/eslint/lib/rules/no-fallthrough.js')
-rw-r--r-- | tools/eslint/lib/rules/no-fallthrough.js | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/tools/eslint/lib/rules/no-fallthrough.js b/tools/eslint/lib/rules/no-fallthrough.js new file mode 100644 index 0000000000..a137fb3f80 --- /dev/null +++ b/tools/eslint/lib/rules/no-fallthrough.js @@ -0,0 +1,95 @@ +/** + * @fileoverview Rule to flag fall-through cases in switch statements. + * @author Matt DuVall <http://mattduvall.com/> + */ +"use strict"; + + +var FALLTHROUGH_COMMENT = /falls\sthrough/; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = function(context) { + + var switches = []; + + return { + + "SwitchCase": function(node) { + + var consequent = node.consequent, + switchData = switches[switches.length - 1], + i, + comments, + comment; + + /* + * Some developers wrap case bodies in blocks, so if there is just one + * node and it's a block statement, check inside. + */ + if (consequent.length === 1 && consequent[0].type === "BlockStatement") { + consequent = consequent[0]; + } + + // checking on previous case + if (!switchData.lastCaseClosed) { + + // a fall through comment will be the last trailing comment of the last case + comments = context.getComments(switchData.lastCase).trailing; + comment = comments[comments.length - 1]; + + // unless the user doesn't like semicolons, in which case it's first leading comment of this case + if (!comment) { + comments = context.getComments(node).leading; + comment = comments[comments.length - 1]; + } + + // check for comment + if (!comment || !FALLTHROUGH_COMMENT.test(comment.value)) { + + context.report(switchData.lastCase, + "Expected a \"break\" statement before \"{{code}}\".", + { code: node.test ? "case" : "default" }); + } + } + + // now dealing with the current case + switchData.lastCaseClosed = false; + switchData.lastCase = node; + + // try to verify using statements - go backwards as a fast path for the search + if (consequent.length) { + for (i = consequent.length - 1; i >= 0; i--) { + if (/(?:Break|Continue|Return|Throw)Statement/.test(consequent[i].type)) { + switchData.lastCaseClosed = true; + break; + } + } + } else { + // the case statement has no statements, so it must logically fall through + switchData.lastCaseClosed = true; + } + + /* + * Any warnings are triggered when the next SwitchCase occurs. + * There is no need to warn on the last SwitchCase, since it can't + * fall through to anything. + */ + }, + + "SwitchStatement": function(node) { + switches.push({ + node: node, + lastCaseClosed: true, + lastCase: null + }); + }, + + "SwitchStatement:exit": function() { + switches.pop(); + } + }; + +}; |