Lints and suggestions for the Nix programming language
at main 75 lines 1.8 kB view raw
1use crate::{Metadata, Report, Rule, Suggestion, make}; 2 3use macros::lint; 4use rnix::{ 5 NodeOrToken, SyntaxElement, SyntaxKind, 6 ast::{BinOpKind, Expr, UnaryOp, UnaryOpKind}, 7}; 8use rowan::ast::AstNode as _; 9 10/// ## What it does 11/// Checks for boolean expressions that can be simplified. 12/// 13/// ## Why is this bad? 14/// Complex booleans affect readibility. 15/// 16/// ## Example 17/// ```nix 18/// if !(x == y) then 0 else 1 19/// ``` 20/// 21/// Use `!=` instead: 22/// 23/// ```nix 24/// if x != y then 0 else 1 25/// ``` 26#[lint( 27 name = "bool_simplification", 28 note = "This boolean expression can be simplified", 29 code = 18, 30 match_with = SyntaxKind::NODE_UNARY_OP 31)] 32struct BoolSimplification; 33 34impl Rule for BoolSimplification { 35 fn validate(&self, node: &SyntaxElement) -> Option<Report> { 36 let NodeOrToken::Node(node) = node else { 37 return None; 38 }; 39 40 let unary_expr = UnaryOp::cast(node.clone())?; 41 42 if unary_expr.operator() != Some(UnaryOpKind::Invert) { 43 return None; 44 } 45 46 let value_expr = unary_expr.expr()?; 47 48 let Expr::Paren(paren_expr) = value_expr else { 49 return None; 50 }; 51 52 let inner_expr = paren_expr.expr()?; 53 54 let Expr::BinOp(bin_expr) = inner_expr else { 55 return None; 56 }; 57 58 let Some(BinOpKind::Equal) = bin_expr.operator() else { 59 return None; 60 }; 61 62 let at = node.text_range(); 63 let message = "Try `!=` instead of `!(... == ...)`"; 64 65 let lhs = bin_expr.lhs()?; 66 let rhs = bin_expr.rhs()?; 67 let replacement = make::binary(lhs.syntax(), "!=", rhs.syntax()) 68 .syntax() 69 .clone(); 70 Some( 71 self.report() 72 .suggest(at, message, Suggestion::with_replacement(at, replacement)), 73 ) 74 } 75}