summaryrefslogtreecommitdiff
path: root/tools/eslint/lib/rules/no-fallthrough.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/eslint/lib/rules/no-fallthrough.js')
-rw-r--r--tools/eslint/lib/rules/no-fallthrough.js95
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();
+ }
+ };
+
+};