···3131 · ╰──────── Concatenation with the empty list, [], is a no-op
3232────╯
3333[W23] Warning: Unnecessary concatenation with empty list
3434- ╭─[data/empty_list_concat.nix:15:4]
3434+ ╭─[data/empty_list_concat.nix:15:10]
3535 │
3636 15 │ ([] ++ [] ++ [])
3737- · ────┬───
3838- · ╰───── Concatenation with the empty list, [], is a no-op
3737+ · ────┬───
3838+ · ╰───── Concatenation with the empty list, [], is a no-op
3939────╯
+13-18
lib/src/lints/bool_comparison.rs
···33use macros::lint;
44use rnix::{
55 NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
66- types::{BinOp, BinOpKind, Ident, TokenWrapper, TypedNode},
66+ ast::{BinOp, BinOpKind, Ident},
77};
88+use rowan::ast::AstNode as _;
89910/// ## What it does
1011/// Checks for expressions of the form `x == true`, `x != true` and
···4041 };
4142 let bin_expr = BinOp::cast(node.clone())?;
4243 let (lhs, rhs) = (bin_expr.lhs()?, bin_expr.rhs()?);
4444+ let (lhs, rhs) = (lhs.syntax(), rhs.syntax());
4345 let op = EqualityBinOpKind::try_from(bin_expr.operator()?)?;
44464547 let (bool_side, non_bool_side): (NixBoolean, &SyntaxNode) =
4646- match (boolean_ident(&lhs), boolean_ident(&rhs)) {
4848+ match (boolean_ident(lhs), boolean_ident(rhs)) {
4749 (None, None) => return None,
4848- (None, Some(bool)) => (bool, &lhs),
4949- (Some(bool), _) => (bool, &rhs),
5050+ (None, Some(bool)) => (bool, lhs),
5151+ (Some(bool), _) => (bool, rhs),
5052 };
51535254 let replacement = match (&bool_side, op) {
···5961 | (NixBoolean::False, EqualityBinOpKind::Equal) => {
6062 // `a != true`, `a == false` replace with `!a`
6163 match non_bool_side.kind() {
6262- SyntaxKind::NODE_APPLY | SyntaxKind::NODE_PAREN | SyntaxKind::NODE_IDENT => {
6464+ SyntaxKind::NODE_APPLY
6565+ | SyntaxKind::NODE_PAREN
6666+ | SyntaxKind::NODE_IDENT
6767+ | SyntaxKind::NODE_HAS_ATTR => {
6368 // do not parenthsize the replacement
6464- make::unary_not(non_bool_side).node().clone()
6565- }
6666- SyntaxKind::NODE_BIN_OP => {
6767- let inner = BinOp::cast(non_bool_side.clone()).unwrap();
6868- // `!a ? b`, no paren required
6969- if inner.operator()? == BinOpKind::IsSet {
7070- make::unary_not(non_bool_side).node().clone()
7171- } else {
7272- let parens = make::parenthesize(non_bool_side);
7373- make::unary_not(parens.node()).node().clone()
7474- }
6969+ make::unary_not(non_bool_side).syntax().clone()
7570 }
7671 _ => {
7772 let parens = make::parenthesize(non_bool_side);
7878- make::unary_not(parens.node()).node().clone()
7373+ make::unary_not(parens.syntax()).syntax().clone()
7974 }
8075 }
8176 }
···125120126121// not entirely accurate, underhanded nix programmers might write `true = false`
127122fn boolean_ident(node: &SyntaxNode) -> Option<NixBoolean> {
128128- Ident::cast(node.clone()).and_then(|ident_expr| match ident_expr.as_str() {
123123+ Ident::cast(node.clone()).and_then(|ident_expr| match ident_expr.to_string().as_str() {
129124 "true" => Some(NixBoolean::True),
130125 "false" => Some(NixBoolean::False),
131126 _ => None,
+17-7
lib/src/lints/bool_simplification.rs
···33use macros::lint;
44use rnix::{
55 NodeOrToken, SyntaxElement, SyntaxKind,
66- types::{BinOp, BinOpKind, Paren, TypedNode, UnaryOp, UnaryOpKind, Wrapper},
66+ ast::{BinOpKind, Expr, UnaryOp, UnaryOpKind},
77};
88+use rowan::ast::AstNode as _;
89910/// ## What it does
1011/// Checks for boolean expressions that can be simplified.
···38393940 let unary_expr = UnaryOp::cast(node.clone())?;
40414141- if unary_expr.operator() != UnaryOpKind::Invert {
4242+ if unary_expr.operator() != Some(UnaryOpKind::Invert) {
4243 return None;
4344 }
44454545- let value_expr = unary_expr.value()?;
4646- let paren_expr = Paren::cast(value_expr)?;
4747- let inner_expr = paren_expr.inner()?;
4848- let bin_expr = BinOp::cast(inner_expr)?;
4646+ let value_expr = unary_expr.expr()?;
4747+4848+ let Expr::Paren(paren_expr) = value_expr else {
4949+ return None;
5050+ };
5151+5252+ let inner_expr = paren_expr.expr()?;
5353+5454+ let Expr::BinOp(bin_expr) = inner_expr else {
5555+ return None;
5656+ };
49575058 let Some(BinOpKind::Equal) = bin_expr.operator() else {
5159 return None;
···56645765 let lhs = bin_expr.lhs()?;
5866 let rhs = bin_expr.rhs()?;
5959- let replacement = make::binary(&lhs, "!=", &rhs).node().clone();
6767+ let replacement = make::binary(lhs.syntax(), "!=", rhs.syntax())
6868+ .syntax()
6969+ .clone();
6070 Some(
6171 self.report()
6272 .suggest(at, message, Suggestion::with_replacement(at, replacement)),
+8-4
lib/src/lints/collapsible_let_in.rs
···33use macros::lint;
44use rnix::{
55 NodeOrToken, SyntaxElement, SyntaxKind, TextRange,
66- types::{LetIn, TypedNode},
66+ ast::{Expr, LetIn},
77};
88-use rowan::Direction;
88+use rowan::{Direction, ast::AstNode as _};
991010/// ## What it does
1111/// Checks for `let-in` expressions whose body is another `let-in`
···5252 let let_in_expr = LetIn::cast(node.clone())?;
5353 let body = let_in_expr.body()?;
54545555- LetIn::cast(body.clone())?;
5555+ let Expr::LetIn(_) = body else {
5656+ return None;
5757+ };
56585759 let first_annotation = node.text_range();
5860 let first_message = "This `let in` expression contains a nested `let in` expression";
59616060- let second_annotation = body.text_range();
6262+ let second_annotation = body.syntax().text_range();
6163 let second_message = "This `let in` expression is nested";
62646365 let replacement_at = {
6466 let start = body
6767+ .syntax()
6568 .siblings_with_tokens(Direction::Prev)
6669 .find(|elem| elem.kind() == SyntaxKind::TOKEN_IN)?
6770 .text_range()
6871 .start();
6972 let end = body
7373+ .syntax()
7074 .descendants_with_tokens()
7175 .find(|elem| elem.kind() == SyntaxKind::TOKEN_LET)?
7276 .text_range()
+2-4
lib/src/lints/deprecated_to_path.rs
···11use crate::{Metadata, Report, Rule, session::SessionInfo};
2233use macros::lint;
44-use rnix::{
55- NodeOrToken, SyntaxElement, SyntaxKind,
66- types::{Apply, TypedNode},
77-};
44+use rnix::{NodeOrToken, SyntaxElement, SyntaxKind, ast::Apply};
55+use rowan::ast::AstNode as _;
8697/// ## What it does
108/// Checks for usage of the `toPath` function.
+3-5
lib/src/lints/empty_inherit.rs
···11use crate::{Metadata, Report, Rule, Suggestion, session::SessionInfo, utils};
2233use macros::lint;
44-use rnix::{
55- NodeOrToken, SyntaxElement, SyntaxKind,
66- types::{Inherit, TypedNode},
77-};
44+use rnix::{NodeOrToken, SyntaxElement, SyntaxKind, ast::Inherit};
55+use rowan::ast::AstNode as _;
8697/// ## What it does
108/// Checks for empty inherit statements.
···3937 return None;
4038 }
41394242- if inherit_stmt.idents().count() != 0 {
4040+ if inherit_stmt.attrs().count() != 0 {
4341 return None;
4442 }
4543
+8-4
lib/src/lints/empty_let_in.rs
···33use macros::lint;
44use rnix::{
55 NodeOrToken, SyntaxElement, SyntaxKind,
66- types::{EntryHolder, LetIn, TypedNode},
66+ ast::{HasEntry as _, LetIn},
77};
88+use rowan::ast::AstNode as _;
89910/// ## What it does
1011/// Checks for `let-in` expressions which create no new bindings.
···4849 .any(|el| el.kind() == SyntaxKind::TOKEN_COMMENT);
49505051 let at = node.text_range();
5151- let replacement = body;
5252+ let replacement = body.syntax();
5253 let message = "This let-in expression has no entries";
5354 Some(if has_comments {
5455 self.report().diagnostic(at, message)
5556 } else {
5656- self.report()
5757- .suggest(at, message, Suggestion::with_replacement(at, replacement))
5757+ self.report().suggest(
5858+ at,
5959+ message,
6060+ Suggestion::with_replacement(at, replacement.clone()),
6161+ )
5862 })
5963 } else {
6064 None