diff --git a/crates/rome_js_analyze/src/semantic_analyzers/nursery/no_const_assign.rs b/crates/rome_js_analyze/src/semantic_analyzers/nursery/no_const_assign.rs index b5e72b332d4..8368e57691d 100644 --- a/crates/rome_js_analyze/src/semantic_analyzers/nursery/no_const_assign.rs +++ b/crates/rome_js_analyze/src/semantic_analyzers/nursery/no_const_assign.rs @@ -2,7 +2,12 @@ use crate::semantic_services::Semantic; use rome_analyze::context::RuleContext; use rome_analyze::{declare_rule, Rule, RuleDiagnostic}; use rome_console::markup; -use rome_js_syntax::{JsIdentifierAssignment, JsVariableDeclaration}; +use rome_js_syntax::{ + JsAnyArrayBindingPatternElement, JsAnyObjectBindingPatternMember, + JsArrayBindingPatternElementList, JsForVariableDeclaration, JsIdentifierAssignment, + JsIdentifierBinding, JsObjectBindingPatternPropertyList, JsVariableDeclaration, + JsVariableDeclarator, JsVariableDeclaratorList, +}; use rome_rowan::{AstNode, TextRange}; declare_rule! { @@ -61,13 +66,31 @@ impl Rule for NoConstAssign { let model = ctx.model(); let declared_binding = model.declaration(node)?; - if let Some(variable_declaration) = declared_binding - .syntax() - .ancestors() - .find_map(|ancestor| JsVariableDeclaration::cast_ref(&ancestor)) - { - if variable_declaration.is_const() { - return Some(declared_binding.syntax().text_trimmed_range()); + + if let Some(possible_declarator) = declared_binding.syntax().ancestors().find(|node| { + !JsAnyObjectBindingPatternMember::can_cast(node.kind()) + && !JsObjectBindingPatternPropertyList::can_cast(node.kind()) + && !JsAnyArrayBindingPatternElement::can_cast(node.kind()) + && !JsArrayBindingPatternElementList::can_cast(node.kind()) + && !JsIdentifierBinding::can_cast(node.kind()) + }) { + if JsVariableDeclarator::can_cast(possible_declarator.kind()) { + let possible_declaration = possible_declarator.parent()?; + if let Some(js_for_variable_declaration) = + JsForVariableDeclaration::cast_ref(&possible_declaration) + { + if js_for_variable_declaration.is_const() { + return Some(declared_binding.syntax().text_trimmed_range()); + } + } else if let Some(js_variable_declaration) = + JsVariableDeclaratorList::cast_ref(&possible_declaration) + .and_then(|declaration| declaration.syntax().parent()) + .and_then(JsVariableDeclaration::cast) + { + if js_variable_declaration.is_const() { + return Some(declared_binding.syntax().text_trimmed_range()); + } + } } } diff --git a/crates/rome_js_analyze/tests/specs/nursery/noConstAssign.js b/crates/rome_js_analyze/tests/specs/nursery/noConstAssign.js index 0f07d27799a..a50191929be 100644 --- a/crates/rome_js_analyze/tests/specs/nursery/noConstAssign.js +++ b/crates/rome_js_analyze/tests/specs/nursery/noConstAssign.js @@ -1,11 +1,55 @@ const a = 1; a = 2; -const b = 2, c = 43; +const b = 2, + c = 43; b = 4; ++b; b += 45; b--; function f() { - b++; -} \ No newline at end of file + b++; +} +function f(d) { + b++; +} +const fn = (val) => { + val = 0; +}; + +const e = () => { + try { + foo(); + } catch (err) { + err = 4; + } +}; + +const f = (...rest) => { + rest = 4; +}; + +const g = class bar {}; +bar = 1; + +const h = function foo() { + foo = 1; +}; + +const { + i, + j: { l }, +} = { i: 1, j: { l: 2 } }; +i = 4; +l = 4; + +for (const k in [1, 2]) { + k = 4; +} + +const [p, { q }] = [1, { q: 2 }]; +p = 3; +q = 4; + +const { r, ...rest } = s; +r = 4; diff --git a/crates/rome_js_analyze/tests/specs/nursery/noConstAssign.js.snap b/crates/rome_js_analyze/tests/specs/nursery/noConstAssign.js.snap index 459304b621a..8b9e129d69c 100644 --- a/crates/rome_js_analyze/tests/specs/nursery/noConstAssign.js.snap +++ b/crates/rome_js_analyze/tests/specs/nursery/noConstAssign.js.snap @@ -1,5 +1,6 @@ --- source: crates/rome_js_analyze/tests/spec_tests.rs +assertion_line: 73 expression: noConstAssign.js --- # Input @@ -7,14 +8,59 @@ expression: noConstAssign.js const a = 1; a = 2; -const b = 2, c = 43; +const b = 2, + c = 43; b = 4; ++b; b += 45; b--; function f() { - b++; + b++; } +function f(d) { + b++; +} +const fn = (val) => { + val = 0; +}; + +const e = () => { + try { + foo(); + } catch (err) { + err = 4; + } +}; + +const f = (...rest) => { + rest = 4; +}; + +const g = class bar {}; +bar = 1; + +const h = function foo() { + foo = 1; +}; + +const { + i, + j: { l }, +} = { i: 1, j: { l: 2 } }; +i = 4; +l = 4; + +for (const k in [1, 2]) { + k = 4; +} + +const [p, { q }] = [1, { q: 2 }]; +p = 3; +q = 4; + +const { r, ...rest } = s; +r = 4; + ``` # Diagnostics @@ -27,7 +73,7 @@ noConstAssign.js:2:1 lint/nursery/noConstAssign ━━━━━━━━━━ > 2 │ a = 2; │ ^ 3 │ - 4 │ const b = 2, c = 43; + 4 │ const b = 2, i This is where the variable is defined as constant @@ -40,119 +86,284 @@ noConstAssign.js:2:1 lint/nursery/noConstAssign ━━━━━━━━━━ ``` ``` -noConstAssign.js:5:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noConstAssign.js:6:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Can't assign b because it's a constant - 4 │ const b = 2, c = 43; - > 5 │ b = 4; + 4 │ const b = 2, + 5 │ c = 43; + > 6 │ b = 4; │ ^ - 6 │ ++b; - 7 │ b += 45; + 7 │ ++b; + 8 │ b += 45; i This is where the variable is defined as constant 2 │ a = 2; 3 │ - > 4 │ const b = 2, c = 43; + > 4 │ const b = 2, │ ^ - 5 │ b = 4; - 6 │ ++b; + 5 │ c = 43; + 6 │ b = 4; ``` ``` -noConstAssign.js:6:3 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noConstAssign.js:7:3 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Can't assign b because it's a constant - 4 │ const b = 2, c = 43; - 5 │ b = 4; - > 6 │ ++b; + 5 │ c = 43; + 6 │ b = 4; + > 7 │ ++b; │ ^ - 7 │ b += 45; - 8 │ b--; + 8 │ b += 45; + 9 │ b--; i This is where the variable is defined as constant 2 │ a = 2; 3 │ - > 4 │ const b = 2, c = 43; + > 4 │ const b = 2, │ ^ - 5 │ b = 4; - 6 │ ++b; + 5 │ c = 43; + 6 │ b = 4; ``` ``` -noConstAssign.js:7:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noConstAssign.js:8:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Can't assign b because it's a constant - 5 │ b = 4; - 6 │ ++b; - > 7 │ b += 45; - │ ^ - 8 │ b--; - 9 │ function f() { + 6 │ b = 4; + 7 │ ++b; + > 8 │ b += 45; + │ ^ + 9 │ b--; + 10 │ function f() { i This is where the variable is defined as constant 2 │ a = 2; 3 │ - > 4 │ const b = 2, c = 43; + > 4 │ const b = 2, │ ^ - 5 │ b = 4; - 6 │ ++b; + 5 │ c = 43; + 6 │ b = 4; ``` ``` -noConstAssign.js:8:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noConstAssign.js:9:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Can't assign b because it's a constant - 6 │ ++b; - 7 │ b += 45; - > 8 │ b--; + 7 │ ++b; + 8 │ b += 45; + > 9 │ b--; │ ^ - 9 │ function f() { - 10 │ b++; + 10 │ function f() { + 11 │ b++; i This is where the variable is defined as constant 2 │ a = 2; 3 │ - > 4 │ const b = 2, c = 43; + > 4 │ const b = 2, │ ^ - 5 │ b = 4; - 6 │ ++b; + 5 │ c = 43; + 6 │ b = 4; ``` ``` -noConstAssign.js:10:5 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noConstAssign.js:11:2 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Can't assign b because it's a constant - 8 │ b--; - 9 │ function f() { - > 10 │ b++; - │ ^ - 11 │ } + 9 │ b--; + 10 │ function f() { + > 11 │ b++; + │ ^ + 12 │ } + 13 │ function f(d) { i This is where the variable is defined as constant 2 │ a = 2; 3 │ - > 4 │ const b = 2, c = 43; + > 4 │ const b = 2, │ ^ - 5 │ b = 4; - 6 │ ++b; + 5 │ c = 43; + 6 │ b = 4; + + +``` + +``` +noConstAssign.js:14:2 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Can't assign b because it's a constant + + 12 │ } + 13 │ function f(d) { + > 14 │ b++; + │ ^ + 15 │ } + 16 │ const fn = (val) => { + + i This is where the variable is defined as constant + + 2 │ a = 2; + 3 │ + > 4 │ const b = 2, + │ ^ + 5 │ c = 43; + 6 │ b = 4; + + +``` + +``` +noConstAssign.js:43:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Can't assign i because it's a constant + + 41 │ j: { l }, + 42 │ } = { i: 1, j: { l: 2 } }; + > 43 │ i = 4; + │ ^ + 44 │ l = 4; + 45 │ + + i This is where the variable is defined as constant + + 39 │ const { + > 40 │ i, + │ ^ + 41 │ j: { l }, + 42 │ } = { i: 1, j: { l: 2 } }; + + +``` + +``` +noConstAssign.js:44:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Can't assign l because it's a constant + + 42 │ } = { i: 1, j: { l: 2 } }; + 43 │ i = 4; + > 44 │ l = 4; + │ ^ + 45 │ + 46 │ for (const k in [1, 2]) { + + i This is where the variable is defined as constant + + 39 │ const { + 40 │ i, + > 41 │ j: { l }, + │ ^ + 42 │ } = { i: 1, j: { l: 2 } }; + 43 │ i = 4; + + +``` + +``` +noConstAssign.js:47:2 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Can't assign k because it's a constant + + 46 │ for (const k in [1, 2]) { + > 47 │ k = 4; + │ ^ + 48 │ } + 49 │ + + i This is where the variable is defined as constant + + 44 │ l = 4; + 45 │ + > 46 │ for (const k in [1, 2]) { + │ ^ + 47 │ k = 4; + 48 │ } + + +``` + +``` +noConstAssign.js:51:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Can't assign p because it's a constant + + 50 │ const [p, { q }] = [1, { q: 2 }]; + > 51 │ p = 3; + │ ^ + 52 │ q = 4; + 53 │ + + i This is where the variable is defined as constant + + 48 │ } + 49 │ + > 50 │ const [p, { q }] = [1, { q: 2 }]; + │ ^ + 51 │ p = 3; + 52 │ q = 4; + + +``` + +``` +noConstAssign.js:52:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Can't assign q because it's a constant + + 50 │ const [p, { q }] = [1, { q: 2 }]; + 51 │ p = 3; + > 52 │ q = 4; + │ ^ + 53 │ + 54 │ const { r, ...rest } = s; + + i This is where the variable is defined as constant + + 48 │ } + 49 │ + > 50 │ const [p, { q }] = [1, { q: 2 }]; + │ ^ + 51 │ p = 3; + 52 │ q = 4; + + +``` + +``` +noConstAssign.js:55:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Can't assign r because it's a constant + + 54 │ const { r, ...rest } = s; + > 55 │ r = 4; + │ ^ + 56 │ + + i This is where the variable is defined as constant + + 52 │ q = 4; + 53 │ + > 54 │ const { r, ...rest } = s; + │ ^ + 55 │ r = 4; + 56 │ ```