]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-lone-blocks.js
import 8.23.1 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/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
37 /**
38 * Reports a node as invalid.
39 * @param {ASTNode} node The node to be reported.
40 * @returns {void}
41 */
42 function report(node) {
43 const messageId = node.parent.type === "BlockStatement" || node.parent.type === "StaticBlock"
44 ? "redundantNestedBlock"
45 : "redundantBlock";
46
47 context.report({
48 node,
49 messageId
50 });
51 }
52
53 /**
54 * Checks for any occurrence of a BlockStatement in a place where lists of statements can appear
55 * @param {ASTNode} node The node to check
56 * @returns {boolean} True if the node is a lone block.
57 */
58 function isLoneBlock(node) {
59 return node.parent.type === "BlockStatement" ||
60 node.parent.type === "StaticBlock" ||
61 node.parent.type === "Program" ||
62
63 // Don't report blocks in switch cases if the block is the only statement of the case.
64 node.parent.type === "SwitchCase" && !(node.parent.consequent[0] === node && node.parent.consequent.length === 1);
65 }
66
67 /**
68 * Checks the enclosing block of the current node for block-level bindings,
69 * and "marks it" as valid if any.
70 * @returns {void}
71 */
72 function markLoneBlock() {
73 if (loneBlocks.length === 0) {
74 return;
75 }
76
77 const block = context.getAncestors().pop();
78
79 if (loneBlocks[loneBlocks.length - 1] === block) {
80 loneBlocks.pop();
81 }
82 }
83
84 // Default rule definition: report all lone blocks
85 ruleDef = {
86 BlockStatement(node) {
87 if (isLoneBlock(node)) {
88 report(node);
89 }
90 }
91 };
92
93 // ES6: report blocks without block-level bindings, or that's only child of another block
94 if (context.languageOptions.ecmaVersion >= 2015) {
95 ruleDef = {
96 BlockStatement(node) {
97 if (isLoneBlock(node)) {
98 loneBlocks.push(node);
99 }
100 },
101 "BlockStatement:exit"(node) {
102 if (loneBlocks.length > 0 && loneBlocks[loneBlocks.length - 1] === node) {
103 loneBlocks.pop();
104 report(node);
105 } else if (
106 (
107 node.parent.type === "BlockStatement" ||
108 node.parent.type === "StaticBlock"
109 ) &&
110 node.parent.body.length === 1
111 ) {
112 report(node);
113 }
114 }
115 };
116
117 ruleDef.VariableDeclaration = function(node) {
118 if (node.kind === "let" || node.kind === "const") {
119 markLoneBlock();
120 }
121 };
122
123 ruleDef.FunctionDeclaration = function() {
124 if (context.getScope().isStrict) {
125 markLoneBlock();
126 }
127 };
128
129 ruleDef.ClassDeclaration = markLoneBlock;
130 }
131
132 return ruleDef;
133 }
134 };