Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update vars-on-top for class static blocks #15306

Merged
merged 1 commit into from Nov 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 46 additions & 4 deletions docs/rules/vars-on-top.md
Expand Up @@ -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;
}
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down
37 changes: 25 additions & 12 deletions lib/rules/vars-on-top.js
Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -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" });
}

//--------------------------------------------------------------------------
Expand All @@ -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);
}
}
};
Expand Down
150 changes: 150 additions & 0 deletions tests/lib/rules/vars-on-top.js
Expand Up @@ -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
}
}
],

Expand Down Expand Up @@ -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]
}
]
});