Lints and suggestions for the Nix programming language
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

new lint: faster_zipattrswith

Akshay a254edfc effe06e2

+109 -2
+13
bin/tests/data/faster_zipattrswith.nix
··· 1 + { 2 + # trivial case 3 + _ = lib.zipAttrsWith (name: values: values) [{ a = 1; } { a = 2; b = 3; }]; 4 + 5 + # offer lint heuristically on this too 6 + _ = nixpkgs.lib.zipAttrsWith (name: values: values) [{ a = 1; } { a = 2; b = 3; }]; 7 + 8 + # do not lint on `builtins` 9 + _ = builtins.zipAttrsWith (name: values: values) [ 10 + { a = 1; } 11 + { a = 2; b = 3; } 12 + ]; 13 + }
+3 -2
bin/tests/main.rs
··· 19 19 test_lint!($($tail)*); 20 20 }; 21 21 ($tname:ident) => { 22 - test_lint!($tname => session_info!("2.5")); 22 + test_lint!($tname => session_info!("2.6")); 23 23 }; 24 24 ($tname:ident => $sess:expr) => { 25 25 #[test] ··· 61 61 unquoted_uri, 62 62 deprecated_is_null, 63 63 empty_inherit, 64 - faster_groupby => session_info!("2.5") 64 + faster_groupby => session_info!("2.5"), 65 + faster_zipattrswith => session_info!("2.6") 65 66 }
+20
bin/tests/snapshots/main__faster_zipattrswith.snap
··· 1 + --- 2 + source: bin/tests/main.rs 3 + expression: "&out" 4 + 5 + --- 6 + [W16] Warning: Found lib.zipAttrsWith 7 + ╭─[data/faster_zipattrswith.nix:3:7] 8 + 9 + 3 │ _ = lib.zipAttrsWith (name: values: values) [{ a = 1; } { a = 2; b = 3; }]; 10 + · ────────┬─────── 11 + · ╰───────── Prefer builtins.zipAttrsWith over lib.zipAttrsWith 12 + ───╯ 13 + [W16] Warning: Found lib.zipAttrsWith 14 + ╭─[data/faster_zipattrswith.nix:6:7] 15 + 16 + 6 │ _ = nixpkgs.lib.zipAttrsWith (name: values: values) [{ a = 1; } { a = 2; b = 3; }]; 17 + · ────────────┬─────────── 18 + · ╰───────────── Prefer builtins.zipAttrsWith over nixpkgs.lib.zipAttrsWith 19 + ───╯ 20 +
+1
lib/src/lints.rs
··· 16 16 deprecated_is_null, 17 17 empty_inherit, 18 18 faster_groupby, 19 + faster_zipattrswith, 19 20 }
+72
lib/src/lints/faster_zipattrswith.rs
··· 1 + use crate::{ 2 + make, 3 + session::{SessionInfo, Version}, 4 + Metadata, Report, Rule, Suggestion, 5 + }; 6 + 7 + use if_chain::if_chain; 8 + use macros::lint; 9 + use rnix::{ 10 + types::{Select, TypedNode}, 11 + NodeOrToken, SyntaxElement, SyntaxKind, 12 + }; 13 + 14 + /// ## What it does 15 + /// Checks for `lib.zipAttrsWith`. 16 + /// 17 + /// ## Why is this bad? 18 + /// Nix 2.6 introduces `builtins.zipAttrsWith` which is faster and does 19 + /// not require a lib import. 20 + /// 21 + /// ## Example 22 + /// 23 + /// ```nix 24 + /// lib.zipAttrsWith (name: values: values) [ {a = "x";} {a = "y"; b = "z";} ] 25 + /// # { a = ["x" "y"]; b = ["z"] } 26 + /// ``` 27 + /// 28 + /// Replace `lib.zipAttrsWith` with `builtins.zipAttrsWith`: 29 + /// 30 + /// ```nix 31 + /// builtins.zipAttrsWith (name: values: values) [ {a = "x";} {a = "y"; b = "z";} ] 32 + /// ``` 33 + #[lint( 34 + name = "faster_zipattrswith", 35 + note = "Found lib.zipAttrsWith", 36 + code = 16, 37 + match_with = SyntaxKind::NODE_SELECT 38 + )] 39 + struct FasterZipAttrsWith; 40 + 41 + impl Rule for FasterZipAttrsWith { 42 + fn validate(&self, node: &SyntaxElement, sess: &SessionInfo) -> Option<Report> { 43 + let lint_version = "2.6".parse::<Version>().unwrap(); 44 + if_chain! { 45 + if sess.version() >= &lint_version; 46 + if let NodeOrToken::Node(node) = node; 47 + if let Some(select_expr) = Select::cast(node.clone()); 48 + if let Some(select_from) = select_expr.set(); 49 + if let Some(zip_attrs_with) = select_expr.index(); 50 + 51 + // a heuristic to lint on nixpkgs.lib.zipAttrsWith 52 + // and lib.zipAttrsWith and its variants 53 + if select_from.text().to_string() != "builtins"; 54 + if zip_attrs_with.text().to_string() == "zipAttrsWith"; 55 + 56 + then { 57 + let at = node.text_range(); 58 + let replacement = { 59 + let builtins = make::ident("builtins"); 60 + make::select(builtins.node(), &zip_attrs_with).node().clone() 61 + }; 62 + let message = format!("Prefer `builtins.zipAttrsWith` over `{}.zipAttrsWith`", select_from); 63 + Some( 64 + self.report() 65 + .suggest(at, message, Suggestion::new(at, replacement)), 66 + ) 67 + } else { 68 + None 69 + } 70 + } 71 + } 72 + }