]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/rules/no-lone-blocks.js
import 8.41.0 source
[pve-eslint.git] / eslint / lib / rules / no-lone-blocks.js
CommitLineData
eb39fafa
DC
1/**
2 * @fileoverview Rule to flag blocks with no reason to exist
3 * @author Brandon Mills
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
34eeec05 12/** @type {import('../shared/types').Rule} */
eb39fafa
DC
13module.exports = {
14 meta: {
15 type: "suggestion",
16
17 docs: {
8f9d1d4d 18 description: "Disallow unnecessary nested blocks",
eb39fafa 19 recommended: false,
f2a92ac6 20 url: "https://eslint.org/docs/latest/rules/no-lone-blocks"
eb39fafa
DC
21 },
22
23 schema: [],
24
25 messages: {
26 redundantBlock: "Block is redundant.",
27 redundantNestedBlock: "Nested block is redundant."
28 }
29 },
30
31 create(context) {
32
33 // A stack of lone blocks to be checked for block-level bindings
34 const loneBlocks = [];
35 let ruleDef;
f2a92ac6 36 const sourceCode = context.sourceCode;
eb39fafa
DC
37
38 /**
39 * Reports a node as invalid.
40 * @param {ASTNode} node The node to be reported.
41 * @returns {void}
42 */
43 function report(node) {
609c276f
TL
44 const messageId = node.parent.type === "BlockStatement" || node.parent.type === "StaticBlock"
45 ? "redundantNestedBlock"
46 : "redundantBlock";
eb39fafa
DC
47
48 context.report({
49 node,
50 messageId
51 });
52 }
53
54 /**
56c4a2cb 55 * Checks for any occurrence of a BlockStatement in a place where lists of statements can appear
eb39fafa
DC
56 * @param {ASTNode} node The node to check
57 * @returns {boolean} True if the node is a lone block.
58 */
59 function isLoneBlock(node) {
60 return node.parent.type === "BlockStatement" ||
609c276f 61 node.parent.type === "StaticBlock" ||
eb39fafa
DC
62 node.parent.type === "Program" ||
63
64 // Don't report blocks in switch cases if the block is the only statement of the case.
65 node.parent.type === "SwitchCase" && !(node.parent.consequent[0] === node && node.parent.consequent.length === 1);
66 }
67
68 /**
69 * Checks the enclosing block of the current node for block-level bindings,
70 * and "marks it" as valid if any.
f2a92ac6 71 * @param {ASTNode} node The current node to check.
eb39fafa
DC
72 * @returns {void}
73 */
f2a92ac6 74 function markLoneBlock(node) {
eb39fafa
DC
75 if (loneBlocks.length === 0) {
76 return;
77 }
78
f2a92ac6 79 const block = node.parent;
eb39fafa
DC
80
81 if (loneBlocks[loneBlocks.length - 1] === block) {
82 loneBlocks.pop();
83 }
84 }
85
86 // Default rule definition: report all lone blocks
87 ruleDef = {
88 BlockStatement(node) {
89 if (isLoneBlock(node)) {
90 report(node);
91 }
92 }
93 };
94
95 // ES6: report blocks without block-level bindings, or that's only child of another block
8f9d1d4d 96 if (context.languageOptions.ecmaVersion >= 2015) {
eb39fafa
DC
97 ruleDef = {
98 BlockStatement(node) {
99 if (isLoneBlock(node)) {
100 loneBlocks.push(node);
101 }
102 },
103 "BlockStatement:exit"(node) {
104 if (loneBlocks.length > 0 && loneBlocks[loneBlocks.length - 1] === node) {
105 loneBlocks.pop();
106 report(node);
107 } else if (
609c276f
TL
108 (
109 node.parent.type === "BlockStatement" ||
110 node.parent.type === "StaticBlock"
111 ) &&
eb39fafa
DC
112 node.parent.body.length === 1
113 ) {
114 report(node);
115 }
116 }
117 };
118
119 ruleDef.VariableDeclaration = function(node) {
120 if (node.kind === "let" || node.kind === "const") {
f2a92ac6 121 markLoneBlock(node);
eb39fafa
DC
122 }
123 };
124
f2a92ac6
DC
125 ruleDef.FunctionDeclaration = function(node) {
126 if (sourceCode.getScope(node).isStrict) {
127 markLoneBlock(node);
eb39fafa
DC
128 }
129 };
130
131 ruleDef.ClassDeclaration = markLoneBlock;
132 }
133
134 return ruleDef;
135 }
136};