]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-lone-blocks.js
import 8.41.0 source
[pve-eslint.git] / eslint / lib / rules / no-lone-blocks.js
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
12 /** @type {import('../shared/types').Rule} */
13 module.exports = {
14 meta: {
15 type: "suggestion",
16
17 docs: {
18 description: "Disallow unnecessary nested blocks",
19 recommended: false,
20 url: "https://eslint.org/docs/latest/rules/no-lone-blocks"
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;
36 const sourceCode = context.sourceCode;
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) {
44 const messageId = node.parent.type === "BlockStatement" || node.parent.type === "StaticBlock"
45 ? "redundantNestedBlock"
46 : "redundantBlock";
47
48 context.report({
49 node,
50 messageId
51 });
52 }
53
54 /**
55 * Checks for any occurrence of a BlockStatement in a place where lists of statements can appear
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" ||
61 node.parent.type === "StaticBlock" ||
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.
71 * @param {ASTNode} node The current node to check.
72 * @returns {void}
73 */
74 function markLoneBlock(node) {
75 if (loneBlocks.length === 0) {
76 return;
77 }
78
79 const block = node.parent;
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
96 if (context.languageOptions.ecmaVersion >= 2015) {
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 (
108 (
109 node.parent.type === "BlockStatement" ||
110 node.parent.type === "StaticBlock"
111 ) &&
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") {
121 markLoneBlock(node);
122 }
123 };
124
125 ruleDef.FunctionDeclaration = function(node) {
126 if (sourceCode.getScope(node).isStrict) {
127 markLoneBlock(node);
128 }
129 };
130
131 ruleDef.ClassDeclaration = markLoneBlock;
132 }
133
134 return ruleDef;
135 }
136 };