UNPKG

4.68 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag fall-through cases in switch statements.
3 * @author Matt DuVall <http://mattduvall.com/>
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Requirements
9//------------------------------------------------------------------------------
10
11const lodash = require("lodash");
12
13//------------------------------------------------------------------------------
14// Helpers
15//------------------------------------------------------------------------------
16
17const DEFAULT_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 * @param {RegExp} fallthroughCommentPattern - A pattern to match comment to.
24 * @returns {boolean} `true` if the node has a valid fallthrough comment.
25 */
26function hasFallthroughComment(node, context, fallthroughCommentPattern) {
27 const sourceCode = context.getSourceCode();
28 const comment = lodash.last(sourceCode.getComments(node).leading);
29
30 return Boolean(comment && fallthroughCommentPattern.test(comment.value));
31}
32
33/**
34 * Checks whether or not a given code path segment is reachable.
35 * @param {CodePathSegment} segment - A CodePathSegment to check.
36 * @returns {boolean} `true` if the segment is reachable.
37 */
38function isReachable(segment) {
39 return segment.reachable;
40}
41
42/**
43 * Checks whether a node and a token are separated by blank lines
44 * @param {ASTNode} node - The node to check
45 * @param {Token} token - The token to compare against
46 * @returns {boolean} `true` if there are blank lines between node and token
47 */
48function hasBlankLinesBetween(node, token) {
49 return token.loc.start.line > node.loc.end.line + 1;
50}
51
52//------------------------------------------------------------------------------
53// Rule Definition
54//------------------------------------------------------------------------------
55
56module.exports = {
57 meta: {
58 docs: {
59 description: "disallow fallthrough of `case` statements",
60 category: "Best Practices",
61 recommended: true
62 },
63
64 schema: [
65 {
66 type: "object",
67 properties: {
68 commentPattern: {
69 type: "string"
70 }
71 },
72 additionalProperties: false
73 }
74 ]
75 },
76
77 create(context) {
78 const options = context.options[0] || {};
79 let currentCodePath = null;
80 const sourceCode = context.getSourceCode();
81
82 /*
83 * We need to use leading comments of the next SwitchCase node because
84 * trailing comments is wrong if semicolons are omitted.
85 */
86 let fallthroughCase = null;
87 let fallthroughCommentPattern = null;
88
89 if (options.commentPattern) {
90 fallthroughCommentPattern = new RegExp(options.commentPattern);
91 } else {
92 fallthroughCommentPattern = DEFAULT_FALLTHROUGH_COMMENT;
93 }
94
95 return {
96 onCodePathStart(codePath) {
97 currentCodePath = codePath;
98 },
99 onCodePathEnd() {
100 currentCodePath = currentCodePath.upper;
101 },
102
103 SwitchCase(node) {
104
105 /*
106 * Checks whether or not there is a fallthrough comment.
107 * And reports the previous fallthrough node if that does not exist.
108 */
109 if (fallthroughCase && !hasFallthroughComment(node, context, fallthroughCommentPattern)) {
110 context.report({
111 message: "Expected a 'break' statement before '{{type}}'.",
112 data: {type: node.test ? "case" : "default"},
113 node
114 });
115 }
116 fallthroughCase = null;
117 },
118
119 "SwitchCase:exit"(node) {
120 const nextToken = sourceCode.getTokenAfter(node);
121
122 /*
123 * `reachable` meant fall through because statements preceded by
124 * `break`, `return`, or `throw` are unreachable.
125 * And allows empty cases and the last case.
126 */
127 if (currentCodePath.currentSegments.some(isReachable) &&
128 (node.consequent.length > 0 || hasBlankLinesBetween(node, nextToken)) &&
129 lodash.last(node.parent.cases) !== node) {
130 fallthroughCase = node;
131 }
132 }
133 };
134 }
135};