diff options
Diffstat (limited to 'lib/src')
-rw-r--r-- | lib/src/lib.rs | 30 | ||||
-rw-r--r-- | lib/src/lints/bool_comparison.rs | 23 | ||||
-rw-r--r-- | lib/src/lints/collapsible_let_in.rs | 37 | ||||
-rw-r--r-- | lib/src/lints/empty_let_in.rs | 25 | ||||
-rw-r--r-- | lib/src/lints/empty_pattern.rs | 31 | ||||
-rw-r--r-- | lib/src/lints/eta_reduction.rs | 30 | ||||
-rw-r--r-- | lib/src/lints/legacy_let_syntax.rs | 32 | ||||
-rw-r--r-- | lib/src/lints/manual_inherit.rs | 28 | ||||
-rw-r--r-- | lib/src/lints/manual_inherit_from.rs | 28 | ||||
-rw-r--r-- | lib/src/lints/redundant_pattern_bind.rs | 25 | ||||
-rw-r--r-- | lib/src/lints/unquoted_splice.rs | 28 | ||||
-rw-r--r-- | lib/src/lints/useless_parens.rs | 35 |
12 files changed, 308 insertions, 44 deletions
diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 196cbf8..5347666 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs | |||
@@ -226,25 +226,29 @@ pub trait Rule { | |||
226 | /// Contains information about the lint itself. Do not implement manually, | 226 | /// Contains information about the lint itself. Do not implement manually, |
227 | /// look at the `lint` attribute macro instead for implementing rules | 227 | /// look at the `lint` attribute macro instead for implementing rules |
228 | pub trait Metadata { | 228 | pub trait Metadata { |
229 | fn name() -> &'static str | 229 | fn name(&self) -> &'static str; |
230 | where | 230 | fn note(&self) -> &'static str; |
231 | Self: Sized; | 231 | fn code(&self) -> u32; |
232 | fn note() -> &'static str | 232 | fn report(&self) -> Report; |
233 | where | ||
234 | Self: Sized; | ||
235 | fn code() -> u32 | ||
236 | where | ||
237 | Self: Sized; | ||
238 | fn report() -> Report | ||
239 | where | ||
240 | Self: Sized; | ||
241 | fn match_with(&self, with: &SyntaxKind) -> bool; | 233 | fn match_with(&self, with: &SyntaxKind) -> bool; |
242 | fn match_kind(&self) -> Vec<SyntaxKind>; | 234 | fn match_kind(&self) -> Vec<SyntaxKind>; |
243 | } | 235 | } |
244 | 236 | ||
237 | /// Contains offline explanation for each lint | ||
238 | /// The `lint` macro scans nearby doc comments for | ||
239 | /// explanations and derives this trait. | ||
240 | /// | ||
241 | /// FIXME: the lint macro does way too much, maybe | ||
242 | /// split it into smaller macros. | ||
243 | pub trait Explain { | ||
244 | fn explanation(&self) -> &'static str { | ||
245 | "no explanation found" | ||
246 | } | ||
247 | } | ||
248 | |||
245 | /// Combines Rule and Metadata, do not implement manually, this is derived by | 249 | /// Combines Rule and Metadata, do not implement manually, this is derived by |
246 | /// the `lint` macro. | 250 | /// the `lint` macro. |
247 | pub trait Lint: Metadata + Rule + Send + Sync {} | 251 | pub trait Lint: Metadata + Explain + Rule + Send + Sync {} |
248 | 252 | ||
249 | /// Helper utility to take lints from modules and insert them into a map for efficient | 253 | /// Helper utility to take lints from modules and insert them into a map for efficient |
250 | /// access. Mapping is from a SyntaxKind to a list of lints that apply on that Kind. | 254 | /// access. Mapping is from a SyntaxKind to a list of lints that apply on that Kind. |
diff --git a/lib/src/lints/bool_comparison.rs b/lib/src/lints/bool_comparison.rs index 0b5733b..5c9bee8 100644 --- a/lib/src/lints/bool_comparison.rs +++ b/lib/src/lints/bool_comparison.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; | 1 | use crate::{make, Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
@@ -7,6 +7,25 @@ use rnix::{ | |||
7 | NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, | 7 | NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | /// ## What it does | ||
11 | /// Checks for expressions of the form `x == true`, `x != true` and | ||
12 | /// suggests using the variable directly. | ||
13 | /// | ||
14 | /// ## Why is this bad? | ||
15 | /// Unnecessary code. | ||
16 | /// | ||
17 | /// ## Example | ||
18 | /// Instead of checking the value of `x`: | ||
19 | /// | ||
20 | /// ``` | ||
21 | /// if x == true then 0 else 1 | ||
22 | /// ``` | ||
23 | /// | ||
24 | /// Use `x` directly: | ||
25 | /// | ||
26 | /// ``` | ||
27 | /// if x then 0 else 1 | ||
28 | /// ``` | ||
10 | #[lint( | 29 | #[lint( |
11 | name = "bool_comparison", | 30 | name = "bool_comparison", |
12 | note = "Unnecessary comparison with boolean", | 31 | note = "Unnecessary comparison with boolean", |
@@ -71,7 +90,7 @@ impl Rule for BoolComparison { | |||
71 | non_bool_side, | 90 | non_bool_side, |
72 | bool_side | 91 | bool_side |
73 | ); | 92 | ); |
74 | Some(Self::report().suggest(at, message, Suggestion::new(at, replacement))) | 93 | Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) |
75 | } else { | 94 | } else { |
76 | None | 95 | None |
77 | } | 96 | } |
diff --git a/lib/src/lints/collapsible_let_in.rs b/lib/src/lints/collapsible_let_in.rs index 878d218..21199a8 100644 --- a/lib/src/lints/collapsible_let_in.rs +++ b/lib/src/lints/collapsible_let_in.rs | |||
@@ -1,13 +1,41 @@ | |||
1 | use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; | 1 | use crate::{make, Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
5 | use rowan::Direction; | ||
6 | use rnix::{ | 5 | use rnix::{ |
7 | types::{LetIn, TypedNode}, | 6 | types::{LetIn, TypedNode}, |
8 | NodeOrToken, SyntaxElement, SyntaxKind, TextRange | 7 | NodeOrToken, SyntaxElement, SyntaxKind, TextRange, |
9 | }; | 8 | }; |
9 | use 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", |
@@ -47,7 +75,7 @@ impl Rule for CollapsibleLetIn { | |||
47 | let replacement = make::empty().node().clone(); | 75 | let replacement = make::empty().node().clone(); |
48 | 76 | ||
49 | Some( | 77 | Some( |
50 | Self::report() | 78 | self.report() |
51 | .diagnostic(first_annotation, first_message) | 79 | .diagnostic(first_annotation, first_message) |
52 | .suggest(second_annotation, second_message, Suggestion::new(replacement_at, replacement)) | 80 | .suggest(second_annotation, second_message, Suggestion::new(replacement_at, replacement)) |
53 | ) | 81 | ) |
@@ -57,4 +85,3 @@ impl Rule for CollapsibleLetIn { | |||
57 | } | 85 | } |
58 | } | 86 | } |
59 | } | 87 | } |
60 | |||
diff --git a/lib/src/lints/empty_let_in.rs b/lib/src/lints/empty_let_in.rs index aae1377..b255c23 100644 --- a/lib/src/lints/empty_let_in.rs +++ b/lib/src/lints/empty_let_in.rs | |||
@@ -1,12 +1,30 @@ | |||
1 | use crate::{Lint, Metadata, Report, Rule, Suggestion}; | 1 | use crate::{Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
5 | use rnix::{ | 5 | use rnix::{ |
6 | types::{LetIn, TypedNode, EntryHolder}, | 6 | types::{EntryHolder, LetIn, TypedNode}, |
7 | NodeOrToken, SyntaxElement, SyntaxKind, | 7 | NodeOrToken, SyntaxElement, SyntaxKind, |
8 | }; | 8 | }; |
9 | 9 | ||
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 | /// ``` | ||
10 | #[lint( | 28 | #[lint( |
11 | name = "empty let-in", | 29 | name = "empty let-in", |
12 | note = "Useless let-in expression", | 30 | note = "Useless let-in expression", |
@@ -31,11 +49,10 @@ impl Rule for EmptyLetIn { | |||
31 | let at = node.text_range(); | 49 | let at = node.text_range(); |
32 | let replacement = body; | 50 | let replacement = body; |
33 | let message = "This let-in expression has no entries"; | 51 | let message = "This let-in expression has no entries"; |
34 | Some(Self::report().suggest(at, message, Suggestion::new(at, replacement))) | 52 | Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) |
35 | } else { | 53 | } else { |
36 | None | 54 | None |
37 | } | 55 | } |
38 | } | 56 | } |
39 | } | 57 | } |
40 | } | 58 | } |
41 | |||
diff --git a/lib/src/lints/empty_pattern.rs b/lib/src/lints/empty_pattern.rs index 6fb7558..5312548 100644 --- a/lib/src/lints/empty_pattern.rs +++ b/lib/src/lints/empty_pattern.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; | 1 | use crate::{make, Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
@@ -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", |
@@ -28,7 +55,7 @@ impl Rule for EmptyPattern { | |||
28 | let at = node.text_range(); | 55 | let at = node.text_range(); |
29 | let message = "This pattern is empty, use `_` instead"; | 56 | let message = "This pattern is empty, use `_` instead"; |
30 | let replacement = make::ident("_").node().clone(); | 57 | let replacement = make::ident("_").node().clone(); |
31 | Some(Self::report().suggest(at, message, Suggestion::new(at, replacement))) | 58 | Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) |
32 | } else { | 59 | } else { |
33 | None | 60 | None |
34 | } | 61 | } |
diff --git a/lib/src/lints/eta_reduction.rs b/lib/src/lints/eta_reduction.rs index 79a5101..3a483d0 100644 --- a/lib/src/lints/eta_reduction.rs +++ b/lib/src/lints/eta_reduction.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use crate::{Lint, Metadata, Report, Rule, Suggestion}; | 1 | use crate::{Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
@@ -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", |
@@ -43,7 +69,7 @@ impl Rule for EtaReduction { | |||
43 | "Found eta-reduction: `{}`", | 69 | "Found eta-reduction: `{}`", |
44 | replacement.text().to_string() | 70 | replacement.text().to_string() |
45 | ); | 71 | ); |
46 | Some(Self::report().suggest(at, message, Suggestion::new(at, replacement))) | 72 | Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) |
47 | } else { | 73 | } else { |
48 | None | 74 | None |
49 | } | 75 | } |
diff --git a/lib/src/lints/legacy_let_syntax.rs b/lib/src/lints/legacy_let_syntax.rs index 2087e27..139f633 100644 --- a/lib/src/lints/legacy_let_syntax.rs +++ b/lib/src/lints/legacy_let_syntax.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; | 1 | use crate::{make, Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
@@ -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", |
@@ -36,7 +64,7 @@ impl Rule for ManualInherit { | |||
36 | let message = "Prefer `rec` over undocumented `let` syntax"; | 64 | let message = "Prefer `rec` over undocumented `let` syntax"; |
37 | let replacement = selected.node().clone(); | 65 | let replacement = selected.node().clone(); |
38 | 66 | ||
39 | Some(Self::report().suggest(at, message, Suggestion::new(at, replacement))) | 67 | Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) |
40 | } else { | 68 | } else { |
41 | None | 69 | None |
42 | } | 70 | } |
diff --git a/lib/src/lints/manual_inherit.rs b/lib/src/lints/manual_inherit.rs index 0a6933c..2d119c3 100644 --- a/lib/src/lints/manual_inherit.rs +++ b/lib/src/lints/manual_inherit.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; | 1 | use crate::{make, Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
@@ -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", |
@@ -35,7 +59,7 @@ impl Rule for ManualInherit { | |||
35 | let at = node.text_range(); | 59 | let at = node.text_range(); |
36 | let replacement = make::inherit_stmt(&[key]).node().clone(); | 60 | let replacement = make::inherit_stmt(&[key]).node().clone(); |
37 | let message = "This assignment is better written with `inherit`"; | 61 | let message = "This assignment is better written with `inherit`"; |
38 | Some(Self::report().suggest(at, message, Suggestion::new(at, replacement))) | 62 | Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) |
39 | } else { | 63 | } else { |
40 | None | 64 | None |
41 | } | 65 | } |
diff --git a/lib/src/lints/manual_inherit_from.rs b/lib/src/lints/manual_inherit_from.rs index 794aaf9..8d0f539 100644 --- a/lib/src/lints/manual_inherit_from.rs +++ b/lib/src/lints/manual_inherit_from.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; | 1 | use crate::{make, Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
@@ -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", |
@@ -40,7 +64,7 @@ impl Rule for ManualInherit { | |||
40 | make::inherit_from_stmt(set, &[key]).node().clone() | 64 | make::inherit_from_stmt(set, &[key]).node().clone() |
41 | }; | 65 | }; |
42 | let message = "This assignment is better written with `inherit`"; | 66 | let message = "This assignment is better written with `inherit`"; |
43 | Some(Self::report().suggest(at, message, Suggestion::new(at, replacement))) | 67 | Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) |
44 | } else { | 68 | } else { |
45 | None | 69 | None |
46 | } | 70 | } |
diff --git a/lib/src/lints/redundant_pattern_bind.rs b/lib/src/lints/redundant_pattern_bind.rs index aebc549..5b0711f 100644 --- a/lib/src/lints/redundant_pattern_bind.rs +++ b/lib/src/lints/redundant_pattern_bind.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use crate::{Lint, Metadata, Report, Rule, Suggestion}; | 1 | use crate::{Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
@@ -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 | )] |
16 | struct RedundantPatternBind; | 35 | struct RedundantPatternBind; |
@@ -32,7 +51,7 @@ impl Rule for RedundantPatternBind { | |||
32 | let at = node.text_range(); | 51 | let at = node.text_range(); |
33 | let message = format!("This pattern bind is redundant, use `{}` instead", ident.as_str()); | 52 | let message = format!("This pattern bind is redundant, use `{}` instead", ident.as_str()); |
34 | let replacement = ident.node().clone(); | 53 | let replacement = ident.node().clone(); |
35 | Some(Self::report().suggest(at, message, Suggestion::new(at, replacement))) | 54 | Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) |
36 | } else { | 55 | } else { |
37 | None | 56 | None |
38 | } | 57 | } |
diff --git a/lib/src/lints/unquoted_splice.rs b/lib/src/lints/unquoted_splice.rs index 4d1ed69..c2fd6e4 100644 --- a/lib/src/lints/unquoted_splice.rs +++ b/lib/src/lints/unquoted_splice.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; | 1 | use crate::{make, Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
@@ -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", |
@@ -24,7 +48,7 @@ impl Rule for UnquotedSplice { | |||
24 | let at = node.text_range(); | 48 | let at = node.text_range(); |
25 | let replacement = make::quote(&node).node().clone(); | 49 | let replacement = make::quote(&node).node().clone(); |
26 | let message = "Consider quoting this splice expression"; | 50 | let message = "Consider quoting this splice expression"; |
27 | Some(Self::report().suggest(at, message, Suggestion::new(at, replacement))) | 51 | Some(self.report().suggest(at, message, Suggestion::new(at, replacement))) |
28 | } else { | 52 | } else { |
29 | None | 53 | None |
30 | } | 54 | } |
diff --git a/lib/src/lints/useless_parens.rs b/lib/src/lints/useless_parens.rs index 2d6ba8f..36ad1b7 100644 --- a/lib/src/lints/useless_parens.rs +++ b/lib/src/lints/useless_parens.rs | |||
@@ -1,12 +1,37 @@ | |||
1 | use crate::{Lint, Metadata, Report, Rule, Suggestion, Diagnostic}; | 1 | use crate::{Diagnostic, Metadata, Report, Rule, Suggestion}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use macros::lint; |
5 | use rnix::{ | 5 | use rnix::{ |
6 | types::{ParsedType, KeyValue, Paren, TypedNode, Wrapper}, | 6 | types::{KeyValue, Paren, ParsedType, TypedNode, Wrapper}, |
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", |
@@ -27,7 +52,7 @@ impl Rule for UselessParens { | |||
27 | 52 | ||
28 | if let Some(diagnostic) = do_thing(parsed_type_node); | 53 | if let Some(diagnostic) = do_thing(parsed_type_node); |
29 | then { | 54 | then { |
30 | let mut report = Self::report(); | 55 | let mut report = self.report(); |
31 | report.diagnostics.push(diagnostic); | 56 | report.diagnostics.push(diagnostic); |
32 | Some(report) | 57 | Some(report) |
33 | } else { | 58 | } else { |
@@ -79,7 +104,7 @@ fn do_thing(parsed_type_node: ParsedType) -> Option<Diagnostic> { | |||
79 | if let Some(parsed_inner) = ParsedType::cast(inner_node); | 104 | if let Some(parsed_inner) = ParsedType::cast(inner_node); |
80 | if matches!( | 105 | if matches!( |
81 | parsed_inner, | 106 | parsed_inner, |
82 | ParsedType::List(_) | 107 | ParsedType::List(_) |
83 | | ParsedType::Paren(_) | 108 | | ParsedType::Paren(_) |
84 | | ParsedType::Str(_) | 109 | | ParsedType::Str(_) |
85 | | ParsedType::AttrSet(_) | 110 | | ParsedType::AttrSet(_) |
@@ -95,6 +120,6 @@ fn do_thing(parsed_type_node: ParsedType) -> Option<Diagnostic> { | |||
95 | None | 120 | None |
96 | } | 121 | } |
97 | }, | 122 | }, |
98 | _ => None | 123 | _ => None, |
99 | } | 124 | } |
100 | } | 125 | } |