123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- "use strict";
- const { directivesPattern } = require("../shared/directives");
- const DEFAULT_FALLTHROUGH_COMMENT = /falls?\s?through/iu;
- function isAnySegmentReachable(segments) {
- for (const segment of segments) {
- if (segment.reachable) {
- return true;
- }
- }
- return false;
- }
- function isFallThroughComment(comment, fallthroughCommentPattern) {
- return fallthroughCommentPattern.test(comment) && !directivesPattern.test(comment.trim());
- }
- function hasFallthroughComment(caseWhichFallsThrough, subsequentCase, context, fallthroughCommentPattern) {
- const sourceCode = context.sourceCode;
- if (caseWhichFallsThrough.consequent.length === 1 && caseWhichFallsThrough.consequent[0].type === "BlockStatement") {
- const trailingCloseBrace = sourceCode.getLastToken(caseWhichFallsThrough.consequent[0]);
- const commentInBlock = sourceCode.getCommentsBefore(trailingCloseBrace).pop();
- if (commentInBlock && isFallThroughComment(commentInBlock.value, fallthroughCommentPattern)) {
- return true;
- }
- }
- const comment = sourceCode.getCommentsBefore(subsequentCase).pop();
- return Boolean(comment && isFallThroughComment(comment.value, fallthroughCommentPattern));
- }
- function hasBlankLinesBetween(node, token) {
- return token.loc.start.line > node.loc.end.line + 1;
- }
- module.exports = {
- meta: {
- type: "problem",
- docs: {
- description: "Disallow fallthrough of `case` statements",
- recommended: true,
- url: "https://eslint.org/docs/latest/rules/no-fallthrough"
- },
- schema: [
- {
- type: "object",
- properties: {
- commentPattern: {
- type: "string",
- default: ""
- },
- allowEmptyCase: {
- type: "boolean",
- default: false
- }
- },
- additionalProperties: false
- }
- ],
- messages: {
- case: "Expected a 'break' statement before 'case'.",
- default: "Expected a 'break' statement before 'default'."
- }
- },
- create(context) {
- const options = context.options[0] || {};
- const codePathSegments = [];
- let currentCodePathSegments = new Set();
- const sourceCode = context.sourceCode;
- const allowEmptyCase = options.allowEmptyCase || false;
-
- let fallthroughCase = null;
- let fallthroughCommentPattern = null;
- if (options.commentPattern) {
- fallthroughCommentPattern = new RegExp(options.commentPattern, "u");
- } else {
- fallthroughCommentPattern = DEFAULT_FALLTHROUGH_COMMENT;
- }
- return {
- onCodePathStart() {
- codePathSegments.push(currentCodePathSegments);
- currentCodePathSegments = new Set();
- },
- onCodePathEnd() {
- currentCodePathSegments = codePathSegments.pop();
- },
- onUnreachableCodePathSegmentStart(segment) {
- currentCodePathSegments.add(segment);
- },
- onUnreachableCodePathSegmentEnd(segment) {
- currentCodePathSegments.delete(segment);
- },
- onCodePathSegmentStart(segment) {
- currentCodePathSegments.add(segment);
- },
- onCodePathSegmentEnd(segment) {
- currentCodePathSegments.delete(segment);
- },
- SwitchCase(node) {
-
- if (fallthroughCase && (!hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern))) {
- context.report({
- messageId: node.test ? "case" : "default",
- node
- });
- }
- fallthroughCase = null;
- },
- "SwitchCase:exit"(node) {
- const nextToken = sourceCode.getTokenAfter(node);
-
- if (isAnySegmentReachable(currentCodePathSegments) &&
- (node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) &&
- node.parent.cases[node.parent.cases.length - 1] !== node) {
- fallthroughCase = node;
- }
- }
- };
- }
- };
|