Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
feat(rome_js_analyze): noClassAssign rule (#4033)
Browse files Browse the repository at this point in the history
* feat(rome_js_analyze): `noClassAssign` rule
  • Loading branch information
kaioduarte authored Dec 16, 2022
1 parent 8597c39 commit 128a684
Show file tree
Hide file tree
Showing 11 changed files with 724 additions and 10 deletions.
1 change: 1 addition & 0 deletions crates/rome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ define_dategories! {
"lint/nursery/noAssignInExpressions": "https://docs.rome.tools/lint/rules/noAssignInExpressions",
"lint/nursery/noWith": "https://docs.rome.tools/lint/rules/noWith",
"lint/nursery/noBannedTypes":"https://docs.rome.tools/lint/rules/noBannedTypes",
"lint/nursery/noClassAssign": "https://docs.rome.tools/lint/rules/noClassAssign",
"lint/nursery/noCommaOperator": "https://docs.rome.tools/lint/rules/noCommaOperator",
"lint/nursery/noConstEnum": "https://docs.rome.tools/lint/rules/noConstEnum",
"lint/nursery/noConstructorReturn": "https://docs.rome.tools/lint/rules/noConstructorReturn",
Expand Down
3 changes: 2 additions & 1 deletion crates/rome_js_analyze/src/semantic_analyzers/nursery.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use rome_analyze::context::RuleContext;
use rome_analyze::{declare_rule, Rule, RuleDiagnostic};
use rome_console::markup;
use rome_js_semantic::{Reference, ReferencesExtensions};
use rome_js_syntax::AnyJsClass;

use crate::semantic_services::Semantic;

declare_rule! {
/// Disallow reassigning class members.
///
/// A class declaration creates a variable that we can modify, however, the modification is a mistake in most cases.
///
/// ## Examples
///
/// ### Invalid
///
/// ```js,expect_diagnostic
/// class A {}
/// A = 0;
/// ```
///
/// ```js,expect_diagnostic
/// A = 0;
/// class A {}
/// ```
///
/// ```js,expect_diagnostic
/// class A {
/// b() {
/// A = 0;
/// }
/// }
/// ```
///
/// ```js,expect_diagnostic
/// let A = class A {
/// b() {
/// A = 0;
/// // `let A` is shadowed by the class name.
/// }
/// }
/// ```
///
/// ### Valid
///
/// ```js
/// let A = class A {}
/// A = 0; // A is a variable.
/// ```
///
/// ```js
/// let A = class {
/// b() {
/// A = 0; // A is a variable.
/// }
/// }
/// ```
///
/// ```js
/// class A {
/// b(A) {
/// A = 0; // A is a parameter.
/// }
/// }
/// ```
///
pub(crate) NoClassAssign {
version: "12.0.0",
name: "noClassAssign",
recommended: true,
}
}

impl Rule for NoClassAssign {
type Query = Semantic<AnyJsClass>;
type State = Reference;
type Signals = Vec<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let node = ctx.query();
let model = ctx.model();

if let Ok(Some(id)) = node.id() {
if let Some(id_binding) = id.as_js_identifier_binding() {
return id_binding.all_writes(model).collect();
}
}

Vec::new()
}

fn diagnostic(ctx: &RuleContext<Self>, reference: &Self::State) -> Option<RuleDiagnostic> {
let binding = ctx
.query()
.id()
.ok()??
.as_js_identifier_binding()?
.name_token()
.ok()?;
let class_name = binding.text_trimmed();

Some(
RuleDiagnostic::new(
rule_category!(),
reference.syntax().text_trimmed_range(),
markup! {"'"{class_name}"' is a class."},
)
.detail(
binding.text_trimmed_range(),
markup! {"'"{class_name}"' is defined here."},
),
)
}
}
110 changes: 110 additions & 0 deletions crates/rome_js_analyze/tests/specs/nursery/noClassAssign.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* Valid */
function case1() {
class A { }
foo(A);
}

function case2() {
let A = class A { };
foo(A);
}

function case3() {
class A {
b(A) {
A = 0;
}
}
}

function case4() {
class A {
b() {
let A;
A = 0;
}
}
}

function case5() {
let A = class {
b() {
A = 0;
}
}
}

// /* Ignores non class. */
function case6() {
var x = 0;
x = 1;
}

function case7() {
let x = 0;
x = 1;
}

function case8() {
const x = 0;
x = 1;
}

function case9() {
function x() {}
x = 1;
}

function case10(x) {
x = 1;
}

function case11() {
try {}
catch (x) {
x = 1;
}
}

// /* Invalid */
function case12() {
class A { }
A = 0;
}

function case13() {
class B { }
({B} = 0);
}

function case14() {
class C { }
({b: C = 0} = {});
}

function case15() {
D = 0;
class D { }
}

function case16() {
class E {
b() {
E = 0;
}
}
}

function case17() {
let F = class F {
b() {
F = 0;
}
}
}

function case18() {
class G { }
G = 0;
G = 1;
}
Loading

0 comments on commit 128a684

Please sign in to comment.