-
Notifications
You must be signed in to change notification settings - Fork 653
feat(rome_js_analyze): rule noConstAssign
#3409
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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,93 @@ | ||
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_rowan::{AstNode, TextRange}; | ||
|
||
declare_rule! { | ||
/// Prevents from having `const` variables being re-assigned. | ||
/// | ||
/// Trying to assign a value to a `const` will cause an `TypeError` when the code is executed. | ||
/// | ||
/// ## Examples | ||
/// | ||
/// ### Invalid | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// const a = 1; | ||
/// a = 4; | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// const a = 2; | ||
/// a += 1; | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// const a = 1; | ||
ematipico marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// ++a; | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// const a = 1, b = 2; | ||
/// | ||
/// a = 2; | ||
/// ``` | ||
/// | ||
/// ### Valid | ||
/// | ||
/// ```js | ||
/// const a = 10; | ||
/// let b = 10; | ||
/// b = 20; | ||
/// ``` | ||
/// | ||
pub(crate) NoConstAssign { | ||
version: "10.0.0", | ||
name: "noConstAssign", | ||
recommended: false, | ||
} | ||
} | ||
|
||
impl Rule for NoConstAssign { | ||
type Query = Semantic<JsIdentifierAssignment>; | ||
type State = TextRange; | ||
type Signals = Option<Self::State>; | ||
type Options = (); | ||
|
||
fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
let node = ctx.query(); | ||
let model = ctx.model(); | ||
|
||
let declared_binding = model.declaration(node)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit worried that this will have a significant performance cost because it requires querying the model for every single identifier assignment. I'm not familiar with our semantic model but wonder if it would be cheaper to
That could be cheaper assuming there are fewer declarations than assignments. But this is only a hypothesis and also heavily depends on how the semantic model is implemented internally. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should able to use the semantic model straight from the rule. Let me if I am up to the task |
||
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()); | ||
} | ||
} | ||
|
||
None | ||
} | ||
|
||
fn diagnostic(ctx: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> { | ||
let node = ctx.query(); | ||
let name = node.name_token().ok()?; | ||
let name = name.text_trimmed(); | ||
Some( | ||
RuleDiagnostic::new( | ||
rule_category!(), | ||
node.syntax().text_trimmed_range(), | ||
markup! {"Can't assign "<Emphasis>{name}</Emphasis>" because it's a constant"}, | ||
) | ||
.secondary( | ||
state, | ||
markup! {"This is where the variable is defined as constant"}, | ||
), | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
const a = 1; | ||
a = 2; | ||
|
||
const b = 2, c = 43; | ||
b = 4; | ||
++b; | ||
b += 45; | ||
b--; | ||
function f() { | ||
b++; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
--- | ||
source: crates/rome_js_analyze/tests/spec_tests.rs | ||
expression: noConstAssign.js | ||
--- | ||
# Input | ||
```js | ||
const a = 1; | ||
a = 2; | ||
|
||
const b = 2, c = 43; | ||
b = 4; | ||
++b; | ||
b += 45; | ||
b--; | ||
function f() { | ||
b++; | ||
} | ||
``` | ||
|
||
# Diagnostics | ||
``` | ||
noConstAssign.js:2:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
|
||
! Can't assign a because it's a constant | ||
|
||
1 │ const a = 1; | ||
> 2 │ a = 2; | ||
│ ^ | ||
3 │ | ||
4 │ const b = 2, c = 43; | ||
|
||
i This is where the variable is defined as constant | ||
|
||
> 1 │ const a = 1; | ||
│ ^ | ||
2 │ a = 2; | ||
3 │ | ||
|
||
|
||
``` | ||
|
||
``` | ||
noConstAssign.js:5:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
|
||
! Can't assign b because it's a constant | ||
|
||
4 │ const b = 2, c = 43; | ||
> 5 │ b = 4; | ||
│ ^ | ||
6 │ ++b; | ||
7 │ b += 45; | ||
|
||
i This is where the variable is defined as constant | ||
|
||
2 │ a = 2; | ||
3 │ | ||
> 4 │ const b = 2, c = 43; | ||
│ ^ | ||
5 │ b = 4; | ||
6 │ ++b; | ||
|
||
|
||
``` | ||
|
||
``` | ||
noConstAssign.js:6:3 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
|
||
! Can't assign b because it's a constant | ||
|
||
4 │ const b = 2, c = 43; | ||
5 │ b = 4; | ||
> 6 │ ++b; | ||
│ ^ | ||
7 │ b += 45; | ||
8 │ b--; | ||
|
||
i This is where the variable is defined as constant | ||
|
||
2 │ a = 2; | ||
3 │ | ||
> 4 │ const b = 2, c = 43; | ||
│ ^ | ||
5 │ b = 4; | ||
6 │ ++b; | ||
|
||
|
||
``` | ||
|
||
``` | ||
noConstAssign.js:7: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() { | ||
|
||
i This is where the variable is defined as constant | ||
|
||
2 │ a = 2; | ||
3 │ | ||
> 4 │ const b = 2, c = 43; | ||
│ ^ | ||
5 │ b = 4; | ||
6 │ ++b; | ||
|
||
|
||
``` | ||
|
||
``` | ||
noConstAssign.js:8:1 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
|
||
! Can't assign b because it's a constant | ||
|
||
6 │ ++b; | ||
7 │ b += 45; | ||
> 8 │ b--; | ||
│ ^ | ||
9 │ function f() { | ||
10 │ b++; | ||
|
||
i This is where the variable is defined as constant | ||
|
||
2 │ a = 2; | ||
3 │ | ||
> 4 │ const b = 2, c = 43; | ||
│ ^ | ||
5 │ b = 4; | ||
6 │ ++b; | ||
|
||
|
||
``` | ||
|
||
``` | ||
noConstAssign.js:10:5 lint/nursery/noConstAssign ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
|
||
! Can't assign b because it's a constant | ||
|
||
8 │ b--; | ||
9 │ function f() { | ||
> 10 │ b++; | ||
│ ^ | ||
11 │ } | ||
|
||
i This is where the variable is defined as constant | ||
|
||
2 │ a = 2; | ||
3 │ | ||
> 4 │ const b = 2, c = 43; | ||
│ ^ | ||
5 │ b = 4; | ||
6 │ ++b; | ||
|
||
|
||
``` | ||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.