aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock186
-rw-r--r--bin/Cargo.toml12
-rw-r--r--bin/src/config.rs14
-rw-r--r--bin/src/lib.rs7
-rw-r--r--bin/src/main.rs18
-rw-r--r--bin/tests/data/bool_comparison.nix13
-rw-r--r--bin/tests/data/collapsible_let_in.nix9
-rw-r--r--bin/tests/data/deprecated_is_null.nix6
-rw-r--r--bin/tests/data/empty_let_in.nix3
-rw-r--r--bin/tests/data/empty_pattern.nix9
-rw-r--r--bin/tests/data/eta_reduction.nix18
-rw-r--r--bin/tests/data/legacy_let_syntax.nix5
-rw-r--r--bin/tests/data/manual_inherit.nix12
-rw-r--r--bin/tests/data/manual_inherit_from.nix8
-rw-r--r--bin/tests/data/redundant_pattern_bind.nix1
-rw-r--r--bin/tests/data/unquoted_splices.nix15
-rw-r--r--bin/tests/data/unquoted_uri.nix1
-rw-r--r--bin/tests/data/useless_parens.nix16
-rw-r--r--bin/tests/main.rs47
-rw-r--r--bin/tests/snapshots/main__bool_comparison.snap62
-rw-r--r--bin/tests/snapshots/main__collapsible_let_in.snap17
-rw-r--r--bin/tests/snapshots/main__deprecated_is_null.snap13
-rw-r--r--bin/tests/snapshots/main__empty_let_in.snap14
-rw-r--r--bin/tests/snapshots/main__empty_pattern.snap20
-rw-r--r--bin/tests/snapshots/main__eta_reduction.snap13
-rw-r--r--bin/tests/snapshots/main__legacy_let_syntax.snap14
-rw-r--r--bin/tests/snapshots/main__manual_inherit.snap13
-rw-r--r--bin/tests/snapshots/main__manual_inherit_from.snap20
-rw-r--r--bin/tests/snapshots/main__redundant_pattern_bind.snap13
-rw-r--r--bin/tests/snapshots/main__unquoted_splices.snap35
-rw-r--r--bin/tests/snapshots/main__unquoted_uri.snap13
-rw-r--r--bin/tests/snapshots/main__useless_parens.snap48
-rw-r--r--flake.nix7
-rw-r--r--lib/src/lints/useless_parens.rs10
-rw-r--r--vfs/src/lib.rs2
35 files changed, 665 insertions, 49 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5b4b0eb..08881fd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -21,6 +21,12 @@ dependencies = [
21] 21]
22 22
23[[package]] 23[[package]]
24name = "arrayvec"
25version = "0.5.2"
26source = "registry+https://github.com/rust-lang/crates.io-index"
27checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
28
29[[package]]
24name = "atty" 30name = "atty"
25version = "0.2.14" 31version = "0.2.14"
26source = "registry+https://github.com/rust-lang/crates.io-index" 32source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -69,9 +75,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
69 75
70[[package]] 76[[package]]
71name = "clap" 77name = "clap"
72version = "3.0.0-beta.4" 78version = "3.0.0-beta.5"
73source = "registry+https://github.com/rust-lang/crates.io-index" 79source = "registry+https://github.com/rust-lang/crates.io-index"
74checksum = "fcd70aa5597dbc42f7217a543f9ef2768b2ef823ba29036072d30e1d88e98406" 80checksum = "feff3878564edb93745d58cf63e17b63f24142506e7a20c87a5521ed7bfb1d63"
75dependencies = [ 81dependencies = [
76 "atty", 82 "atty",
77 "bitflags", 83 "bitflags",
@@ -82,14 +88,14 @@ dependencies = [
82 "strsim", 88 "strsim",
83 "termcolor", 89 "termcolor",
84 "textwrap", 90 "textwrap",
85 "vec_map", 91 "unicase",
86] 92]
87 93
88[[package]] 94[[package]]
89name = "clap_derive" 95name = "clap_derive"
90version = "3.0.0-beta.4" 96version = "3.0.0-beta.5"
91source = "registry+https://github.com/rust-lang/crates.io-index" 97source = "registry+https://github.com/rust-lang/crates.io-index"
92checksum = "0b5bb0d655624a0b8770d1c178fb8ffcb1f91cc722cb08f451e3dc72465421ac" 98checksum = "8b15c6b4f786ffb6192ffe65a36855bc1fc2444bcd0945ae16748dcd6ed7d0d3"
93dependencies = [ 99dependencies = [
94 "heck", 100 "heck",
95 "proc-macro-error", 101 "proc-macro-error",
@@ -99,6 +105,19 @@ dependencies = [
99] 105]
100 106
101[[package]] 107[[package]]
108name = "console"
109version = "0.14.1"
110source = "registry+https://github.com/rust-lang/crates.io-index"
111checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45"
112dependencies = [
113 "encode_unicode",
114 "lazy_static",
115 "libc",
116 "terminal_size",
117 "winapi",
118]
119
120[[package]]
102name = "countme" 121name = "countme"
103version = "2.0.4" 122version = "2.0.4"
104source = "registry+https://github.com/rust-lang/crates.io-index" 123source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -115,6 +134,18 @@ dependencies = [
115] 134]
116 135
117[[package]] 136[[package]]
137name = "dtoa"
138version = "0.4.8"
139source = "registry+https://github.com/rust-lang/crates.io-index"
140checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
141
142[[package]]
143name = "encode_unicode"
144version = "0.3.6"
145source = "registry+https://github.com/rust-lang/crates.io-index"
146checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
147
148[[package]]
118name = "fnv" 149name = "fnv"
119version = "1.0.7" 150version = "1.0.7"
120source = "registry+https://github.com/rust-lang/crates.io-index" 151source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -198,6 +229,21 @@ dependencies = [
198] 229]
199 230
200[[package]] 231[[package]]
232name = "insta"
233version = "1.8.0"
234source = "registry+https://github.com/rust-lang/crates.io-index"
235checksum = "15226a375927344c78d39dc6b49e2d5562a5b0705e26a589093c6792e52eed8e"
236dependencies = [
237 "console",
238 "lazy_static",
239 "serde",
240 "serde_json",
241 "serde_yaml",
242 "similar 1.3.0",
243 "uuid",
244]
245
246[[package]]
201name = "itoa" 247name = "itoa"
202version = "0.4.8" 248version = "0.4.8"
203source = "registry+https://github.com/rust-lang/crates.io-index" 249source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -224,9 +270,15 @@ dependencies = [
224 270
225[[package]] 271[[package]]
226name = "libc" 272name = "libc"
227version = "0.2.103" 273version = "0.2.106"
228source = "registry+https://github.com/rust-lang/crates.io-index" 274source = "registry+https://github.com/rust-lang/crates.io-index"
229checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" 275checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
276
277[[package]]
278name = "linked-hash-map"
279version = "0.5.4"
280source = "registry+https://github.com/rust-lang/crates.io-index"
281checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
230 282
231[[package]] 283[[package]]
232name = "log" 284name = "log"
@@ -278,9 +330,12 @@ checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
278 330
279[[package]] 331[[package]]
280name = "os_str_bytes" 332name = "os_str_bytes"
281version = "3.1.0" 333version = "4.2.0"
282source = "registry+https://github.com/rust-lang/crates.io-index" 334source = "registry+https://github.com/rust-lang/crates.io-index"
283checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d" 335checksum = "addaa943333a514159c80c97ff4a93306530d965d27e139188283cd13e06a799"
336dependencies = [
337 "memchr",
338]
284 339
285[[package]] 340[[package]]
286name = "proc-macro-error" 341name = "proc-macro-error"
@@ -308,18 +363,18 @@ dependencies = [
308 363
309[[package]] 364[[package]]
310name = "proc-macro2" 365name = "proc-macro2"
311version = "1.0.29" 366version = "1.0.32"
312source = "registry+https://github.com/rust-lang/crates.io-index" 367source = "registry+https://github.com/rust-lang/crates.io-index"
313checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" 368checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
314dependencies = [ 369dependencies = [
315 "unicode-xid", 370 "unicode-xid",
316] 371]
317 372
318[[package]] 373[[package]]
319name = "quote" 374name = "quote"
320version = "1.0.9" 375version = "1.0.10"
321source = "registry+https://github.com/rust-lang/crates.io-index" 376source = "registry+https://github.com/rust-lang/crates.io-index"
322checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 377checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
323dependencies = [ 378dependencies = [
324 "proc-macro2", 379 "proc-macro2",
325] 380]
@@ -343,9 +398,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
343 398
344[[package]] 399[[package]]
345name = "rnix" 400name = "rnix"
346version = "0.9.0" 401version = "0.9.1"
347source = "registry+https://github.com/rust-lang/crates.io-index" 402source = "registry+https://github.com/rust-lang/crates.io-index"
348checksum = "1b37f8af07a0354606141df076458660af7e22238e4117a041c21c548080addd" 403checksum = "294becb48f58c496d96c10a12df290266204ca75c9799be4d04222bfaebb6a37"
349dependencies = [ 404dependencies = [
350 "cbitset", 405 "cbitset",
351 "rowan", 406 "rowan",
@@ -418,6 +473,24 @@ dependencies = [
418] 473]
419 474
420[[package]] 475[[package]]
476name = "serde_yaml"
477version = "0.8.21"
478source = "registry+https://github.com/rust-lang/crates.io-index"
479checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af"
480dependencies = [
481 "dtoa",
482 "indexmap",
483 "serde",
484 "yaml-rust",
485]
486
487[[package]]
488name = "similar"
489version = "1.3.0"
490source = "registry+https://github.com/rust-lang/crates.io-index"
491checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec"
492
493[[package]]
421name = "similar" 494name = "similar"
422version = "2.1.0" 495version = "2.1.0"
423source = "registry+https://github.com/rust-lang/crates.io-index" 496source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -425,9 +498,12 @@ checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3"
425 498
426[[package]] 499[[package]]
427name = "smol_str" 500name = "smol_str"
428version = "0.1.18" 501version = "0.1.20"
429source = "registry+https://github.com/rust-lang/crates.io-index" 502source = "registry+https://github.com/rust-lang/crates.io-index"
430checksum = "b203e79e90905594272c1c97c7af701533d42adaab0beb3859018e477d54a3b0" 503checksum = "559b173452ec4061933b0c0e22d7d429c90ecdc1b3ae1c6e64238e7c15c3ee15"
504dependencies = [
505 "serde",
506]
431 507
432[[package]] 508[[package]]
433name = "statix" 509name = "statix"
@@ -436,16 +512,27 @@ dependencies = [
436 "ariadne", 512 "ariadne",
437 "clap", 513 "clap",
438 "ignore", 514 "ignore",
515 "insta",
439 "lib", 516 "lib",
440 "rnix", 517 "rnix",
441 "serde", 518 "serde",
442 "serde_json", 519 "serde_json",
443 "similar", 520 "similar 2.1.0",
521 "strip-ansi-escapes",
444 "thiserror", 522 "thiserror",
445 "vfs", 523 "vfs",
446] 524]
447 525
448[[package]] 526[[package]]
527name = "strip-ansi-escapes"
528version = "0.1.1"
529source = "registry+https://github.com/rust-lang/crates.io-index"
530checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8"
531dependencies = [
532 "vte",
533]
534
535[[package]]
449name = "strsim" 536name = "strsim"
450version = "0.10.0" 537version = "0.10.0"
451source = "registry+https://github.com/rust-lang/crates.io-index" 538source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -453,9 +540,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
453 540
454[[package]] 541[[package]]
455name = "syn" 542name = "syn"
456version = "1.0.76" 543version = "1.0.81"
457source = "registry+https://github.com/rust-lang/crates.io-index" 544source = "registry+https://github.com/rust-lang/crates.io-index"
458checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" 545checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
459dependencies = [ 546dependencies = [
460 "proc-macro2", 547 "proc-macro2",
461 "quote", 548 "quote",
@@ -472,6 +559,16 @@ dependencies = [
472] 559]
473 560
474[[package]] 561[[package]]
562name = "terminal_size"
563version = "0.1.17"
564source = "registry+https://github.com/rust-lang/crates.io-index"
565checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
566dependencies = [
567 "libc",
568 "winapi",
569]
570
571[[package]]
475name = "text-size" 572name = "text-size"
476version = "1.1.0" 573version = "1.1.0"
477source = "registry+https://github.com/rust-lang/crates.io-index" 574source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -516,6 +613,15 @@ dependencies = [
516] 613]
517 614
518[[package]] 615[[package]]
616name = "unicase"
617version = "2.6.0"
618source = "registry+https://github.com/rust-lang/crates.io-index"
619checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
620dependencies = [
621 "version_check",
622]
623
624[[package]]
519name = "unicode-segmentation" 625name = "unicode-segmentation"
520version = "1.8.0" 626version = "1.8.0"
521source = "registry+https://github.com/rust-lang/crates.io-index" 627source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -534,10 +640,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
534checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 640checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
535 641
536[[package]] 642[[package]]
537name = "vec_map" 643name = "utf8parse"
644version = "0.2.0"
645source = "registry+https://github.com/rust-lang/crates.io-index"
646checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
647
648[[package]]
649name = "uuid"
538version = "0.8.2" 650version = "0.8.2"
539source = "registry+https://github.com/rust-lang/crates.io-index" 651source = "registry+https://github.com/rust-lang/crates.io-index"
540checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 652checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
541 653
542[[package]] 654[[package]]
543name = "version_check" 655name = "version_check"
@@ -553,6 +665,27 @@ dependencies = [
553] 665]
554 666
555[[package]] 667[[package]]
668name = "vte"
669version = "0.10.1"
670source = "registry+https://github.com/rust-lang/crates.io-index"
671checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983"
672dependencies = [
673 "arrayvec",
674 "utf8parse",
675 "vte_generate_state_changes",
676]
677
678[[package]]
679name = "vte_generate_state_changes"
680version = "0.1.1"
681source = "registry+https://github.com/rust-lang/crates.io-index"
682checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
683dependencies = [
684 "proc-macro2",
685 "quote",
686]
687
688[[package]]
556name = "walkdir" 689name = "walkdir"
557version = "2.3.2" 690version = "2.3.2"
558source = "registry+https://github.com/rust-lang/crates.io-index" 691source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -595,6 +728,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
595checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 728checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
596 729
597[[package]] 730[[package]]
731name = "yaml-rust"
732version = "0.4.5"
733source = "registry+https://github.com/rust-lang/crates.io-index"
734checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
735dependencies = [
736 "linked-hash-map",
737]
738
739[[package]]
598name = "yansi" 740name = "yansi"
599version = "0.5.0" 741version = "0.5.0"
600source = "registry+https://github.com/rust-lang/crates.io-index" 742source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/bin/Cargo.toml b/bin/Cargo.toml
index d67e6c1..7c48083 100644
--- a/bin/Cargo.toml
+++ b/bin/Cargo.toml
@@ -6,7 +6,13 @@ license = "MIT"
6authors = [ "Akshay <[email protected]>" ] 6authors = [ "Akshay <[email protected]>" ]
7description = "Lints and suggestions for the Nix programming language" 7description = "Lints and suggestions for the Nix programming language"
8 8
9# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9[lib]
10name = "statix"
11path = "src/lib.rs"
12
13[[bin]]
14name = "statix"
15path = "src/main.rs"
10 16
11[dependencies] 17[dependencies]
12ariadne = "0.1.3" 18ariadne = "0.1.3"
@@ -27,5 +33,9 @@ version = "1.0.68"
27features = [ "derive" ] 33features = [ "derive" ]
28optional = true 34optional = true
29 35
36[dev-dependencies]
37insta = "1.8.0"
38strip-ansi-escapes = "0.1.1"
39
30[features] 40[features]
31json = [ "lib/json-out", "serde_json", "serde" ] 41json = [ "lib/json-out", "serde_json", "serde" ]
diff --git a/bin/src/config.rs b/bin/src/config.rs
index 25c2a7f..d3944ac 100644
--- a/bin/src/config.rs
+++ b/bin/src/config.rs
@@ -2,17 +2,17 @@ use std::{default::Default, fmt, fs, path::PathBuf, str::FromStr};
2 2
3use crate::{dirs, err::ConfigErr}; 3use crate::{dirs, err::ConfigErr};
4 4
5use clap::Clap; 5use clap::Parser;
6use vfs::ReadOnlyVfs; 6use vfs::ReadOnlyVfs;
7 7
8#[derive(Clap, Debug)] 8#[derive(Parser, Debug)]
9#[clap(version, author, about)] 9#[clap(version, author, about)]
10pub struct Opts { 10pub struct Opts {
11 #[clap(subcommand)] 11 #[clap(subcommand)]
12 pub cmd: SubCommand, 12 pub cmd: SubCommand,
13} 13}
14 14
15#[derive(Clap, Debug)] 15#[derive(Parser, Debug)]
16pub enum SubCommand { 16pub enum SubCommand {
17 /// Lints and suggestions for the nix programming language 17 /// Lints and suggestions for the nix programming language
18 Check(Check), 18 Check(Check),
@@ -24,7 +24,7 @@ pub enum SubCommand {
24 Explain(Explain), 24 Explain(Explain),
25} 25}
26 26
27#[derive(Clap, Debug)] 27#[derive(Parser, Debug)]
28pub struct Check { 28pub struct Check {
29 /// File or directory to run check on 29 /// File or directory to run check on
30 #[clap(default_value = ".", parse(from_os_str))] 30 #[clap(default_value = ".", parse(from_os_str))]
@@ -67,7 +67,7 @@ impl Check {
67 } 67 }
68} 68}
69 69
70#[derive(Clap, Debug)] 70#[derive(Parser, Debug)]
71pub struct Fix { 71pub struct Fix {
72 /// File or directory to run fix on 72 /// File or directory to run fix on
73 #[clap(default_value = ".", parse(from_os_str))] 73 #[clap(default_value = ".", parse(from_os_str))]
@@ -127,7 +127,7 @@ impl Fix {
127 } 127 }
128} 128}
129 129
130#[derive(Clap, Debug)] 130#[derive(Parser, Debug)]
131pub struct Single { 131pub struct Single {
132 /// File to run single-fix on 132 /// File to run single-fix on
133 #[clap(parse(from_os_str))] 133 #[clap(parse(from_os_str))]
@@ -174,7 +174,7 @@ impl Single {
174 } 174 }
175} 175}
176 176
177#[derive(Clap, Debug)] 177#[derive(Parser, Debug)]
178pub struct Explain { 178pub struct Explain {
179 /// Warning code to explain 179 /// Warning code to explain
180 #[clap(parse(try_from_str = parse_warning_code))] 180 #[clap(parse(try_from_str = parse_warning_code))]
diff --git a/bin/src/lib.rs b/bin/src/lib.rs
new file mode 100644
index 0000000..49c1a41
--- /dev/null
+++ b/bin/src/lib.rs
@@ -0,0 +1,7 @@
1pub mod config;
2pub mod dirs;
3pub mod err;
4pub mod explain;
5pub mod fix;
6pub mod lint;
7pub mod traits;
diff --git a/bin/src/main.rs b/bin/src/main.rs
index fabc509..f504796 100644
--- a/bin/src/main.rs
+++ b/bin/src/main.rs
@@ -1,15 +1,9 @@
1mod config; 1use clap::Parser;
2mod dirs; 2use statix::{
3mod err; 3 config::{Opts, SubCommand},
4mod explain; 4 err::StatixErr,
5mod fix; 5 explain, fix, lint,
6mod lint; 6};
7mod traits;
8
9use crate::err::StatixErr;
10
11use clap::Clap;
12use config::{Opts, SubCommand};
13 7
14fn _main() -> Result<(), StatixErr> { 8fn _main() -> Result<(), StatixErr> {
15 let opts = Opts::parse(); 9 let opts = Opts::parse();
diff --git a/bin/tests/data/bool_comparison.nix b/bin/tests/data/bool_comparison.nix
new file mode 100644
index 0000000..dee2d08
--- /dev/null
+++ b/bin/tests/data/bool_comparison.nix
@@ -0,0 +1,13 @@
1[
2 # trivial
3 (a == true)
4 (b == true)
5 (true == c)
6 (true == d)
7
8 # not equals
9 (e != true)
10 (f != false)
11 (true != g)
12 (false != h)
13]
diff --git a/bin/tests/data/collapsible_let_in.nix b/bin/tests/data/collapsible_let_in.nix
new file mode 100644
index 0000000..7b41014
--- /dev/null
+++ b/bin/tests/data/collapsible_let_in.nix
@@ -0,0 +1,9 @@
1let
2 a = 2;
3 b = 3;
4in
5 let
6 c = 5;
7 d = 6;
8 in
9 a + b + c + d
diff --git a/bin/tests/data/deprecated_is_null.nix b/bin/tests/data/deprecated_is_null.nix
new file mode 100644
index 0000000..42596d7
--- /dev/null
+++ b/bin/tests/data/deprecated_is_null.nix
@@ -0,0 +1,6 @@
1let
2 e = null;
3in
4if isNull e
5then "no"
6else "yes"
diff --git a/bin/tests/data/empty_let_in.nix b/bin/tests/data/empty_let_in.nix
new file mode 100644
index 0000000..3ecb6e4
--- /dev/null
+++ b/bin/tests/data/empty_let_in.nix
@@ -0,0 +1,3 @@
1let
2in
3 null
diff --git a/bin/tests/data/empty_pattern.nix b/bin/tests/data/empty_pattern.nix
new file mode 100644
index 0000000..23d99e8
--- /dev/null
+++ b/bin/tests/data/empty_pattern.nix
@@ -0,0 +1,9 @@
1[
2 # match
3 ({ ... }: 42)
4
5 # don't match
6 ({ a, ... }: a)
7 ({ ... } @ inputs: inputs)
8]
9
diff --git a/bin/tests/data/eta_reduction.nix b/bin/tests/data/eta_reduction.nix
new file mode 100644
index 0000000..e717ee7
--- /dev/null
+++ b/bin/tests/data/eta_reduction.nix
@@ -0,0 +1,18 @@
1let
2 double = x: x * 2;
3 inherit (builtins) map;
4 xs = [ 1 2 3 ];
5 f = {
6 inherit double;
7 val = 2;
8 };
9in
10[
11 (map (x: double x) xs)
12
13 # don't lint on non-free exprs
14 (map (f: f.double f.val) [ f ])
15
16 # other non-free forms
17 (map (f: {inherit f;}.double f.val) [ f ])
18]
diff --git a/bin/tests/data/legacy_let_syntax.nix b/bin/tests/data/legacy_let_syntax.nix
new file mode 100644
index 0000000..46e3191
--- /dev/null
+++ b/bin/tests/data/legacy_let_syntax.nix
@@ -0,0 +1,5 @@
1let {
2 body = x + y;
3 x = "hello,";
4 y = " world!";
5}
diff --git a/bin/tests/data/manual_inherit.nix b/bin/tests/data/manual_inherit.nix
new file mode 100644
index 0000000..53ae4d7
--- /dev/null
+++ b/bin/tests/data/manual_inherit.nix
@@ -0,0 +1,12 @@
1let
2 a = 2;
3 y = "y";
4in
5{
6 # trivial
7 a = a;
8
9 # don't lint
10 x.y = y;
11}
12
diff --git a/bin/tests/data/manual_inherit_from.nix b/bin/tests/data/manual_inherit_from.nix
new file mode 100644
index 0000000..214b2a3
--- /dev/null
+++ b/bin/tests/data/manual_inherit_from.nix
@@ -0,0 +1,8 @@
1let
2 a = {b = 2; c = 3;};
3in
4{
5 b = a.b;
6 c = a.c;
7}
8
diff --git a/bin/tests/data/redundant_pattern_bind.nix b/bin/tests/data/redundant_pattern_bind.nix
new file mode 100644
index 0000000..d328c50
--- /dev/null
+++ b/bin/tests/data/redundant_pattern_bind.nix
@@ -0,0 +1 @@
{ ... } @ inputs: null
diff --git a/bin/tests/data/unquoted_splices.nix b/bin/tests/data/unquoted_splices.nix
new file mode 100644
index 0000000..30935b0
--- /dev/null
+++ b/bin/tests/data/unquoted_splices.nix
@@ -0,0 +1,15 @@
1let
2 x = 2;
3 y = 3;
4 a = { "2" = y; };
5in
6[
7 ${x}
8 ${toString (x + y)}
9 a.${toString x}
10
11 # multiline test
12 ${
13 toString x
14 }
15]
diff --git a/bin/tests/data/unquoted_uri.nix b/bin/tests/data/unquoted_uri.nix
new file mode 100644
index 0000000..e56574a
--- /dev/null
+++ b/bin/tests/data/unquoted_uri.nix
@@ -0,0 +1 @@
github:nerdypepper/statix
diff --git a/bin/tests/data/useless_parens.nix b/bin/tests/data/useless_parens.nix
new file mode 100644
index 0000000..cf26441
--- /dev/null
+++ b/bin/tests/data/useless_parens.nix
@@ -0,0 +1,16 @@
1let
2 # parens around primitives
3 a = {
4 b = ("hello");
5 c = (d);
6 e = ({ f = 2; });
7 };
8
9 # parens around let-value
10 g = (1 + 2);
11 h = ({ inherit i; });
12
13 # TODO: binary exprs, function args etc.
14in
15 # parens around let body
16 (null)
diff --git a/bin/tests/main.rs b/bin/tests/main.rs
new file mode 100644
index 0000000..6175c90
--- /dev/null
+++ b/bin/tests/main.rs
@@ -0,0 +1,47 @@
1mod util {
2 #[macro_export]
3 macro_rules! test_lint {
4 ($($tname:ident),*,) => {
5 test_lint!($($tname),*);
6 };
7 ($($tname:ident),*) => {
8 $(
9 #[test]
10 fn $tname() {
11 use statix::{config::OutFormat, traits::WriteDiagnostic, lint};
12 use vfs::ReadOnlyVfs;
13
14 let file_path = concat!("data/", stringify!($tname), ".nix");
15 let contents = include_str!(concat!("data/", stringify!($tname), ".nix"));
16
17 let vfs = ReadOnlyVfs::singleton(file_path, contents.as_bytes());
18
19 let mut buffer = Vec::new();
20 vfs.iter().map(lint::lint).for_each(|r| {
21 buffer.write(&r, &vfs, OutFormat::StdErr).unwrap();
22 });
23
24 let stripped = strip_ansi_escapes::strip(&buffer).unwrap();
25 let out = std::str::from_utf8(&stripped).unwrap();
26 insta::assert_snapshot!(&out);
27 }
28 )*
29 };
30 }
31}
32
33test_lint! {
34 bool_comparison,
35 empty_let_in,
36 manual_inherit,
37 manual_inherit_from,
38 legacy_let_syntax,
39 collapsible_let_in,
40 eta_reduction,
41 useless_parens,
42 unquoted_splices,
43 empty_pattern,
44 redundant_pattern_bind,
45 unquoted_uri,
46 deprecated_is_null,
47}
diff --git a/bin/tests/snapshots/main__bool_comparison.snap b/bin/tests/snapshots/main__bool_comparison.snap
new file mode 100644
index 0000000..0865332
--- /dev/null
+++ b/bin/tests/snapshots/main__bool_comparison.snap
@@ -0,0 +1,62 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W01] Warning: Unnecessary comparison with boolean
7 ╭─[data/bool_comparison.nix:3:4]
8
9 3 │ (a == true)
10 · ────┬────
11 · ╰────── Comparing a with boolean literal true
12───╯
13[W01] Warning: Unnecessary comparison with boolean
14 ╭─[data/bool_comparison.nix:4:4]
15
16 4 │ (b == true)
17 · ────┬────
18 · ╰────── Comparing b with boolean literal true
19───╯
20[W01] Warning: Unnecessary comparison with boolean
21 ╭─[data/bool_comparison.nix:5:4]
22
23 5 │ (true == c)
24 · ────┬────
25 · ╰────── Comparing c with boolean literal true
26───╯
27[W01] Warning: Unnecessary comparison with boolean
28 ╭─[data/bool_comparison.nix:6:4]
29
30 6 │ (true == d)
31 · ────┬────
32 · ╰────── Comparing d with boolean literal true
33───╯
34[W01] Warning: Unnecessary comparison with boolean
35 ╭─[data/bool_comparison.nix:9:4]
36
37 9 │ (e != true)
38 · ────┬────
39 · ╰────── Comparing e with boolean literal true
40───╯
41[W01] Warning: Unnecessary comparison with boolean
42 ╭─[data/bool_comparison.nix:10:4]
43
44 10 │ (f != false)
45 · ─────┬────
46 · ╰────── Comparing f with boolean literal false
47────╯
48[W01] Warning: Unnecessary comparison with boolean
49 ╭─[data/bool_comparison.nix:11:4]
50
51 11 │ (true != g)
52 · ────┬────
53 · ╰────── Comparing g with boolean literal true
54────╯
55[W01] Warning: Unnecessary comparison with boolean
56 ╭─[data/bool_comparison.nix:12:4]
57
58 12 │ (false != h)
59 · ─────┬────
60 · ╰────── Comparing h with boolean literal false
61────╯
62
diff --git a/bin/tests/snapshots/main__collapsible_let_in.snap b/bin/tests/snapshots/main__collapsible_let_in.snap
new file mode 100644
index 0000000..b135abc
--- /dev/null
+++ b/bin/tests/snapshots/main__collapsible_let_in.snap
@@ -0,0 +1,17 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W06] Warning: These let-in expressions are collapsible
7 ╭─[data/collapsible_let_in.nix:1:1]
8
9 1 │ ╭───▶ let
10 5 │ │ ╭─▶ let
11 9 │ │ ├─▶ a + b + c + d
12 · │ │ │
13 · │ ╰───────────────────── This let in expression is nested
14 · │ │
15 · ╰───────────────────┴─── This let in expression contains a nested let in expression
16───╯
17
diff --git a/bin/tests/snapshots/main__deprecated_is_null.snap b/bin/tests/snapshots/main__deprecated_is_null.snap
new file mode 100644
index 0000000..d49b381
--- /dev/null
+++ b/bin/tests/snapshots/main__deprecated_is_null.snap
@@ -0,0 +1,13 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W13] Warning: Found usage of deprecated builtin isNull
7 ╭─[data/deprecated_is_null.nix:4:4]
8
9 4 │ if isNull e
10 · ────┬───
11 · ╰───── isNull is deprecated, check equality with null instead
12───╯
13
diff --git a/bin/tests/snapshots/main__empty_let_in.snap b/bin/tests/snapshots/main__empty_let_in.snap
new file mode 100644
index 0000000..426692f
--- /dev/null
+++ b/bin/tests/snapshots/main__empty_let_in.snap
@@ -0,0 +1,14 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W02] Warning: Useless let-in expression
7 ╭─[data/empty_let_in.nix:1:1]
8
9 1 │ ╭─▶ let
10 3 │ ├─▶ null
11 · │
12 · ╰──────────── This let-in expression has no entries
13───╯
14
diff --git a/bin/tests/snapshots/main__empty_pattern.snap b/bin/tests/snapshots/main__empty_pattern.snap
new file mode 100644
index 0000000..3ea7ae0
--- /dev/null
+++ b/bin/tests/snapshots/main__empty_pattern.snap
@@ -0,0 +1,20 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W10] Warning: Found empty pattern in function argument
7 ╭─[data/empty_pattern.nix:3:4]
8
9 3 │ ({ ... }: 42)
10 · ───┬───
11 · ╰───── This pattern is empty, use _ instead
12───╯
13[W11] Warning: Found redundant pattern bind in function argument
14 ╭─[data/empty_pattern.nix:7:4]
15
16 7 │ ({ ... } @ inputs: inputs)
17 · ────────┬───────
18 · ╰───────── This pattern bind is redundant, use inputs instead
19───╯
20
diff --git a/bin/tests/snapshots/main__eta_reduction.snap b/bin/tests/snapshots/main__eta_reduction.snap
new file mode 100644
index 0000000..6271980
--- /dev/null
+++ b/bin/tests/snapshots/main__eta_reduction.snap
@@ -0,0 +1,13 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W07] Warning: This function expression is eta reducible
7 ╭─[data/eta_reduction.nix:11:9]
8
9 11 │ (map (x: double x) xs)
10 · ─────┬─────
11 · ╰─────── Found eta-reduction: double
12────╯
13
diff --git a/bin/tests/snapshots/main__legacy_let_syntax.snap b/bin/tests/snapshots/main__legacy_let_syntax.snap
new file mode 100644
index 0000000..35aa7ee
--- /dev/null
+++ b/bin/tests/snapshots/main__legacy_let_syntax.snap
@@ -0,0 +1,14 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W05] Warning: Using undocumented `let` syntax
7 ╭─[data/legacy_let_syntax.nix:1:1]
8
9 1 │ ╭─▶ let {
10 5 │ ├─▶ }
11 · │
12 · ╰─────── Prefer rec over undocumented let syntax
13───╯
14
diff --git a/bin/tests/snapshots/main__manual_inherit.snap b/bin/tests/snapshots/main__manual_inherit.snap
new file mode 100644
index 0000000..063867c
--- /dev/null
+++ b/bin/tests/snapshots/main__manual_inherit.snap
@@ -0,0 +1,13 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W03] Warning: Assignment instead of inherit
7 ╭─[data/manual_inherit.nix:7:3]
8
9 7 │ a = a;
10 · ───┬──
11 · ╰──── This assignment is better written with inherit
12───╯
13
diff --git a/bin/tests/snapshots/main__manual_inherit_from.snap b/bin/tests/snapshots/main__manual_inherit_from.snap
new file mode 100644
index 0000000..9cf1f5d
--- /dev/null
+++ b/bin/tests/snapshots/main__manual_inherit_from.snap
@@ -0,0 +1,20 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W04] Warning: Assignment instead of inherit from
7 ╭─[data/manual_inherit_from.nix:5:3]
8
9 5 │ b = a.b;
10 · ────┬───
11 · ╰───── This assignment is better written with inherit
12───╯
13[W04] Warning: Assignment instead of inherit from
14 ╭─[data/manual_inherit_from.nix:6:3]
15
16 6 │ c = a.c;
17 · ────┬───
18 · ╰───── This assignment is better written with inherit
19───╯
20
diff --git a/bin/tests/snapshots/main__redundant_pattern_bind.snap b/bin/tests/snapshots/main__redundant_pattern_bind.snap
new file mode 100644
index 0000000..2f26818
--- /dev/null
+++ b/bin/tests/snapshots/main__redundant_pattern_bind.snap
@@ -0,0 +1,13 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W11] Warning: Found redundant pattern bind in function argument
7 ╭─[data/redundant_pattern_bind.nix:1:1]
8
9 1 │ { ... } @ inputs: null
10 · ────────┬────────
11 · ╰────────── This pattern bind is redundant, use inputs instead
12───╯
13
diff --git a/bin/tests/snapshots/main__unquoted_splices.snap b/bin/tests/snapshots/main__unquoted_splices.snap
new file mode 100644
index 0000000..5fd1917
--- /dev/null
+++ b/bin/tests/snapshots/main__unquoted_splices.snap
@@ -0,0 +1,35 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W09] Warning: Found unquoted splice expression
7 ╭─[data/unquoted_splices.nix:7:3]
8
9 7 │ ${x}
10 · ──┬─
11 · ╰─── Consider quoting this splice expression
12───╯
13[W09] Warning: Found unquoted splice expression
14 ╭─[data/unquoted_splices.nix:8:3]
15
16 8 │ ${toString (x + y)}
17 · ─────────┬─────────
18 · ╰─────────── Consider quoting this splice expression
19───╯
20[W09] Warning: Found unquoted splice expression
21 ╭─[data/unquoted_splices.nix:9:5]
22
23 9 │ a.${toString x}
24 · ──────┬──────
25 · ╰──────── Consider quoting this splice expression
26───╯
27[W09] Warning: Found unquoted splice expression
28 ╭─[data/unquoted_splices.nix:12:3]
29
30 12 │ ╭─▶ ${
31 14 │ ├─▶ }
32 · │
33 · ╰───────── Consider quoting this splice expression
34────╯
35
diff --git a/bin/tests/snapshots/main__unquoted_uri.snap b/bin/tests/snapshots/main__unquoted_uri.snap
new file mode 100644
index 0000000..2f0e5a9
--- /dev/null
+++ b/bin/tests/snapshots/main__unquoted_uri.snap
@@ -0,0 +1,13 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W12] Warning: Found unquoted URI expression
7 ╭─[data/unquoted_uri.nix:1:1]
8
9 1 │ github:nerdypepper/statix
10 · ────────────┬────────────
11 · ╰────────────── Consider quoting this URI expression
12───╯
13
diff --git a/bin/tests/snapshots/main__useless_parens.snap b/bin/tests/snapshots/main__useless_parens.snap
new file mode 100644
index 0000000..d44176e
--- /dev/null
+++ b/bin/tests/snapshots/main__useless_parens.snap
@@ -0,0 +1,48 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W08] Warning: These parentheses can be omitted
7 ╭─[data/useless_parens.nix:16:3]
8
9 16 │ (null)
10 · ───┬──
11 · ╰──── Useless parentheses around body of let expression
12────╯
13[W08] Warning: These parentheses can be omitted
14 ╭─[data/useless_parens.nix:4:9]
15
16 4 │ b = ("hello");
17 · ────┬────
18 · ╰────── Useless parentheses around value in binding
19───╯
20[W08] Warning: These parentheses can be omitted
21 ╭─[data/useless_parens.nix:5:9]
22
23 5 │ c = (d);
24 · ─┬─
25 · ╰─── Useless parentheses around value in binding
26───╯
27[W08] Warning: These parentheses can be omitted
28 ╭─[data/useless_parens.nix:6:9]
29
30 6 │ e = ({ f = 2; });
31 · ──────┬─────
32 · ╰─────── Useless parentheses around value in binding
33───╯
34[W08] Warning: These parentheses can be omitted
35 ╭─[data/useless_parens.nix:10:7]
36
37 10 │ g = (1 + 2);
38 · ───┬───
39 · ╰───── Useless parentheses around value in binding
40────╯
41[W08] Warning: These parentheses can be omitted
42 ╭─[data/useless_parens.nix:11:7]
43
44 11 │ h = ({ inherit i; });
45 · ────────┬───────
46 · ╰───────── Useless parentheses around value in binding
47────╯
48
diff --git a/flake.nix b/flake.nix
index 3b9d011..b166817 100644
--- a/flake.nix
+++ b/flake.nix
@@ -93,11 +93,12 @@
93 "clippy" 93 "clippy"
94 "rust-src" 94 "rust-src"
95 ]; 95 ];
96 inherit (fenix.packages."${system}") rust-analyzer;
96 in 97 in
97 with pkgs; 98 pkgs.mkShell {
98 mkShell rec {
99 nativeBuildInputs = [ 99 nativeBuildInputs = [
100 cargo-watch 100 pkgs.cargo-watch
101 pkgs.cargo-insta
101 rust-analyzer 102 rust-analyzer
102 toolchain 103 toolchain
103 ]; 104 ];
diff --git a/lib/src/lints/useless_parens.rs b/lib/src/lints/useless_parens.rs
index 45d80ae..dccc717 100644
--- a/lib/src/lints/useless_parens.rs
+++ b/lib/src/lints/useless_parens.rs
@@ -3,7 +3,7 @@ use crate::{Diagnostic, Metadata, Report, Rule, Suggestion};
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use macros::lint;
5use rnix::{ 5use rnix::{
6 types::{KeyValue, Paren, ParsedType, TypedNode, Wrapper}, 6 types::{KeyValue, LetIn, Paren, ParsedType, TypedNode, Wrapper},
7 NodeOrToken, SyntaxElement, SyntaxKind, 7 NodeOrToken, SyntaxElement, SyntaxKind,
8}; 8};
9 9
@@ -71,7 +71,7 @@ fn do_thing(parsed_type_node: ParsedType) -> Option<Diagnostic> {
71 if let Some(inner) = value_in_parens.inner(); 71 if let Some(inner) = value_in_parens.inner();
72 then { 72 then {
73 let at = value_range; 73 let at = value_range;
74 let message = "Useless parentheses around value in `let` binding"; 74 let message = "Useless parentheses around value in binding";
75 let replacement = inner; 75 let replacement = inner;
76 Some(Diagnostic::suggest(at, message, Suggestion::new(at, replacement))) 76 Some(Diagnostic::suggest(at, message, Suggestion::new(at, replacement)))
77 } else { 77 } else {
@@ -98,7 +98,11 @@ fn do_thing(parsed_type_node: ParsedType) -> Option<Diagnostic> {
98 98
99 // ensure that we don't lint inside let-in statements 99 // ensure that we don't lint inside let-in statements
100 // we already lint such cases in previous match stmt 100 // we already lint such cases in previous match stmt
101 if KeyValue::cast(father_node).is_none(); 101 if KeyValue::cast(father_node.clone()).is_none();
102
103 // ensure that we don't lint inside let-bodies
104 // if this primitive is a let-body, we have already linted it
105 if LetIn::cast(father_node).is_none();
102 106
103 if let Some(inner_node) = paren_expr.inner(); 107 if let Some(inner_node) = paren_expr.inner();
104 if let Some(parsed_inner) = ParsedType::cast(inner_node); 108 if let Some(parsed_inner) = ParsedType::cast(inner_node);
diff --git a/vfs/src/lib.rs b/vfs/src/lib.rs
index 8b5df79..cd6cc03 100644
--- a/vfs/src/lib.rs
+++ b/vfs/src/lib.rs
@@ -7,7 +7,7 @@ use std::{
7use indexmap::IndexSet; 7use indexmap::IndexSet;
8 8
9#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)] 9#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
10pub struct FileId(u32); 10pub struct FileId(pub u32);
11 11
12#[derive(Debug, Default)] 12#[derive(Debug, Default)]
13pub struct Interner { 13pub struct Interner {