Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat: update the complexity rule for class static blocks (#15328)
Updates the `complexity` rule to apply to class static blocks.

Refs #15016
  • Loading branch information
mdjermanovic committed Nov 19, 2021
1 parent 3530337 commit 194f36d
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 7 deletions.
20 changes: 19 additions & 1 deletion docs/rules/complexity.md
Expand Up @@ -57,7 +57,7 @@ function b() {
}
```

Class field initializers are implicit functions. Therefore, their complexity is calculated separately for each initializer, and it doesn't contribute to the complexity of the enclosing code.
Class field initializers and class static blocks are implicit functions. Therefore, their complexity is calculated separately for each initializer and each static block, and it doesn't contribute to the complexity of the enclosing code.

Examples of additional **incorrect** code for a maximum of 2:

Expand All @@ -67,6 +67,14 @@ Examples of additional **incorrect** code for a maximum of 2:
class C {
x = a || b || c; // this initializer has complexity = 3
}

class D { // this static block has complexity = 3
static {
if (foo) {
bar = baz || qux;
}
}
}
```

Examples of additional **correct** code for a maximum of 2:
Expand All @@ -82,6 +90,16 @@ function foo() { // this function has complexity = 1

static p = g || h; // this initializer has complexity = 2
static q = i ? j : k; // this initializer has complexity = 2

static { // this static block has complexity = 2
if (foo) {
baz = bar;
}
}

static { // this static block has complexity = 2
qux = baz || quux;
}
}
}
```
Expand Down
20 changes: 14 additions & 6 deletions lib/rules/complexity.js
Expand Up @@ -124,20 +124,28 @@ module.exports = {

/*
* This rule only evaluates complexity of functions, so "program" is excluded.
* Class field initializers are implicit functions. Therefore, they shouldn't contribute
* to the enclosing function's complexity, but their own complexity should be evaluated.
* Class field initializers and class static blocks are implicit functions. Therefore,
* they shouldn't contribute to the enclosing function's complexity, but their
* own complexity should be evaluated.
*/
if (
codePath.origin !== "function" &&
codePath.origin !== "class-field-initializer"
codePath.origin !== "class-field-initializer" &&
codePath.origin !== "class-static-block"
) {
return;
}

if (complexity > THRESHOLD) {
const name = codePath.origin === "class-field-initializer"
? "class field initializer"
: astUtils.getFunctionNameWithKind(node);
let name;

if (codePath.origin === "class-field-initializer") {
name = "class field initializer";
} else if (codePath.origin === "class-static-block") {
name = "class static block";
} else {
name = astUtils.getFunctionNameWithKind(node);
}

context.report({
node,
Expand Down
152 changes: 152 additions & 0 deletions tests/lib/rules/complexity.js
Expand Up @@ -104,6 +104,25 @@ ruleTester.run("complexity", rule, {
{ code: "class C { x = a || class { y = b || c; z = d || e; }; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { x; y = a; static z; static q = b; }", options: [1], parserOptions: { ecmaVersion: 2022 } },

// class static blocks
{ code: "function foo() { class C { static { a || b; } static { c || d; } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "function foo() { a || b; class C { static { c || d; } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "function foo() { class C { static { a || b; } } c || d; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "function foo() { class C { static { a || b; } } class D { static { c || d; } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { a || b; } static { c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { a || b; } static { c || d; } static { e || f; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { () => a || b; c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { a || b; () => c || d; } static { c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { a } }", options: [1], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { a } static { b } }", options: [1], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { a || b; } } class D { static { c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { a || b; } static c = d || e; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static a = b || c; static { c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { a || b; } c = d || e; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { a = b || c; static { d || e; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { a || b; c || d; } }", options: [3], parserOptions: { ecmaVersion: 2022 } },
{ code: "class C { static { if (a || b) c = d || e; } }", options: [4], parserOptions: { ecmaVersion: 2022 } },

// object property options
{ code: "function b(x) {}", options: [{ max: 1 }] }
],
Expand Down Expand Up @@ -369,6 +388,139 @@ ruleTester.run("complexity", rule, {
]
},

// class static blocks
{
code: "function foo () { a || b; class C { static {} } c || d; }",
options: [2],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Function 'foo'", 3, 2)]
},
{
code: "function foo () { a || b; class C { static { c || d; } } e || f; }",
options: [2],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Function 'foo'", 3, 2)]
},
{
code: "class C { static { a || b; } }",
options: [1],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Class static block", 2, 1)]
},
{
code: "class C { static { a || b || c; } }",
options: [2],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Class static block", 3, 2)]
},
{
code: "class C { static { a || b; c || d; } }",
options: [2],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Class static block", 3, 2)]
},
{
code: "class C { static { a || b; c || d; e || f; } }",
options: [3],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Class static block", 4, 3)]
},
{
code: "class C { static { a || b; c || d; { e || f; } } }",
options: [3],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Class static block", 4, 3)]
},
{
code: "class C { static { if (a || b) c = d || e; } }",
options: [3],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Class static block", 4, 3)]
},
{
code: "class C { static { if (a || b) c = (d => e || f)() || (g => h || i)(); } }",
options: [3],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Class static block", 4, 3)]
},
{
code: "class C { x(){ a || b; } static { c || d || e; } z() { f || g; } }",
options: [2],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Class static block", 3, 2)]
},
{
code: "class C { x = a || b; static { c || d || e; } y = f || g; }",
options: [2],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Class static block", 3, 2)]
},
{
code: "class C { static x = a || b; static { c || d || e; } static y = f || g; }",
options: [2],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Class static block", 3, 2)]
},
{
code: "class C { static { a || b; } static(){ c || d || e; } static { f || g; } }",
options: [2],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Method 'static'", 3, 2)]
},
{
code: "class C { static { a || b; } static static(){ c || d || e; } static { f || g; } }",
options: [2],
parserOptions: { ecmaVersion: 2022 },
errors: [makeError("Static method 'static'", 3, 2)]
},
{
code: "class C { static { a || b; } static x = c || d || e; static { f || g; } }",
options: [2],
parserOptions: { ecmaVersion: 2022 },
errors: [{
...makeError("Class field initializer", 3, 2),
column: 41,
endColumn: 52
}]
},
{
code: "class C { static { a || b || c || d; } static { e || f || g; } }",
options: [3],
parserOptions: { ecmaVersion: 2022 },
errors: [{
...makeError("Class static block", 4, 3),
column: 11,
endColumn: 39
}]
},
{
code: "class C { static { a || b || c; } static { d || e || f || g; } }",
options: [3],
parserOptions: { ecmaVersion: 2022 },
errors: [{
...makeError("Class static block", 4, 3),
column: 35,
endColumn: 63
}]
},
{
code: "class C { static { a || b || c || d; } static { e || f || g || h; } }",
options: [3],
parserOptions: { ecmaVersion: 2022 },
errors: [
{
...makeError("Class static block", 4, 3),
column: 11,
endColumn: 39
},
{
...makeError("Class static block", 4, 3),
column: 40,
endColumn: 68
}
]
},

// object property options
{ code: "function a(x) {}", options: [{ max: 0 }], errors: [makeError("Function 'a'", 1, 0)] }
]
Expand Down

0 comments on commit 194f36d

Please sign in to comment.