/** * @fileoverview Rule to flag fall-through cases in switch statements. * @author Matt DuVall */ "use strict"; //------------------------------------------------------------------------------ // Requirements //------------------------------------------------------------------------------ var getLast = require("../util").getLast; //------------------------------------------------------------------------------ // Helpers //------------------------------------------------------------------------------ var FALLTHROUGH_COMMENT = /falls?\s?through/i; /** * Checks whether or not a given node has a fallthrough comment. * @param {ASTNode} node - A SwitchCase node to get comments. * @param {RuleContext} context - A rule context which stores comments. * @returns {boolean} `true` if the node has a fallthrough comment. */ function hasFallthroughComment(node, context) { var sourceCode = context.getSourceCode(); var comment = getLast(sourceCode.getComments(node).leading); return Boolean(comment && FALLTHROUGH_COMMENT.test(comment.value)); } /** * Checks whether or not a given code path segment is reachable. * @param {CodePathSegment} segment - A CodePathSegment to check. * @returns {boolean} `true` if the segment is reachable. */ function isReachable(segment) { return segment.reachable; } //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ module.exports = function(context) { var currentCodePath = null; // We need to use leading comments of the next SwitchCase node because // trailing comments is wrong if semicolons are omitted. var fallthroughCase = null; return { "onCodePathStart": function(codePath) { currentCodePath = codePath; }, "onCodePathEnd": function() { currentCodePath = currentCodePath.upper; }, "SwitchCase": function(node) { // Checks whether or not there is a fallthrough comment. // And reports the previous fallthrough node if that does not exist. if (fallthroughCase && !hasFallthroughComment(node, context)) { context.report({ message: "Expected a 'break' statement before '{{type}}'.", data: {type: node.test ? "case" : "default"}, node: node }); } fallthroughCase = null; }, "SwitchCase:exit": function(node) { // `reachable` meant fall through because statements preceded by // `break`, `return`, or `throw` are unreachable. // And allows empty cases and the last case. if (currentCodePath.currentSegments.some(isReachable) && node.consequent.length > 0 && getLast(node.parent.cases) !== node ) { fallthroughCase = node; } } }; }; module.exports.schema = [];