diff --git a/Cargo.lock b/Cargo.lock index 363ebb9..d7202e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -234,12 +234,6 @@ dependencies = [ "libc", ] -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "ignore" version = "0.4.18" @@ -299,7 +293,6 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" name = "lib" version = "0.0.0" dependencies = [ - "if_chain", "lazy_static", "macros", "rnix", diff --git a/Cargo.toml b/Cargo.toml index 98b44cf..45662b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ license = "MIT" [workspace.dependencies] ariadne = "0.1.3" clap = "3.0.0-beta.4" -if_chain = "1.0" ignore = "0.4.18" indexmap = "1.6.2" insta = "1.8.0" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index b6ae5fc..91edc2b 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -7,7 +7,6 @@ version = "0.0.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -if_chain.workspace = true lazy_static.workspace = true macros.workspace = true rnix.workspace = true diff --git a/lib/src/lints/bool_comparison.rs b/lib/src/lints/bool_comparison.rs index 17a856c..1ac274c 100644 --- a/lib/src/lints/bool_comparison.rs +++ b/lib/src/lints/bool_comparison.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, @@ -36,63 +35,61 @@ struct BoolComparison; impl Rule for BoolComparison { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(bin_expr) = BinOp::cast(node.clone()); - if let Some(lhs) = bin_expr.lhs(); - if let Some(rhs) = bin_expr.rhs(); - if let Some(op) = bin_expr.operator(); - - if let BinOpKind::Equal | BinOpKind::NotEqual = op; + if let NodeOrToken::Node(node) = node + && let Some(bin_expr) = BinOp::cast(node.clone()) + && let Some(lhs) = bin_expr.lhs() + && let Some(rhs) = bin_expr.rhs() + && let Some(op) = bin_expr.operator() + && let BinOpKind::Equal | BinOpKind::NotEqual = op + { let (non_bool_side, bool_side) = if boolean_ident(&lhs).is_some() { (rhs, lhs) } else if boolean_ident(&rhs).is_some() { (lhs, rhs) } else { - return None + return None; }; - then { - let at = node.text_range(); - let replacement = { - match (boolean_ident(&bool_side).unwrap(), op == BinOpKind::Equal) { - (NixBoolean::True, true) | (NixBoolean::False, false) => { - // `a == true`, `a != false` replace with just `a` - non_bool_side.clone() - }, - (NixBoolean::True, false) | (NixBoolean::False, true) => { - // `a != true`, `a == false` replace with `!a` - match non_bool_side.kind() { - SyntaxKind::NODE_APPLY - | SyntaxKind::NODE_PAREN - | SyntaxKind::NODE_IDENT => { - // do not parenthsize the replacement + let at = node.text_range(); + let replacement = { + match (boolean_ident(&bool_side).unwrap(), op == BinOpKind::Equal) { + (NixBoolean::True, true) | (NixBoolean::False, false) => { + // `a == true`, `a != false` replace with just `a` + non_bool_side.clone() + } + (NixBoolean::True, false) | (NixBoolean::False, true) => { + // `a != true`, `a == false` replace with `!a` + match non_bool_side.kind() { + SyntaxKind::NODE_APPLY + | SyntaxKind::NODE_PAREN + | SyntaxKind::NODE_IDENT => { + // do not parenthsize the replacement + make::unary_not(&non_bool_side).node().clone() + } + SyntaxKind::NODE_BIN_OP => { + let inner = BinOp::cast(non_bool_side.clone()).unwrap(); + // `!a ? b`, no paren required + if inner.operator()? == BinOpKind::IsSet { make::unary_not(&non_bool_side).node().clone() - }, - SyntaxKind::NODE_BIN_OP => { - let inner = BinOp::cast(non_bool_side.clone()).unwrap(); - // `!a ? b`, no paren required - if inner.operator()? == BinOpKind::IsSet { - make::unary_not(&non_bool_side).node().clone() - } else { - let parens = make::parenthesize(&non_bool_side); - make::unary_not(parens.node()).node().clone() - } - }, - _ => { + } else { let parens = make::parenthesize(&non_bool_side); make::unary_not(parens.node()).node().clone() } } - }, + _ => { + let parens = make::parenthesize(&non_bool_side); + make::unary_not(parens.node()).node().clone() + } + } } - }; - let message = format!( - "Comparing `{non_bool_side}` with boolean literal `{bool_side}`" - ); - Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) - } else { - None - } + } + }; + let message = format!("Comparing `{non_bool_side}` with boolean literal `{bool_side}`"); + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/bool_simplification.rs b/lib/src/lints/bool_simplification.rs index 7ef3502..232cbd0 100644 --- a/lib/src/lints/bool_simplification.rs +++ b/lib/src/lints/bool_simplification.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -33,29 +32,27 @@ struct BoolSimplification; impl Rule for BoolSimplification { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(unary_expr) = UnaryOp::cast(node.clone()); - if unary_expr.operator() == UnaryOpKind::Invert; - if let Some(value_expr) = unary_expr.value(); - if let Some(paren_expr) = Paren::cast(value_expr); - if let Some(inner_expr) = paren_expr.inner(); - if let Some(bin_expr) = BinOp::cast(inner_expr); - if let Some(BinOpKind::Equal) = bin_expr.operator(); - then { - let at = node.text_range(); - let message = "Try `!=` instead of `!(... == ...)`"; + if let NodeOrToken::Node(node) = node + && let Some(unary_expr) = UnaryOp::cast(node.clone()) + && unary_expr.operator() == UnaryOpKind::Invert + && let Some(value_expr) = unary_expr.value() + && let Some(paren_expr) = Paren::cast(value_expr) + && let Some(inner_expr) = paren_expr.inner() + && let Some(bin_expr) = BinOp::cast(inner_expr) + && let Some(BinOpKind::Equal) = bin_expr.operator() + { + let at = node.text_range(); + let message = "Try `!=` instead of `!(... == ...)`"; - let lhs = bin_expr.lhs()?; - let rhs = bin_expr.rhs()?; - let replacement = make::binary(&lhs, "!=", &rhs).node().clone(); - Some( - self.report() - .suggest(at, message, Suggestion::new(at, replacement)), - ) - } else { - None - } + let lhs = bin_expr.lhs()?; + let rhs = bin_expr.rhs()?; + let replacement = make::binary(&lhs, "!=", &rhs).node().clone(); + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/collapsible_let_in.rs b/lib/src/lints/collapsible_let_in.rs index 9cc9f30..191e4e2 100644 --- a/lib/src/lints/collapsible_let_in.rs +++ b/lib/src/lints/collapsible_let_in.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, TextRange, @@ -46,42 +45,43 @@ struct CollapsibleLetIn; impl Rule for CollapsibleLetIn { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(let_in_expr) = LetIn::cast(node.clone()); - if let Some(body) = let_in_expr.body(); + if let NodeOrToken::Node(node) = node + && let Some(let_in_expr) = LetIn::cast(node.clone()) + && let Some(body) = let_in_expr.body() + && LetIn::cast(body.clone()).is_some() + { + let first_annotation = node.text_range(); + let first_message = "This `let in` expression contains a nested `let in` expression"; - if LetIn::cast(body.clone()).is_some(); - then { - let first_annotation = node.text_range(); - let first_message = "This `let in` expression contains a nested `let in` expression"; + let second_annotation = body.text_range(); + let second_message = "This `let in` expression is nested"; - let second_annotation = body.text_range(); - let second_message = "This `let in` expression is nested"; + let replacement_at = { + let start = body + .siblings_with_tokens(Direction::Prev) + .find(|elem| elem.kind() == SyntaxKind::TOKEN_IN)? + .text_range() + .start(); + let end = body + .descendants_with_tokens() + .find(|elem| elem.kind() == SyntaxKind::TOKEN_LET)? + .text_range() + .end(); + TextRange::new(start, end) + }; + let replacement = make::empty().node().clone(); - let replacement_at = { - let start = body - .siblings_with_tokens(Direction::Prev) - .find(|elem| elem.kind() == SyntaxKind::TOKEN_IN)? - .text_range() - .start(); - let end = body - .descendants_with_tokens() - .find(|elem| elem.kind() == SyntaxKind::TOKEN_LET)? - .text_range() - .end(); - TextRange::new(start, end) - }; - let replacement = make::empty().node().clone(); - - Some( - self.report() - .diagnostic(first_annotation, first_message) - .suggest(second_annotation, second_message, Suggestion::new(replacement_at, replacement)) - ) - } else { - None - } + Some( + self.report() + .diagnostic(first_annotation, first_message) + .suggest( + second_annotation, + second_message, + Suggestion::new(replacement_at, replacement), + ), + ) + } else { + None } } } diff --git a/lib/src/lints/deprecated_to_path.rs b/lib/src/lints/deprecated_to_path.rs index 7b37f1e..bdf4ec7 100644 --- a/lib/src/lints/deprecated_to_path.rs +++ b/lib/src/lints/deprecated_to_path.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -42,18 +41,18 @@ static ALLOWED_PATHS: &[&str; 2] = &["builtins.toPath", "toPath"]; impl Rule for DeprecatedIsNull { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(apply) = Apply::cast(node.clone()); - let lambda_path = apply.lambda()?.to_string(); - if ALLOWED_PATHS.contains(&lambda_path.as_str()); - then { - let at = node.text_range(); - let message = format!("`{lambda_path}` is deprecated, see `:doc builtins.toPath` within the REPL for more"); - Some(self.report().diagnostic(at, message)) - } else { - None - } + if let NodeOrToken::Node(node) = node + && let Some(apply) = Apply::cast(node.clone()) + && let lambda_path = apply.lambda()?.to_string() + && ALLOWED_PATHS.contains(&lambda_path.as_str()) + { + let at = node.text_range(); + let message = format!( + "`{lambda_path}` is deprecated, see `:doc builtins.toPath` within the REPL for more" + ); + Some(self.report().diagnostic(at, message)) + } else { + None } } } diff --git a/lib/src/lints/empty_inherit.rs b/lib/src/lints/empty_inherit.rs index 48f17fc..3eb4213 100644 --- a/lib/src/lints/empty_inherit.rs +++ b/lib/src/lints/empty_inherit.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo, utils}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -30,24 +29,21 @@ struct EmptyInherit; impl Rule for EmptyInherit { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(inherit_stmt) = Inherit::cast(node.clone()); - if inherit_stmt.from().is_none(); - if inherit_stmt.idents().count() == 0; - then { - let at = node.text_range(); - let replacement = make::empty().node().clone(); - let replacement_at = utils::with_preceeding_whitespace(node); - let message = "Remove this empty `inherit` statement"; - Some( - self - .report() - .suggest(at, message, Suggestion::new(replacement_at, replacement)) - ) - } else { - None - } + if let NodeOrToken::Node(node) = node + && let Some(inherit_stmt) = Inherit::cast(node.clone()) + && inherit_stmt.from().is_none() + && inherit_stmt.idents().count() == 0 + { + let at = node.text_range(); + let replacement = make::empty().node().clone(); + let replacement_at = utils::with_preceeding_whitespace(node); + let message = "Remove this empty `inherit` statement"; + Some( + self.report() + .suggest(at, message, Suggestion::new(replacement_at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/empty_let_in.rs b/lib/src/lints/empty_let_in.rs index 460e369..4624920 100644 --- a/lib/src/lints/empty_let_in.rs +++ b/lib/src/lints/empty_let_in.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -35,33 +34,30 @@ struct EmptyLetIn; impl Rule for EmptyLetIn { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(let_in_expr) = LetIn::cast(node.clone()); - let entries = let_in_expr.entries(); - let inherits = let_in_expr.inherits(); - - if entries.count() == 0; - if inherits.count() == 0; - - if let Some(body) = let_in_expr.body(); - + if let NodeOrToken::Node(node) = node + && let Some(let_in_expr) = LetIn::cast(node.clone()) + && let entries = let_in_expr.entries() + && let inherits = let_in_expr.inherits() + && entries.count() == 0 + && inherits.count() == 0 + && let Some(body) = let_in_expr.body() + { // ensure that the let-in-expr does not have comments let has_comments = node .children_with_tokens() .any(|el| el.kind() == SyntaxKind::TOKEN_COMMENT); - then { - let at = node.text_range(); - let replacement = body; - let message = "This let-in expression has no entries"; - Some(if has_comments { - self.report().diagnostic(at, message) - } else { - self.report().suggest(at, message, Suggestion::new(at, replacement)) - }) + + let at = node.text_range(); + let replacement = body; + let message = "This let-in expression has no entries"; + Some(if has_comments { + self.report().diagnostic(at, message) } else { - None - } + self.report() + .suggest(at, message, Suggestion::new(at, replacement)) + }) + } else { + None } } } diff --git a/lib/src/lints/empty_list_concat.rs b/lib/src/lints/empty_list_concat.rs index 3ff7e40..4885809 100644 --- a/lib/src/lints/empty_list_concat.rs +++ b/lib/src/lints/empty_list_concat.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, @@ -33,26 +32,24 @@ struct EmptyListConcat; impl Rule for EmptyListConcat { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(bin_expr) = BinOp::cast(node.clone()); - if let Some(lhs) = bin_expr.lhs(); - if let Some(rhs) = bin_expr.rhs(); - if let Some(op) = bin_expr.operator(); - if let BinOpKind::Concat = op; - then { - let at = node.text_range(); - let message = "Concatenation with the empty list, `[]`, is a no-op"; - if is_empty_array(&lhs) { - Some(self.report().suggest(at, message, Suggestion::new(at, rhs))) - } else if is_empty_array(&rhs) { - Some(self.report().suggest(at, message, Suggestion::new(at, lhs))) - } else { - None - } + if let NodeOrToken::Node(node) = node + && let Some(bin_expr) = BinOp::cast(node.clone()) + && let Some(lhs) = bin_expr.lhs() + && let Some(rhs) = bin_expr.rhs() + && let Some(op) = bin_expr.operator() + && let BinOpKind::Concat = op + { + let at = node.text_range(); + let message = "Concatenation with the empty list, `[]`, is a no-op"; + if is_empty_array(&lhs) { + Some(self.report().suggest(at, message, Suggestion::new(at, rhs))) + } else if is_empty_array(&rhs) { + Some(self.report().suggest(at, message, Suggestion::new(at, lhs))) } else { None } + } else { + None } } } diff --git a/lib/src/lints/empty_pattern.rs b/lib/src/lints/empty_pattern.rs index 6d54e4a..7664d7a 100644 --- a/lib/src/lints/empty_pattern.rs +++ b/lib/src/lints/empty_pattern.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, @@ -42,45 +41,40 @@ struct EmptyPattern; impl Rule for EmptyPattern { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(lambda_expr) = Lambda::cast(node.clone()); - if let Some(arg) = lambda_expr.arg(); - if let Some(body) = lambda_expr.body(); - - if let Some(pattern) = Pattern::cast(arg); - + if let NodeOrToken::Node(node) = node + && let Some(lambda_expr) = Lambda::cast(node.clone()) + && let Some(arg) = lambda_expr.arg() + && let Some(body) = lambda_expr.body() + && let Some(pattern) = Pattern::cast(arg) // no patterns within `{ }` - if pattern.entries().count() == 0; + && pattern.entries().count() == 0 // pattern is not bound - if pattern.at().is_none(); - + && pattern.at().is_none() // not a nixos module - if !is_module(&body); - - then { - let at = pattern.node().text_range(); - let message = "This pattern is empty, use `_` instead"; - let replacement = make::ident("_").node().clone(); - Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) - } else { - None - } + && !is_module(&body) + { + let at = pattern.node().text_range(); + let message = "This pattern is empty, use `_` instead"; + let replacement = make::ident("_").node().clone(); + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } fn is_module(body: &SyntaxNode) -> bool { - if_chain! { - if let Some(attr_set) = AttrSet::cast(body.clone()); - if attr_set + if let Some(attr_set) = AttrSet::cast(body.clone()) + && attr_set .entries() .filter_map(|e| e.key()) - .any(|k| k.node().to_string() == "imports"); - then { - true - } else { - false - } + .any(|k| k.node().to_string() == "imports") + { + true + } else { + false } } diff --git a/lib/src/lints/eta_reduction.rs b/lib/src/lints/eta_reduction.rs index 7a12126..0124b86 100644 --- a/lib/src/lints/eta_reduction.rs +++ b/lib/src/lints/eta_reduction.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, @@ -43,39 +42,30 @@ struct EtaReduction; impl Rule for EtaReduction { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(lambda_expr) = Lambda::cast(node.clone()); - - if let Some(arg_node) = lambda_expr.arg(); - if let Some(arg) = Ident::cast(arg_node); - - if let Some(body_node) = lambda_expr.body(); - if let Some(body) = Apply::cast(body_node); - - if let Some(value_node) = body.value(); - if let Some(value) = Ident::cast(value_node); - - if arg.as_str() == value.as_str(); - - if let Some(lambda_node) = body.lambda(); - if !mentions_ident(&arg, &lambda_node); - // lambda body should be no more than a single Ident to - // retain code readability - if let Some(_) = Ident::cast(lambda_node); - - then { - let at = node.text_range(); - let replacement = body.lambda()?; - let message = - format!( - "Found eta-reduction: `{}`", - replacement.text() - ); - Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) - } else { - None - } + if let NodeOrToken::Node(node) = node + && let Some(lambda_expr) = Lambda::cast(node.clone()) + && let Some(arg_node) = lambda_expr.arg() + && let Some(arg) = Ident::cast(arg_node) + && let Some(body_node) = lambda_expr.body() + && let Some(body) = Apply::cast(body_node) + && let Some(value_node) = body.value() + && let Some(value) = Ident::cast(value_node) + && arg.as_str() == value.as_str() + && let Some(lambda_node) = body.lambda() + && !mentions_ident(&arg, &lambda_node) + // lambda body should be no more than a single Ident to + // retain code readability + && let Some(_) = Ident::cast(lambda_node) + { + let at = node.text_range(); + let replacement = body.lambda()?; + let message = format!("Found eta-reduction: `{}`", replacement.text()); + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/legacy_let_syntax.rs b/lib/src/lints/legacy_let_syntax.rs index b33043e..4028cc1 100644 --- a/lib/src/lints/legacy_let_syntax.rs +++ b/lib/src/lints/legacy_let_syntax.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -45,29 +44,28 @@ struct ManualInherit; impl Rule for ManualInherit { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(legacy_let) = LegacyLet::cast(node.clone()); - - if legacy_let + if let NodeOrToken::Node(node) = node + && let Some(legacy_let) = LegacyLet::cast(node.clone()) + && legacy_let .entries() - .any(|kv| matches!(kv.key(), Some(k) if key_is_ident(&k, "body"))); - - then { - let inherits = legacy_let.inherits(); - let entries = legacy_let.entries(); - let attrset = make::attrset(inherits, entries, true); - let parenthesized = make::parenthesize(attrset.node()); - let selected = make::select(parenthesized.node(), make::ident("body").node()); + .any(|kv| matches!(kv.key(), Some(k) if key_is_ident(&k, "body"))) + { + let inherits = legacy_let.inherits(); + let entries = legacy_let.entries(); + let attrset = make::attrset(inherits, entries, true); + let parenthesized = make::parenthesize(attrset.node()); + let selected = make::select(parenthesized.node(), make::ident("body").node()); - let at = node.text_range(); - let message = "Prefer `rec` over undocumented `let` syntax"; - let replacement = selected.node().clone(); + let at = node.text_range(); + let message = "Prefer `rec` over undocumented `let` syntax"; + let replacement = selected.node().clone(); - Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) - } else { - None - } + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/manual_inherit.rs b/lib/src/lints/manual_inherit.rs index 9b9651c..fbafc07 100644 --- a/lib/src/lints/manual_inherit.rs +++ b/lib/src/lints/manual_inherit.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -41,28 +40,28 @@ struct ManualInherit; impl Rule for ManualInherit { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(key_value_stmt) = KeyValue::cast(node.clone()); - if let mut key_path = key_value_stmt.key()?.path(); - if let Some(key_node) = key_path.next(); + if let NodeOrToken::Node(node) = node + && let Some(key_value_stmt) = KeyValue::cast(node.clone()) + && let mut key_path = key_value_stmt.key()?.path() + && let Some(key_node) = key_path.next() // ensure that path has exactly one component - if key_path.next().is_none(); - if let Some(key) = Ident::cast(key_node); + && key_path.next().is_none() + && let Some(key) = Ident::cast(key_node) - if let Some(value_node) = key_value_stmt.value(); - if let Some(value) = Ident::cast(value_node); + && let Some(value_node) = key_value_stmt.value() + && let Some(value) = Ident::cast(value_node) - if key.as_str() == value.as_str(); - - then { - let at = node.text_range(); - let replacement = make::inherit_stmt(&[key]).node().clone(); - let message = "This assignment is better written with `inherit`"; - Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) - } else { - None - } + && key.as_str() == value.as_str() + { + let at = node.text_range(); + let replacement = make::inherit_stmt(&[key]).node().clone(); + let message = "This assignment is better written with `inherit`"; + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/manual_inherit_from.rs b/lib/src/lints/manual_inherit_from.rs index f1814c8..a0fbe5c 100644 --- a/lib/src/lints/manual_inherit_from.rs +++ b/lib/src/lints/manual_inherit_from.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -41,33 +40,31 @@ struct ManualInherit; impl Rule for ManualInherit { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(key_value_stmt) = KeyValue::cast(node.clone()); - if let mut key_path = key_value_stmt.key()?.path(); - if let Some(key_node) = key_path.next(); + if let NodeOrToken::Node(node) = node + && let Some(key_value_stmt) = KeyValue::cast(node.clone()) + && let mut key_path = key_value_stmt.key()?.path() + && let Some(key_node) = key_path.next() // ensure that path has exactly one component - if key_path.next().is_none(); - if let Some(key) = Ident::cast(key_node); - - if let Some(value_node) = key_value_stmt.value(); - if let Some(value) = Select::cast(value_node); - if let Some(index_node) = value.index(); - if let Some(index) = Ident::cast(index_node); - - if key.as_str() == index.as_str(); - - then { - let at = node.text_range(); - let replacement = { - let set = value.set()?; - make::inherit_from_stmt(&set, &[key]).node().clone() - }; - let message = "This assignment is better written with `inherit`"; - Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) - } else { - None - } + && key_path.next().is_none() + && let Some(key) = Ident::cast(key_node) + && let Some(value_node) = key_value_stmt.value() + && let Some(value) = Select::cast(value_node) + && let Some(index_node) = value.index() + && let Some(index) = Ident::cast(index_node) + && key.as_str() == index.as_str() + { + let at = node.text_range(); + let replacement = { + let set = value.set()?; + make::inherit_from_stmt(&set, &[key]).node().clone() + }; + let message = "This assignment is better written with `inherit`"; + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/redundant_pattern_bind.rs b/lib/src/lints/redundant_pattern_bind.rs index b55a3fa..d50ac32 100644 --- a/lib/src/lints/redundant_pattern_bind.rs +++ b/lib/src/lints/redundant_pattern_bind.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -36,25 +35,27 @@ struct RedundantPatternBind; impl Rule for RedundantPatternBind { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(pattern) = Pattern::cast(node.clone()); + if let NodeOrToken::Node(node) = node + && let Some(pattern) = Pattern::cast(node.clone()) // no patterns within `{ }` - if pattern.entries().count() == 0; - + && pattern.entries().count() == 0 // pattern is just ellipsis - if pattern.ellipsis(); - + && pattern.ellipsis() // pattern is bound - if let Some(ident) = pattern.at(); - then { - let at = node.text_range(); - let message = format!("This pattern bind is redundant, use `{}` instead", ident.as_str()); - let replacement = ident.node().clone(); - Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) - } else { - None - } + && let Some(ident) = pattern.at() + { + let at = node.text_range(); + let message = format!( + "This pattern bind is redundant, use `{}` instead", + ident.as_str() + ); + let replacement = ident.node().clone(); + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/repeated_keys.rs b/lib/src/lints/repeated_keys.rs index b1617f3..2dd4714 100644 --- a/lib/src/lints/repeated_keys.rs +++ b/lib/src/lints/repeated_keys.rs @@ -2,7 +2,6 @@ use std::fmt::Write as _; use crate::{Metadata, Report, Rule, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -46,21 +45,18 @@ struct RepeatedKeys; impl Rule for RepeatedKeys { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(key_value) = KeyValue::cast(node.clone()); - if let Some(key) = key_value.key(); - if let mut components = key.path(); - if let Some(first_component) = components.next(); - if let Some(first_component_ident) = Ident::cast(first_component); + if let NodeOrToken::Node(node) = node + && let Some(key_value) = KeyValue::cast(node.clone()) + && let Some(key) = key_value.key() + && let mut components = key.path() + && let Some(first_component) = components.next() + && let Some(first_component_ident) = Ident::cast(first_component) // ensure that there are >1 components - if components.next().is_some(); - - if let Some(parent_node) = node.parent(); - if let Some(parent_attr_set) = AttrSet::cast(parent_node); - - if !parent_attr_set.recursive(); - let occurrences = parent_attr_set.entries().filter_map(|kv_scrutinee| { + && components.next().is_some() + && let Some(parent_node) = node.parent() + && let Some(parent_attr_set) = AttrSet::cast(parent_node) + && !parent_attr_set.recursive() + && let occurrences = parent_attr_set.entries().filter_map(|kv_scrutinee| { let scrutinee_key = kv_scrutinee.key()?; let mut kv_scrutinee_components = scrutinee_key.path(); let kv_scrutinee_first_component = kv_scrutinee_components.next()?; @@ -76,41 +72,49 @@ impl Rule for RepeatedKeys { } else { None } - }).collect::>(); - - if occurrences.first()?.0 == key.node().text_range(); - if occurrences.len() >= 3; + }).collect::>() + && occurrences.first()?.0 == key.node().text_range() + && occurrences.len() >= 3 + { + let mut iter = occurrences.into_iter(); - then { - let mut iter = occurrences.into_iter(); + let (first_annotation, first_subkey) = iter.next().unwrap(); + let first_message = format!( + "The key `{}` is first assigned here ...", + first_component_ident.as_str() + ); - let (first_annotation, first_subkey) = iter.next().unwrap(); - let first_message = format!("The key `{}` is first assigned here ...", first_component_ident.as_str()); + let (second_annotation, second_subkey) = iter.next().unwrap(); + let second_message = "... repeated here ..."; - let (second_annotation, second_subkey) = iter.next().unwrap(); - let second_message = "... repeated here ..."; - - let (third_annotation, third_subkey) = iter.next().unwrap(); - let third_message = { - let remaining_occurrences = iter.count(); - let mut message = match remaining_occurrences { - 0 => "... and here.".to_string(), - 1 => "... and here (`1` occurrence omitted).".to_string(), - n => format!("... and here (`{n}` occurrences omitted)."), - }; - write!(message, " Try `{} = {{ {}=...; {}=...; {}=...; }}` instead.", first_component_ident.as_str(), first_subkey, second_subkey, third_subkey).unwrap(); - message + let (third_annotation, third_subkey) = iter.next().unwrap(); + let third_message = { + let remaining_occurrences = iter.count(); + let mut message = match remaining_occurrences { + 0 => "... and here.".to_string(), + 1 => "... and here (`1` occurrence omitted).".to_string(), + n => format!("... and here (`{n}` occurrences omitted)."), }; + write!( + message, + " Try `{} = {{ {}=...; {}=...; {}=...; }}` instead.", + first_component_ident.as_str(), + first_subkey, + second_subkey, + third_subkey + ) + .unwrap(); + message + }; - Some( - self.report() + Some( + self.report() .diagnostic(first_annotation, first_message) .diagnostic(second_annotation, second_message) .diagnostic(third_annotation, third_message), - ) - } else { - None - } + ) + } else { + None } } } diff --git a/lib/src/lints/unquoted_splice.rs b/lib/src/lints/unquoted_splice.rs index 1cd6003..c8b7bcf 100644 --- a/lib/src/lints/unquoted_splice.rs +++ b/lib/src/lints/unquoted_splice.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -41,17 +40,18 @@ struct UnquotedSplice; impl Rule for UnquotedSplice { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if Dynamic::cast(node.clone()).is_some(); - then { - let at = node.text_range(); - let replacement = make::quote(node).node().clone(); - let message = "Consider quoting this splice expression"; - Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) - } else { - None - } + if let NodeOrToken::Node(node) = node + && Dynamic::cast(node.clone()).is_some() + { + let at = node.text_range(); + let replacement = make::quote(node).node().clone(); + let message = "Consider quoting this splice expression"; + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/unquoted_uri.rs b/lib/src/lints/unquoted_uri.rs index d11a651..4c25d46 100644 --- a/lib/src/lints/unquoted_uri.rs +++ b/lib/src/lints/unquoted_uri.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{NodeOrToken, SyntaxElement, SyntaxKind, types::TypedNode}; @@ -47,17 +46,17 @@ struct UnquotedUri; impl Rule for UnquotedUri { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Token(token) = node; - then { - let parent_node = token.parent(); - let at = token.text_range(); - let replacement = make::quote(&parent_node).node().clone(); - let message = "Consider quoting this URI expression"; - Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) - } else { - None - } + if let NodeOrToken::Token(token) = node { + let parent_node = token.parent(); + let at = token.text_range(); + let replacement = make::quote(&parent_node).node().clone(); + let message = "Consider quoting this URI expression"; + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/useless_has_attr.rs b/lib/src/lints/useless_has_attr.rs index 41eab35..b157e9d 100644 --- a/lib/src/lints/useless_has_attr.rs +++ b/lib/src/lints/useless_has_attr.rs @@ -1,6 +1,5 @@ use crate::{Metadata, Report, Rule, Suggestion, make, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -34,50 +33,48 @@ struct UselessHasAttr; impl Rule for UselessHasAttr { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(if_else_expr) = IfElse::cast(node.clone()); - if let Some(condition_expr) = if_else_expr.condition(); - if let Some(default_expr) = if_else_expr.else_body(); - if let Some(cond_bin_expr) = BinOp::cast(condition_expr); - if let Some(BinOpKind::IsSet) = cond_bin_expr.operator(); + if let NodeOrToken::Node(node) = node + && let Some(if_else_expr) = IfElse::cast(node.clone()) + && let Some(condition_expr) = if_else_expr.condition() + && let Some(default_expr) = if_else_expr.else_body() + && let Some(cond_bin_expr) = BinOp::cast(condition_expr) + && let Some(BinOpKind::IsSet) = cond_bin_expr.operator() // set ? attr_path // ^^^--------------- lhs // ^^^^^^^^^^--- rhs - if let Some(set) = cond_bin_expr.lhs(); - if let Some(attr_path) = cond_bin_expr.rhs(); + && let Some(set) = cond_bin_expr.lhs() + && let Some(attr_path) = cond_bin_expr.rhs() // check if body of the `if` expression is of the form `set.attr_path` - if let Some(body_expr) = if_else_expr.body(); - if let Some(body_select_expr) = Select::cast(body_expr); - let expected_body = make::select(&set, &attr_path); + && let Some(body_expr) = if_else_expr.body() + && let Some(body_select_expr) = Select::cast(body_expr) + &&let expected_body = make::select(&set, &attr_path) // text comparison will do for now - if body_select_expr.node().text() == expected_body.node().text(); - then { - let at = node.text_range(); - // `or` is tightly binding, we need to parenthesize non-literal exprs - let default_with_parens = match default_expr.kind() { - SyntaxKind::NODE_LIST - | SyntaxKind::NODE_PAREN - | SyntaxKind::NODE_STRING - | SyntaxKind::NODE_ATTR_SET - | SyntaxKind::NODE_IDENT - | SyntaxKind::NODE_SELECT => default_expr, - _ => make::parenthesize(&default_expr).node().clone(), - }; - let replacement = make::or_default(&set, &attr_path, &default_with_parens).node().clone(); - let message = format!( - "Consider using `{replacement}` instead of this `if` expression" - ); - Some( - self.report() - .suggest(at, message, Suggestion::new(at, replacement)), - ) - } else { - None - } + && body_select_expr.node().text() == expected_body.node().text() + { + let at = node.text_range(); + // `or` is tightly binding, we need to parenthesize non-literal exprs + let default_with_parens = match default_expr.kind() { + SyntaxKind::NODE_LIST + | SyntaxKind::NODE_PAREN + | SyntaxKind::NODE_STRING + | SyntaxKind::NODE_ATTR_SET + | SyntaxKind::NODE_IDENT + | SyntaxKind::NODE_SELECT => default_expr, + _ => make::parenthesize(&default_expr).node().clone(), + }; + let replacement = make::or_default(&set, &attr_path, &default_with_parens) + .node() + .clone(); + let message = format!("Consider using `{replacement}` instead of this `if` expression"); + Some( + self.report() + .suggest(at, message, Suggestion::new(at, replacement)), + ) + } else { + None } } } diff --git a/lib/src/lints/useless_parens.rs b/lib/src/lints/useless_parens.rs index 14be74f..1756196 100644 --- a/lib/src/lints/useless_parens.rs +++ b/lib/src/lints/useless_parens.rs @@ -1,6 +1,5 @@ use crate::{Diagnostic, Metadata, Report, Rule, Suggestion, session::SessionInfo}; -use if_chain::if_chain; use macros::lint; use rnix::{ NodeOrToken, SyntaxElement, SyntaxKind, @@ -46,67 +45,71 @@ struct UselessParens; impl Rule for UselessParens { fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option { - if_chain! { - if let NodeOrToken::Node(node) = node; - if let Some(parsed_type_node) = ParsedType::cast(node.clone()); - - if let Some(diagnostic) = do_thing(parsed_type_node); - then { - let mut report = self.report(); - report.diagnostics.push(diagnostic); - Some(report) - } else { - None - } + if let NodeOrToken::Node(node) = node + && let Some(parsed_type_node) = ParsedType::cast(node.clone()) + && let Some(diagnostic) = do_thing(parsed_type_node) + { + let mut report = self.report(); + report.diagnostics.push(diagnostic); + Some(report) + } else { + None } } } fn do_thing(parsed_type_node: ParsedType) -> Option { match parsed_type_node { - ParsedType::KeyValue(kv) => if_chain! { - if let Some(value_node) = kv.value(); - let value_range = value_node.text_range(); - if let Some(value_in_parens) = Paren::cast(value_node); - if let Some(inner) = value_in_parens.inner(); - then { + ParsedType::KeyValue(kv) => { + if let Some(value_node) = kv.value() + && let value_range = value_node.text_range() + && let Some(value_in_parens) = Paren::cast(value_node) + && let Some(inner) = value_in_parens.inner() + { let at = value_range; let message = "Useless parentheses around value in binding"; let replacement = inner; - Some(Diagnostic::suggest(at, message, Suggestion::new(at, replacement))) + Some(Diagnostic::suggest( + at, + message, + Suggestion::new(at, replacement), + )) } else { None } - }, - ParsedType::LetIn(let_in) => if_chain! { - if let Some(body_node) = let_in.body(); - let body_range = body_node.text_range(); - if let Some(body_as_parens) = Paren::cast(body_node); - if let Some(inner) = body_as_parens.inner(); - then { + } + ParsedType::LetIn(let_in) => { + if let Some(body_node) = let_in.body() + && let body_range = body_node.text_range() + && let Some(body_as_parens) = Paren::cast(body_node) + && let Some(inner) = body_as_parens.inner() + { let at = body_range; let message = "Useless parentheses around body of `let` expression"; let replacement = inner; - Some(Diagnostic::suggest(at, message, Suggestion::new(at, replacement))) + Some(Diagnostic::suggest( + at, + message, + Suggestion::new(at, replacement), + )) } else { None } - }, - ParsedType::Paren(paren_expr) => if_chain! { + } + ParsedType::Paren(paren_expr) => { let paren_expr_range = paren_expr.node().text_range(); - if let Some(father_node) = paren_expr.node().parent(); - + if let Some(father_node) = paren_expr.node().parent() // ensure that we don't lint inside let-in statements // we already lint such cases in previous match stmt - if KeyValue::cast(father_node.clone()).is_none(); + && KeyValue::cast(father_node.clone()).is_none() // ensure that we don't lint inside let-bodies // if this primitive is a let-body, we have already linted it - if LetIn::cast(father_node).is_none(); + && LetIn::cast(father_node).is_none() - if let Some(inner_node) = paren_expr.inner(); - if let Some(parsed_inner) = ParsedType::cast(inner_node); - if matches!( + && let Some(inner_node) = paren_expr.inner() + && let Some(parsed_inner) = ParsedType::cast(inner_node) + && matches!( parsed_inner, ParsedType::List(_) | ParsedType::Paren(_) @@ -114,16 +117,19 @@ fn do_thing(parsed_type_node: ParsedType) -> Option { | ParsedType::AttrSet(_) | ParsedType::Select(_) | ParsedType::Ident(_) - ); - then { + ) { let at = paren_expr_range; let message = "Useless parentheses around primitive expression"; let replacement = parsed_inner.node().clone(); - Some(Diagnostic::suggest(at, message, Suggestion::new(at, replacement))) + Some(Diagnostic::suggest( + at, + message, + Suggestion::new(at, replacement), + )) } else { None } - }, + } _ => None, } }