1 | /**
|
2 | * @fileoverview Rule to flag fall-through cases in switch statements.
|
3 | * @author Matt DuVall <http://mattduvall.com/>
|
4 | */
|
5 | ;
|
6 |
|
7 | //------------------------------------------------------------------------------
|
8 | // Requirements
|
9 | //------------------------------------------------------------------------------
|
10 |
|
11 | var getLast = require("../util").getLast;
|
12 |
|
13 | //------------------------------------------------------------------------------
|
14 | // Helpers
|
15 | //------------------------------------------------------------------------------
|
16 |
|
17 | var FALLTHROUGH_COMMENT = /falls?\s?through/i;
|
18 |
|
19 | /**
|
20 | * Checks whether or not a given node has a fallthrough comment.
|
21 | * @param {ASTNode} node - A SwitchCase node to get comments.
|
22 | * @param {RuleContext} context - A rule context which stores comments.
|
23 | * @returns {boolean} `true` if the node has a fallthrough comment.
|
24 | */
|
25 | function hasFallthroughComment(node, context) {
|
26 | var sourceCode = context.getSourceCode();
|
27 | var comment = getLast(sourceCode.getComments(node).leading);
|
28 |
|
29 | return Boolean(comment && FALLTHROUGH_COMMENT.test(comment.value));
|
30 | }
|
31 |
|
32 | /**
|
33 | * Checks whether or not a given code path segment is reachable.
|
34 | * @param {CodePathSegment} segment - A CodePathSegment to check.
|
35 | * @returns {boolean} `true` if the segment is reachable.
|
36 | */
|
37 | function isReachable(segment) {
|
38 | return segment.reachable;
|
39 | }
|
40 |
|
41 | //------------------------------------------------------------------------------
|
42 | // Rule Definition
|
43 | //------------------------------------------------------------------------------
|
44 |
|
45 | module.exports = function(context) {
|
46 | var currentCodePath = null;
|
47 |
|
48 | // We need to use leading comments of the next SwitchCase node because
|
49 | // trailing comments is wrong if semicolons are omitted.
|
50 | var fallthroughCase = null;
|
51 |
|
52 | return {
|
53 | "onCodePathStart": function(codePath) {
|
54 | currentCodePath = codePath;
|
55 | },
|
56 | "onCodePathEnd": function() {
|
57 | currentCodePath = currentCodePath.upper;
|
58 | },
|
59 |
|
60 | "SwitchCase": function(node) {
|
61 | // Checks whether or not there is a fallthrough comment.
|
62 | // And reports the previous fallthrough node if that does not exist.
|
63 | if (fallthroughCase && !hasFallthroughComment(node, context)) {
|
64 | context.report({
|
65 | message: "Expected a 'break' statement before '{{type}}'.",
|
66 | data: {type: node.test ? "case" : "default"},
|
67 | node: node
|
68 | });
|
69 | }
|
70 | fallthroughCase = null;
|
71 | },
|
72 |
|
73 | "SwitchCase:exit": function(node) {
|
74 | // `reachable` meant fall through because statements preceded by
|
75 | // `break`, `return`, or `throw` are unreachable.
|
76 | // And allows empty cases and the last case.
|
77 | if (currentCodePath.currentSegments.some(isReachable) &&
|
78 | node.consequent.length > 0 &&
|
79 | getLast(node.parent.cases) !== node
|
80 | ) {
|
81 | fallthroughCase = node;
|
82 | }
|
83 | }
|
84 | };
|
85 | };
|
86 |
|
87 | module.exports.schema = [];
|