From 8aa764524cf74f0b70d184c7957dbbb5f36a5ac7 Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Sun, 21 Nov 2021 06:40:43 +0100 Subject: [PATCH] fix: update vars-on-top for class static blocks (#15306) Fixes the `vars-on-top` rule to allow `var` declarations at the top of class static blocks. Refs #15016 --- docs/rules/vars-on-top.md | 50 ++++++++++- lib/rules/vars-on-top.js | 37 +++++--- tests/lib/rules/vars-on-top.js | 150 +++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+), 16 deletions(-) diff --git a/docs/rules/vars-on-top.md b/docs/rules/vars-on-top.md index 78a8bee86b6..c8ce81e8191 100644 --- a/docs/rules/vars-on-top.md +++ b/docs/rules/vars-on-top.md @@ -14,11 +14,10 @@ Examples of **incorrect** code for this rule: ```js /*eslint vars-on-top: "error"*/ -// Variable declarations in a block: +// Variable declaration in a nested block, and a variable declaration after other statements: function doSomething() { - var first; if (true) { - first = true; + var first = true; } var second; } @@ -32,11 +31,34 @@ function doSomething() { ```js /*eslint vars-on-top: "error"*/ -// Variables after other statements: +// Variable declaration after other statements: f(); var a; ``` +```js +/*eslint vars-on-top: "error"*/ + +// Variables in class static blocks should be at the top of the static blocks. + +class C { + + // Variable declaration in a nested block: + static { + if (something) { + var a = true; + } + } + + // Variable declaration after other statements: + static { + f(); + var a; + } + +} +``` + Examples of **correct** code for this rule: ```js @@ -66,6 +88,26 @@ f(); ```js /*eslint vars-on-top: "error"*/ +class C { + + static { + var a; + if (something) { + a = true; + } + } + + static { + var a; + f(); + } + +} +``` + +```js +/*eslint vars-on-top: "error"*/ + // Directives may precede variable declarations. "use strict"; var a; diff --git a/lib/rules/vars-on-top.js b/lib/rules/vars-on-top.js index 6f913dcad9d..0f95d58dbc6 100644 --- a/lib/rules/vars-on-top.js +++ b/lib/rules/vars-on-top.js @@ -77,10 +77,12 @@ module.exports = { const l = statements.length; let i = 0; - // skip over directives - for (; i < l; ++i) { - if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) { - break; + // Skip over directives and imports. Static blocks don't have either. + if (node.parent.type !== "StaticBlock") { + for (; i < l; ++i) { + if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) { + break; + } } } @@ -111,16 +113,27 @@ module.exports = { /** * Checks whether variable is on top at functional block scope level * @param {ASTNode} node The node to check - * @param {ASTNode} parent Parent of the node - * @param {ASTNode} grandParent Parent of the node's parent * @returns {void} */ - function blockScopeVarCheck(node, parent, grandParent) { - if (!(/Function/u.test(grandParent.type) && - parent.type === "BlockStatement" && - isVarOnTop(node, parent.body))) { - context.report({ node, messageId: "top" }); + function blockScopeVarCheck(node) { + const { parent } = node; + + if ( + parent.type === "BlockStatement" && + /Function/u.test(parent.parent.type) && + isVarOnTop(node, parent.body) + ) { + return; } + + if ( + parent.type === "StaticBlock" && + isVarOnTop(node, parent.body) + ) { + return; + } + + context.report({ node, messageId: "top" }); } //-------------------------------------------------------------------------- @@ -134,7 +147,7 @@ module.exports = { } else if (node.parent.type === "Program") { globalVarCheck(node, node.parent); } else { - blockScopeVarCheck(node, node.parent, node.parent.parent); + blockScopeVarCheck(node); } } }; diff --git a/tests/lib/rules/vars-on-top.js b/tests/lib/rules/vars-on-top.js index 92e1a0d1cc0..e2b1bb689c5 100644 --- a/tests/lib/rules/vars-on-top.js +++ b/tests/lib/rules/vars-on-top.js @@ -191,6 +191,84 @@ ruleTester.run("vars-on-top", rule, { ecmaVersion: 6, sourceType: "module" } + }, + { + code: [ + "class C {", + " static {", + " var x;", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + } + }, + { + code: [ + "class C {", + " static {", + " var x;", + " foo();", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + } + }, + { + code: [ + "class C {", + " static {", + " var x;", + " var y;", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + } + }, + { + code: [ + "class C {", + " static {", + " var x;", + " var y;", + " foo();", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + } + }, + { + code: [ + "class C {", + " static {", + " let x;", + " var y;", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + } + }, + { + code: [ + "class C {", + " static {", + " foo();", + " let x;", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + } } ], @@ -427,6 +505,78 @@ ruleTester.run("vars-on-top", rule, { sourceType: "module" }, errors: [error] + }, + { + code: [ + "class C {", + " static {", + " foo();", + " var x;", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + }, + errors: [error] + }, + { + code: [ + "class C {", + " static {", + " 'use strict';", // static blocks do not have directives + " var x;", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + }, + errors: [error] + }, + { + code: [ + "class C {", + " static {", + " var x;", + " foo();", + " var y;", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + }, + errors: [{ ...error, line: 5 }] + }, + { + code: [ + "class C {", + " static {", + " if (foo) {", + " var x;", + " }", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + }, + errors: [error] + }, + { + code: [ + "class C {", + " static {", + " if (foo)", + " var x;", + " }", + "}" + ].join("\n"), + parserOptions: { + ecmaVersion: 2022 + }, + errors: [error] } ] });