-
-
Notifications
You must be signed in to change notification settings - Fork 481
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: no useless undefined initialization (#2591)
- Loading branch information
Showing
12 changed files
with
458 additions
and
8 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
138 changes: 138 additions & 0 deletions
138
crates/biome_js_analyze/src/lint/nursery/no_useless_undefined_initialization.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
use biome_analyze::{ | ||
context::RuleContext, declare_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic, | ||
RuleSource, RuleSourceKind, | ||
}; | ||
use biome_console::markup; | ||
use biome_diagnostics::Applicability; | ||
use biome_js_syntax::JsVariableDeclaration; | ||
use biome_rowan::{AstNode, BatchMutationExt, TextRange}; | ||
|
||
use crate::JsRuleAction; | ||
|
||
declare_rule! { | ||
/// Disallow initializing variables to `undefined`. | ||
/// | ||
/// A variable that is declared and not initialized to any value automatically gets the value of `undefined`. | ||
/// It’s considered a best practice to avoid initializing variables to `undefined`. | ||
/// Please note that any inline comments attached to the initialization value or variable will be removed on auto-fix. | ||
/// Please be also aware that this differs from Eslint's behaviour and we are still discussing on how to properly handle this case. | ||
/// | ||
/// ## Examples | ||
/// | ||
/// ### Invalid | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// var a = undefined; | ||
/// ``` | ||
/// ```js,expect_diagnostic | ||
/// let b = undefined, c = 1, d = undefined; | ||
/// ``` | ||
/// ```js,expect_diagnostic | ||
/// for (let i = 0; i < 100; i++) { | ||
/// let i = undefined; | ||
/// } | ||
/// ``` | ||
/// ```js,expect_diagnostic | ||
/// let f = /**/undefined/**/ ; | ||
/// ``` | ||
/// ### Valid | ||
/// | ||
/// ```js | ||
/// var a = 1; | ||
/// ``` | ||
/// ```js | ||
/// class Foo { | ||
/// bar = undefined; | ||
/// } | ||
/// ``` | ||
/// | ||
pub NoUselessUndefinedInitialization { | ||
version: "next", | ||
name: "noUselessUndefinedInitialization", | ||
sources: &[RuleSource::Eslint("no-undef-init")], | ||
source_kind: RuleSourceKind::Inspired, | ||
fix_kind: FixKind::Unsafe, | ||
recommended: false, | ||
} | ||
} | ||
|
||
impl Rule for NoUselessUndefinedInitialization { | ||
type Query = Ast<JsVariableDeclaration>; | ||
type State = (String, TextRange); | ||
type Signals = Vec<Self::State>; | ||
type Options = (); | ||
|
||
fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
let node = ctx.query(); | ||
let let_or_var_kind = node.is_let() || node.is_var(); | ||
|
||
let mut signals = vec![]; | ||
|
||
if !let_or_var_kind { | ||
return signals; | ||
} | ||
|
||
for declarator in node.declarators() { | ||
let Ok(decl) = declarator else { continue }; | ||
|
||
let Some(initializer) = decl.initializer() else { | ||
continue; | ||
}; | ||
|
||
let Some(keyword) = initializer | ||
.expression() | ||
.ok() | ||
.and_then(|expression| expression.as_js_reference_identifier()) | ||
else { | ||
continue; | ||
}; | ||
|
||
if keyword.is_undefined() { | ||
let decl_range = initializer.range(); | ||
let Some(binding_name) = decl.id().ok().map(|id| id.text()) else { | ||
continue; | ||
}; | ||
signals.push((binding_name, decl_range)); | ||
} | ||
} | ||
|
||
signals | ||
} | ||
|
||
fn diagnostic(_ctx: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> { | ||
Some(RuleDiagnostic::new( | ||
rule_category!(), | ||
state.1, | ||
markup! { | ||
"It's not necessary to initialize "<Emphasis>{state.0}</Emphasis>" to undefined." | ||
}).note("A variable that is declared and not initialized to any value automatically gets the value of undefined.") | ||
) | ||
} | ||
|
||
fn action(ctx: &RuleContext<Self>, state: &Self::State) -> Option<JsRuleAction> { | ||
let declarators = ctx.query().declarators(); | ||
|
||
let initializer = declarators | ||
.clone() | ||
.into_iter() | ||
.find(|el| { | ||
el.as_ref() | ||
.ok() | ||
.and_then(|element| element.id().ok()) | ||
.is_some_and(|id| id.text() == state.0) | ||
}) | ||
.map(|decl| decl.ok())? | ||
.and_then(|declarator| declarator.initializer())?; | ||
|
||
let mut mutation = ctx.root().begin(); | ||
// This will remove any comments attached to the initialization clause | ||
mutation.remove_node(initializer); | ||
|
||
Some(JsRuleAction { | ||
category: ActionCategory::QuickFix, | ||
applicability: Applicability::MaybeIncorrect, | ||
message: markup! { "Remove undefined initialization." }.to_owned(), | ||
mutation, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
crates/biome_js_analyze/tests/specs/nursery/noUselessUndefinedInitialization/invalid.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
let test1, | ||
test2 = undefined; | ||
var c = undefined, | ||
o = undefined; | ||
|
||
for (let i = 0; i < 100; i++) { | ||
let i = undefined; | ||
} | ||
|
||
let x = 1, y = undefined, z = 40 | ||
|
||
let /* comment */d = undefined; | ||
|
||
let e = undefined/**/ ; | ||
let f = /**/undefined/**/ ; | ||
let g | ||
/**/= /**/undefined/**/ ; |
Oops, something went wrong.