diff options
author | Akshay <[email protected]> | 2021-10-15 08:36:59 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2021-10-15 08:36:59 +0100 |
commit | 2993b205dbad0f7fd3755f9f3cfb3003c79a8b5a (patch) | |
tree | ca1b8d8b3e95677b83865b842925bbe391815c14 /lib | |
parent | 45ab8abf76aa8157b43f3c7675b11fb51fac66d9 (diff) |
new lint: useless_parens
Diffstat (limited to 'lib')
-rw-r--r-- | lib/src/lints.rs | 1 | ||||
-rw-r--r-- | lib/src/lints/useless_parens.rs | 99 | ||||
-rw-r--r-- | lib/src/make.rs | 4 |
3 files changed, 102 insertions, 2 deletions
diff --git a/lib/src/lints.rs b/lib/src/lints.rs index 868cd26..f7e98ac 100644 --- a/lib/src/lints.rs +++ b/lib/src/lints.rs | |||
@@ -8,4 +8,5 @@ lint_map! { | |||
8 | legacy_let_syntax, | 8 | legacy_let_syntax, |
9 | collapsible_let_in, | 9 | collapsible_let_in, |
10 | eta_reduction, | 10 | eta_reduction, |
11 | useless_parens, | ||
11 | } | 12 | } |
diff --git a/lib/src/lints/useless_parens.rs b/lib/src/lints/useless_parens.rs new file mode 100644 index 0000000..d93f06c --- /dev/null +++ b/lib/src/lints/useless_parens.rs | |||
@@ -0,0 +1,99 @@ | |||
1 | use crate::{Lint, Metadata, Report, Rule, Suggestion, Diagnostic}; | ||
2 | |||
3 | use if_chain::if_chain; | ||
4 | use macros::lint; | ||
5 | use rnix::{ | ||
6 | types::{ParsedType, KeyValue, Paren, TypedNode, Wrapper}, | ||
7 | NodeOrToken, SyntaxElement, SyntaxKind, | ||
8 | }; | ||
9 | |||
10 | #[lint( | ||
11 | name = "useless parens", | ||
12 | note = "These parentheses can be omitted", | ||
13 | code = 8, | ||
14 | match_with = [ | ||
15 | SyntaxKind::NODE_KEY_VALUE, | ||
16 | SyntaxKind::NODE_PAREN, | ||
17 | SyntaxKind::NODE_LET_IN, | ||
18 | ] | ||
19 | )] | ||
20 | struct UselessParens; | ||
21 | |||
22 | impl Rule for UselessParens { | ||
23 | fn validate(&self, node: &SyntaxElement) -> Option<Report> { | ||
24 | if_chain! { | ||
25 | if let NodeOrToken::Node(node) = node; | ||
26 | if let Some(parsed_type_node) = ParsedType::cast(node.clone()); | ||
27 | |||
28 | if let Some(diagnostic) = do_thing(parsed_type_node); | ||
29 | then { | ||
30 | let mut report = Self::report(); | ||
31 | report.diagnostics.push(diagnostic); | ||
32 | Some(report) | ||
33 | } else { | ||
34 | None | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | fn do_thing(parsed_type_node: ParsedType) -> Option<Diagnostic> { | ||
41 | match parsed_type_node { | ||
42 | ParsedType::KeyValue(kv) => if_chain! { | ||
43 | if let Some(value_node) = kv.value(); | ||
44 | let value_range = value_node.text_range(); | ||
45 | if let Some(value_in_parens) = Paren::cast(value_node); | ||
46 | if let Some(inner) = value_in_parens.inner(); | ||
47 | then { | ||
48 | let at = value_range; | ||
49 | let message = "Useless parentheses around value in `let` binding"; | ||
50 | let replacement = inner; | ||
51 | Some(Diagnostic::suggest(at, message, Suggestion::new(at, replacement))) | ||
52 | } else { | ||
53 | None | ||
54 | } | ||
55 | }, | ||
56 | ParsedType::LetIn(let_in) => if_chain! { | ||
57 | if let Some(body_node) = let_in.body(); | ||
58 | let body_range = body_node.text_range(); | ||
59 | if let Some(body_as_parens) = Paren::cast(body_node); | ||
60 | if let Some(inner) = body_as_parens.inner(); | ||
61 | then { | ||
62 | let at = body_range; | ||
63 | let message = "Useless parentheses around body of `let` expression"; | ||
64 | let replacement = inner; | ||
65 | Some(Diagnostic::suggest(at, message, Suggestion::new(at, replacement))) | ||
66 | } else { | ||
67 | None | ||
68 | } | ||
69 | }, | ||
70 | ParsedType::Paren(paren_expr) => if_chain! { | ||
71 | let paren_expr_range = paren_expr.node().text_range(); | ||
72 | if let Some(father_node) = paren_expr.node().parent(); | ||
73 | |||
74 | // ensure that we don't lint inside let-in statements | ||
75 | // we already lint such cases in previous match stmt | ||
76 | if KeyValue::cast(father_node).is_none(); | ||
77 | |||
78 | if let Some(inner_node) = paren_expr.inner(); | ||
79 | if let Some(parsed_inner) = ParsedType::cast(inner_node); | ||
80 | if matches!( | ||
81 | parsed_inner, | ||
82 | ParsedType::List(_) | ||
83 | | ParsedType::Paren(_) | ||
84 | | ParsedType::Str(_) | ||
85 | | ParsedType::AttrSet(_) | ||
86 | | ParsedType::Select(_) | ||
87 | ); | ||
88 | then { | ||
89 | let at = paren_expr_range; | ||
90 | let message = "Useless parentheses around primitive expression"; | ||
91 | let replacement = parsed_inner.node().clone(); | ||
92 | Some(Diagnostic::suggest(at, message, Suggestion::new(at, replacement))) | ||
93 | } else { | ||
94 | None | ||
95 | } | ||
96 | }, | ||
97 | _ => None | ||
98 | } | ||
99 | } | ||
diff --git a/lib/src/make.rs b/lib/src/make.rs index aec32d5..217ba12 100644 --- a/lib/src/make.rs +++ b/lib/src/make.rs | |||
@@ -58,10 +58,10 @@ pub fn attrset( | |||
58 | 58 | ||
59 | writeln!(buffer, "{}{{", if recursive { "rec " } else { "" }).unwrap(); | 59 | writeln!(buffer, "{}{{", if recursive { "rec " } else { "" }).unwrap(); |
60 | for inherit in inherits.into_iter() { | 60 | for inherit in inherits.into_iter() { |
61 | writeln!(buffer, " {}", inherit.node().text()).unwrap(); | 61 | writeln!(buffer, " {}", inherit.node().text()).unwrap(); |
62 | } | 62 | } |
63 | for entry in entries.into_iter() { | 63 | for entry in entries.into_iter() { |
64 | writeln!(buffer, " {}", entry.node().text()).unwrap(); | 64 | writeln!(buffer, " {}", entry.node().text()).unwrap(); |
65 | } | 65 | } |
66 | write!(buffer, "}}").unwrap(); | 66 | write!(buffer, "}}").unwrap(); |
67 | 67 | ||