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