Skip to content

Commit

Permalink
feat: no useless undefined initialization (#2591)
Browse files Browse the repository at this point in the history
  • Loading branch information
lutaok committed Apr 26, 2024
1 parent ce4979d commit a13d36d
Show file tree
Hide file tree
Showing 12 changed files with 458 additions and 8 deletions.
14 changes: 14 additions & 0 deletions 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.

36 changes: 28 additions & 8 deletions crates/biome_configuration/src/linter/rules.rs

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

1 change: 1 addition & 0 deletions crates/biome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ define_categories! {
"lint/nursery/noRestrictedImports": "https://biomejs.dev/linter/rules/no-restricted-imports",
"lint/nursery/noTypeOnlyImportAttributes": "https://biomejs.dev/linter/rules/no-type-only-import-attributes",
"lint/nursery/noUndeclaredDependencies": "https://biomejs.dev/linter/rules/no-undeclared-dependencies",
"lint/nursery/noUselessUndefinedInitialization": "https://biomejs.dev/linter/rules/no-useless-undefined-initialization",
"lint/nursery/noUnknownUnit": "https://biomejs.dev/linter/rules/no-unknown-unit",
"lint/nursery/useBiomeSuppressionComment": "https://biomejs.dev/linter/rules/use-biome-suppression-comment",
"lint/nursery/useConsistentNewBuiltin": "https://biomejs.dev/linter/rules/use-consistent-new-builtin",
Expand Down
2 changes: 2 additions & 0 deletions crates/biome_js_analyze/src/lint/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,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,
})
}
}
1 change: 1 addition & 0 deletions crates/biome_js_analyze/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ pub type NoUselessTernary =
pub type NoUselessThisAlias =
<lint::complexity::no_useless_this_alias::NoUselessThisAlias as biome_analyze::Rule>::Options;
pub type NoUselessTypeConstraint = < lint :: complexity :: no_useless_type_constraint :: NoUselessTypeConstraint as biome_analyze :: Rule > :: Options ;
pub type NoUselessUndefinedInitialization = < lint :: nursery :: no_useless_undefined_initialization :: NoUselessUndefinedInitialization as biome_analyze :: Rule > :: Options ;
pub type NoVar = <lint::style::no_var::NoVar as biome_analyze::Rule>::Options;
pub type NoVoid = <lint::complexity::no_void::NoVoid as biome_analyze::Rule>::Options;
pub type NoVoidElementsWithChildren = < lint :: correctness :: no_void_elements_with_children :: NoVoidElementsWithChildren as biome_analyze :: Rule > :: Options ;
Expand Down
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/**/ ;
Loading

0 comments on commit a13d36d

Please sign in to comment.