aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-10-31 09:05:26 +0000
committerAkshay <[email protected]>2021-10-31 16:05:15 +0000
commite8c955da4cbb042e6f9b89307d143f5bfa6779fa (patch)
tree0ae4ec11fd3dc0f8b69bc0f32c08858ef23a9485 /lib
parent246c69f8cfc74cf4c56fdaceaeb0562ed1f3dad5 (diff)
add `explain` subcommand and explanations to all lints
Diffstat (limited to 'lib')
-rw-r--r--lib/src/lib.rs30
-rw-r--r--lib/src/lints/bool_comparison.rs23
-rw-r--r--lib/src/lints/collapsible_let_in.rs37
-rw-r--r--lib/src/lints/empty_let_in.rs25
-rw-r--r--lib/src/lints/empty_pattern.rs31
-rw-r--r--lib/src/lints/eta_reduction.rs30
-rw-r--r--lib/src/lints/legacy_let_syntax.rs32
-rw-r--r--lib/src/lints/manual_inherit.rs28
-rw-r--r--lib/src/lints/manual_inherit_from.rs28
-rw-r--r--lib/src/lints/redundant_pattern_bind.rs25
-rw-r--r--lib/src/lints/unquoted_splice.rs28
-rw-r--r--lib/src/lints/useless_parens.rs35
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
228pub trait Metadata { 228pub 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.
243pub 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.
247pub trait Lint: Metadata + Rule + Send + Sync {} 251pub 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 @@
1use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; 1use crate::{make, Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use 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 @@
1use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; 1use crate::{make, Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use macros::lint;
5use rowan::Direction;
6use rnix::{ 5use rnix::{
7 types::{LetIn, TypedNode}, 6 types::{LetIn, TypedNode},
8 NodeOrToken, SyntaxElement, SyntaxKind, TextRange 7 NodeOrToken, SyntaxElement, SyntaxKind, TextRange,
9}; 8};
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",
@@ -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 @@
1use crate::{Lint, Metadata, Report, Rule, Suggestion}; 1use crate::{Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use macros::lint;
5use rnix::{ 5use 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 @@
1use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; 1use crate::{make, Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use 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 @@
1use crate::{Lint, Metadata, Report, Rule, Suggestion}; 1use crate::{Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use 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 @@
1use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; 1use crate::{make, Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use 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 @@
1use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; 1use crate::{make, Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use 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 @@
1use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; 1use crate::{make, Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use 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 @@
1use crate::{Lint, Metadata, Report, Rule, Suggestion}; 1use crate::{Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use 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)]
16struct RedundantPatternBind; 35struct 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 @@
1use crate::{make, Lint, Metadata, Report, Rule, Suggestion}; 1use crate::{make, Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use 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 @@
1use crate::{Lint, Metadata, Report, Rule, Suggestion, Diagnostic}; 1use crate::{Diagnostic, Metadata, Report, Rule, Suggestion};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use macros::lint;
5use rnix::{ 5use 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}