From 194f36d9c009a72ec72fa9592ea9e31f9f168a52 Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Fri, 19 Nov 2021 12:17:22 +0100 Subject: [PATCH] feat: update the complexity rule for class static blocks (#15328) Updates the `complexity` rule to apply to class static blocks. Refs #15016 --- docs/rules/complexity.md | 20 ++++- lib/rules/complexity.js | 20 +++-- tests/lib/rules/complexity.js | 152 ++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 7 deletions(-) diff --git a/docs/rules/complexity.md b/docs/rules/complexity.md index f6413d6c5dd..7955d1a2854 100644 --- a/docs/rules/complexity.md +++ b/docs/rules/complexity.md @@ -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: @@ -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: @@ -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; + } } } ``` diff --git a/lib/rules/complexity.js b/lib/rules/complexity.js index b833aafc0f7..a247039760e 100644 --- a/lib/rules/complexity.js +++ b/lib/rules/complexity.js @@ -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, diff --git a/tests/lib/rules/complexity.js b/tests/lib/rules/complexity.js index badde8a715a..d6feb8a2f05 100644 --- a/tests/lib/rules/complexity.js +++ b/tests/lib/rules/complexity.js @@ -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 }] } ], @@ -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)] } ]