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

feat: update the complexity rule for class static blocks #15328

Merged
merged 1 commit into from Nov 19, 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
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