aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/src/main.rs4
-rw-r--r--lib/src/lints/bool_comparison.rs27
-rw-r--r--lib/src/lints/collapsible_let_in.rs28
-rw-r--r--lib/src/lints/empty_let_in.rs19
-rw-r--r--lib/src/lints/empty_pattern.rs27
-rw-r--r--lib/src/lints/eta_reduction.rs26
-rw-r--r--lib/src/lints/legacy_let_syntax.rs28
-rw-r--r--lib/src/lints/manual_inherit.rs24
-rw-r--r--lib/src/lints/manual_inherit_from.rs24
-rw-r--r--lib/src/lints/redundant_pattern_bind.rs21
-rw-r--r--lib/src/lints/unquoted_splice.rs24
-rw-r--r--lib/src/lints/useless_parens.rs25
-rw-r--r--macros/src/explain.rs14
13 files changed, 265 insertions, 26 deletions
diff --git a/bin/src/main.rs b/bin/src/main.rs
index 138b243..31f6823 100644
--- a/bin/src/main.rs
+++ b/bin/src/main.rs
@@ -88,8 +88,8 @@ fn _main() -> Result<(), StatixErr> {
88 } 88 }
89 } 89 }
90 SubCommand::Explain(explain_config) => { 90 SubCommand::Explain(explain_config) => {
91 let explaination = explain::explain(explain_config.target)?; 91 let explanation = explain::explain(explain_config.target)?;
92 println!("{}", explaination); 92 println!("{}", explanation)
93 } 93 }
94 } 94 }
95 Ok(()) 95 Ok(())
diff --git a/lib/src/lints/bool_comparison.rs b/lib/src/lints/bool_comparison.rs
index 6636faf..5c9bee8 100644
--- a/lib/src/lints/bool_comparison.rs
+++ b/lib/src/lints/bool_comparison.rs
@@ -7,28 +7,25 @@ use rnix::{
7 NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, 7 NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
8}; 8};
9 9
10/// What it does 10/// ## What it does
11/// ------------ 11/// Checks for expressions of the form `x == true`, `x != true` and
12/// Checks for expressions of the form x == true, x != true and
13/// suggests using the variable directly. 12/// suggests using the variable directly.
14/// 13///
15/// Why is this bad? 14/// ## Why is this bad?
16/// ----------------
17/// Unnecessary code. 15/// Unnecessary code.
18/// 16///
19/// Example 17/// ## Example
20/// -------- 18/// Instead of checking the value of `x`:
21/// Instead of checking the value of x:
22/// 19///
23/// if x == true 20/// ```
24/// then 0 21/// if x == true then 0 else 1
25/// else 1 22/// ```
26/// 23///
27/// Use x directly: 24/// Use `x` directly:
28/// 25///
29/// if x 26/// ```
30/// then 0 27/// if x then 0 else 1
31/// else 1 28/// ```
32#[lint( 29#[lint(
33 name = "bool_comparison", 30 name = "bool_comparison",
34 note = "Unnecessary comparison with boolean", 31 note = "Unnecessary comparison with boolean",
diff --git a/lib/src/lints/collapsible_let_in.rs b/lib/src/lints/collapsible_let_in.rs
index cb76c37..21199a8 100644
--- a/lib/src/lints/collapsible_let_in.rs
+++ b/lib/src/lints/collapsible_let_in.rs
@@ -8,6 +8,34 @@ use rnix::{
8}; 8};
9use rowan::Direction; 9use rowan::Direction;
10 10
11/// ## What it does
12/// Checks for `let-in` expressions whose body is another `let-in`
13/// expression.
14///
15/// ## Why is this bad?
16/// Unnecessary code, the `let-in` expressions can be merged.
17///
18/// ## Example
19///
20/// ```
21/// let
22/// a = 2;
23/// in
24/// let
25/// b = 3;
26/// in
27/// a + b
28/// ```
29///
30/// Merge both `let-in` expressions:
31///
32/// ```
33/// let
34/// a = 2;
35/// b = 3;
36/// in
37/// a + b
38/// ```
11#[lint( 39#[lint(
12 name = "collapsible let in", 40 name = "collapsible let in",
13 note = "These let-in expressions are collapsible", 41 note = "These let-in expressions are collapsible",
diff --git a/lib/src/lints/empty_let_in.rs b/lib/src/lints/empty_let_in.rs
index 4e0887d..b255c23 100644
--- a/lib/src/lints/empty_let_in.rs
+++ b/lib/src/lints/empty_let_in.rs
@@ -7,7 +7,24 @@ use rnix::{
7 NodeOrToken, SyntaxElement, SyntaxKind, 7 NodeOrToken, SyntaxElement, SyntaxKind,
8}; 8};
9 9
10/// empty let-in found 10/// ## What it does
11/// Checks for `let-in` expressions which create no new bindings.
12///
13/// ## Why is this bad?
14/// `let-in` expressions that create no new bindings are useless.
15/// These are probably remnants from debugging or editing expressions.
16///
17/// ## Example
18///
19/// ```
20/// let in pkgs.statix
21/// ```
22///
23/// Preserve only the body of the `let-in` expression:
24///
25/// ```
26/// pkgs.statix
27/// ```
11#[lint( 28#[lint(
12 name = "empty let-in", 29 name = "empty let-in",
13 note = "Useless let-in expression", 30 note = "Useless let-in expression",
diff --git a/lib/src/lints/empty_pattern.rs b/lib/src/lints/empty_pattern.rs
index ef47c69..5312548 100644
--- a/lib/src/lints/empty_pattern.rs
+++ b/lib/src/lints/empty_pattern.rs
@@ -7,6 +7,33 @@ use rnix::{
7 NodeOrToken, SyntaxElement, SyntaxKind, 7 NodeOrToken, SyntaxElement, SyntaxKind,
8}; 8};
9 9
10/// ## What it does
11/// Checks for an empty variadic pattern: `{...}`, in a function
12/// argument.
13///
14/// ## Why is this bad?
15/// The intention with empty patterns is not instantly obvious. Prefer
16/// an underscore identifier instead, to indicate that the argument
17/// is being ignored.
18///
19/// ## Example
20///
21/// ```
22/// client = { ... }: {
23/// imports = [ self.nixosModules.irmaseal-pkg ];
24/// services.irmaseal-pkg.enable = true;
25/// };
26/// ```
27///
28/// Replace the empty variadic pattern with `_` to indicate that you
29/// intend to ignore the argument:
30///
31/// ```
32/// client = _: {
33/// imports = [ self.nixosModules.irmaseal-pkg ];
34/// services.irmaseal-pkg.enable = true;
35/// };
36/// ```
10#[lint( 37#[lint(
11 name = "empty pattern", 38 name = "empty pattern",
12 note = "Found empty pattern in function argument", 39 note = "Found empty pattern in function argument",
diff --git a/lib/src/lints/eta_reduction.rs b/lib/src/lints/eta_reduction.rs
index b329ae8..3a483d0 100644
--- a/lib/src/lints/eta_reduction.rs
+++ b/lib/src/lints/eta_reduction.rs
@@ -7,6 +7,32 @@ use rnix::{
7 NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, 7 NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
8}; 8};
9 9
10/// ## What it does
11/// Checks for eta-reducible functions, i.e.: converts lambda
12/// expressions into free standing functions where applicable.
13///
14/// ## Why is this bad?
15/// Oftentimes, eta-reduction results in code that is more natural
16/// to read.
17///
18/// ## Example
19///
20/// ```
21/// let
22/// double = i: 2 * i;
23/// in
24/// map (x: double x) [ 1 2 3 ]
25/// ```
26///
27/// The lambda passed to the `map` function is eta-reducible, and the
28/// result reads more naturally:
29///
30/// ```
31/// let
32/// double = i: 2 * i;
33/// in
34/// map double [ 1 2 3 ]
35/// ```
10#[lint( 36#[lint(
11 name = "eta reduction", 37 name = "eta reduction",
12 note = "This function expression is eta reducible", 38 note = "This function expression is eta reducible",
diff --git a/lib/src/lints/legacy_let_syntax.rs b/lib/src/lints/legacy_let_syntax.rs
index 8b37df8..139f633 100644
--- a/lib/src/lints/legacy_let_syntax.rs
+++ b/lib/src/lints/legacy_let_syntax.rs
@@ -7,6 +7,34 @@ use rnix::{
7 NodeOrToken, SyntaxElement, SyntaxKind, 7 NodeOrToken, SyntaxElement, SyntaxKind,
8}; 8};
9 9
10/// ## What it does
11/// Checks for legacy-let syntax that was never formalized.
12///
13/// ## Why is this bad?
14/// This syntax construct is undocumented, refrain from using it.
15///
16/// ## Example
17///
18/// Legacy let syntax makes use of an attribute set annotated with
19/// `let` and expects a `body` attribute.
20/// ```
21/// let {
22/// body = x + y;
23/// x = 2;
24/// y = 3;
25/// }
26/// ```
27///
28/// This is trivially representible via `rec`, which is documented
29/// and more widely known:
30///
31/// ```
32/// rec {
33/// body = x + y;
34/// x = 2;
35/// y = 3;
36/// }.body
37/// ```
10#[lint( 38#[lint(
11 name = "legacy let syntax", 39 name = "legacy let syntax",
12 note = "Using undocumented `let` syntax", 40 note = "Using undocumented `let` syntax",
diff --git a/lib/src/lints/manual_inherit.rs b/lib/src/lints/manual_inherit.rs
index 1c10cbf..2d119c3 100644
--- a/lib/src/lints/manual_inherit.rs
+++ b/lib/src/lints/manual_inherit.rs
@@ -7,6 +7,30 @@ use rnix::{
7 NodeOrToken, SyntaxElement, SyntaxKind, 7 NodeOrToken, SyntaxElement, SyntaxKind,
8}; 8};
9 9
10/// ## What it does
11/// Checks for bindings of the form `a = a`.
12///
13/// ## Why is this bad?
14/// If the aim is to bring attributes from a larger scope into
15/// the current scope, prefer an inherit statement.
16///
17/// ## Example
18///
19/// ```
20/// let
21/// a = 2;
22/// in
23/// { a = a; b = 3; }
24/// ```
25///
26/// Try `inherit` instead:
27///
28/// ```
29/// let
30/// a = 2;
31/// in
32/// { inherit a; b = 3; }
33/// ```
10#[lint( 34#[lint(
11 name = "manual inherit", 35 name = "manual inherit",
12 note = "Assignment instead of inherit", 36 note = "Assignment instead of inherit",
diff --git a/lib/src/lints/manual_inherit_from.rs b/lib/src/lints/manual_inherit_from.rs
index 146d55b..8d0f539 100644
--- a/lib/src/lints/manual_inherit_from.rs
+++ b/lib/src/lints/manual_inherit_from.rs
@@ -7,6 +7,30 @@ use rnix::{
7 NodeOrToken, SyntaxElement, SyntaxKind, 7 NodeOrToken, SyntaxElement, SyntaxKind,
8}; 8};
9 9
10/// ## What it does
11/// Checks for bindings of the form `a = someAttr.a`.
12///
13/// ## Why is this bad?
14/// If the aim is to extract or bring attributes of an attrset into
15/// scope, prefer an inherit statement.
16///
17/// ## Example
18///
19/// ```
20/// let
21/// mtl = pkgs.haskellPackages.mtl;
22/// in
23/// null
24/// ```
25///
26/// Try `inherit` instead:
27///
28/// ```
29/// let
30/// inherit (pkgs.haskellPackages) mtl;
31/// in
32/// null
33/// ```
10#[lint( 34#[lint(
11 name = "manual inherit from", 35 name = "manual inherit from",
12 note = "Assignment instead of inherit from", 36 note = "Assignment instead of inherit from",
diff --git a/lib/src/lints/redundant_pattern_bind.rs b/lib/src/lints/redundant_pattern_bind.rs
index ad1aba8..5b0711f 100644
--- a/lib/src/lints/redundant_pattern_bind.rs
+++ b/lib/src/lints/redundant_pattern_bind.rs
@@ -7,10 +7,29 @@ use rnix::{
7 NodeOrToken, SyntaxElement, SyntaxKind, 7 NodeOrToken, SyntaxElement, SyntaxKind,
8}; 8};
9 9
10/// ## What it does
11/// Checks for binds of the form `inputs @ { ... }` in function
12/// arguments.
13///
14/// ## Why is this bad?
15/// The variadic pattern here is redundant, as it does not capture
16/// anything.
17///
18/// ## Example
19///
20/// ```
21/// inputs @ { ... }: inputs.nixpkgs
22/// ```
23///
24/// Remove the pattern altogether:
25///
26/// ```
27/// inputs: inputs.nixpkgs
28/// ```
10#[lint( 29#[lint(
11 name = "redundant pattern bind", 30 name = "redundant pattern bind",
12 note = "Found redundant pattern bind in function argument", 31 note = "Found redundant pattern bind in function argument",
13 code = 10, 32 code = 11,
14 match_with = SyntaxKind::NODE_PATTERN 33 match_with = SyntaxKind::NODE_PATTERN
15)] 34)]
16struct RedundantPatternBind; 35struct RedundantPatternBind;
diff --git a/lib/src/lints/unquoted_splice.rs b/lib/src/lints/unquoted_splice.rs
index 2831c1b..c2fd6e4 100644
--- a/lib/src/lints/unquoted_splice.rs
+++ b/lib/src/lints/unquoted_splice.rs
@@ -7,6 +7,30 @@ use rnix::{
7 NodeOrToken, SyntaxElement, SyntaxKind, 7 NodeOrToken, SyntaxElement, SyntaxKind,
8}; 8};
9 9
10/// ## What it does
11/// Checks for antiquote/splice expressions that are not quoted.
12///
13/// ## Why is this bad?
14/// An *anti*quoted expression should always occur within a *quoted*
15/// expression.
16///
17/// ## Example
18///
19/// ```
20/// let
21/// pkgs = nixpkgs.legacyPackages.${system};
22/// in
23/// pkgs
24/// ```
25///
26/// Quote the splice expression:
27///
28/// ```
29/// let
30/// pkgs = nixpkgs.legacyPackages."${system}";
31/// in
32/// pkgs
33/// ```
10#[lint( 34#[lint(
11 name = "unquoted splice", 35 name = "unquoted splice",
12 note = "Found unquoted splice expression", 36 note = "Found unquoted splice expression",
diff --git a/lib/src/lints/useless_parens.rs b/lib/src/lints/useless_parens.rs
index 9dfeabb..36ad1b7 100644
--- a/lib/src/lints/useless_parens.rs
+++ b/lib/src/lints/useless_parens.rs
@@ -7,6 +7,31 @@ use rnix::{
7 NodeOrToken, SyntaxElement, SyntaxKind, 7 NodeOrToken, SyntaxElement, SyntaxKind,
8}; 8};
9 9
10/// ## What it does
11/// Checks for unnecessary parentheses.
12///
13/// ## Why is this bad?
14/// Unnecessarily parenthesized code is hard to read.
15///
16/// ## Example
17///
18/// ```
19/// let
20/// double = (x: 2 * x);
21/// ls = map (double) [ 1 2 3 ];
22/// in
23/// (2 + 3)
24/// ```
25///
26/// Remove unnecessary parentheses:
27///
28/// ```
29/// let
30/// double = x: 2 * x;
31/// ls = map double [ 1 2 3 ];
32/// in
33/// 2 + 3
34/// ```
10#[lint( 35#[lint(
11 name = "useless parens", 36 name = "useless parens",
12 note = "These parentheses can be omitted", 37 note = "These parentheses can be omitted",
diff --git a/macros/src/explain.rs b/macros/src/explain.rs
index 8b00740..9bc3c29 100644
--- a/macros/src/explain.rs
+++ b/macros/src/explain.rs
@@ -7,15 +7,15 @@ pub fn generate_explain_impl(struct_item: &ItemStruct) -> TokenStream2 {
7 let explain = struct_item 7 let explain = struct_item
8 .attrs 8 .attrs
9 .iter() 9 .iter()
10 .filter_map(|a| a.parse_meta().ok()) 10 .filter_map(|attr| match attr.parse_meta().ok() {
11 .filter_map(|meta| match meta { 11 Some(Meta::NameValue(MetaNameValue {
12 Meta::NameValue(MetaNameValue { path, lit, .. }) if path.is_ident("doc") => Some(lit), 12 path,
13 _ => None, 13 lit: Lit::Str(str_lit),
14 }) 14 ..
15 .filter_map(|lit| match lit { 15 })) if path.is_ident("doc") => Some(str_lit.value()),
16 Lit::Str(str_lit) => Some(str_lit.value()),
17 _ => None, 16 _ => None,
18 }) 17 })
18 .map(|s| s.strip_prefix(' ').unwrap_or(&s).to_owned())
19 .collect::<Vec<_>>() 19 .collect::<Vec<_>>()
20 .join("\n"); 20 .join("\n");
21 quote! { 21 quote! {