]>
Commit | Line | Data |
---|---|---|
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 |
13 | module.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 | }; |