aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2022-02-20 05:25:20 +0000
committerAkshay <[email protected]>2022-02-20 05:31:36 +0000
commitc7874ec26d6499a1196ce23432beb33764490dec (patch)
tree226b8dce61e5f7580be9de7ebde72212bb94f682
parente8130a90dca048d195603281f72e1af0b4f7ccc6 (diff)
new lint: useless_has_attr
-rw-r--r--bin/tests/data/useless_has_attr.nix9
-rw-r--r--bin/tests/main.rs3
-rw-r--r--bin/tests/snapshots/main__useless_has_attr.snap34
-rw-r--r--lib/src/lints.rs1
-rw-r--r--lib/src/lints/useless_has_attr.rs83
-rw-r--r--lib/src/make.rs4
6 files changed, 133 insertions, 1 deletions
diff --git a/bin/tests/data/useless_has_attr.nix b/bin/tests/data/useless_has_attr.nix
new file mode 100644
index 0000000..f4c9fd5
--- /dev/null
+++ b/bin/tests/data/useless_has_attr.nix
@@ -0,0 +1,9 @@
1[
2 # trivial
3 (if x ? a then x.a else default)
4 (if x.a ? b then x.a.b else default)
5 (if x ? a.b then x.a.b else default)
6
7 # complex body
8 (if x ? a then x.a else if b then c else d)
9]
diff --git a/bin/tests/main.rs b/bin/tests/main.rs
index 3403f65..3425cfa 100644
--- a/bin/tests/main.rs
+++ b/bin/tests/main.rs
@@ -64,5 +64,6 @@ test_lint! {
64 faster_groupby => session_info!("2.5"), 64 faster_groupby => session_info!("2.5"),
65 faster_zipattrswith => session_info!("2.6"), 65 faster_zipattrswith => session_info!("2.6"),
66 deprecated_to_path => session_info!("2.4"), 66 deprecated_to_path => session_info!("2.4"),
67 bool_simplification 67 bool_simplification,
68 useless_has_attr
68} 69}
diff --git a/bin/tests/snapshots/main__useless_has_attr.snap b/bin/tests/snapshots/main__useless_has_attr.snap
new file mode 100644
index 0000000..df72eaf
--- /dev/null
+++ b/bin/tests/snapshots/main__useless_has_attr.snap
@@ -0,0 +1,34 @@
1---
2source: bin/tests/main.rs
3expression: "&out"
4
5---
6[W19] Warning: This `if` expression can be simplified with `or`
7 ╭─[data/useless_has_attr.nix:3:4]
8
9 3 │ (if x ? a then x.a else default)
10 · ───────────────┬──────────────
11 · ╰──────────────── Consider using x.a or default instead of this if expression
12───╯
13[W19] Warning: This `if` expression can be simplified with `or`
14 ╭─[data/useless_has_attr.nix:4:4]
15
16 4 │ (if x.a ? b then x.a.b else default)
17 · ─────────────────┬────────────────
18 · ╰────────────────── Consider using x.a.b or default instead of this if expression
19───╯
20[W19] Warning: This `if` expression can be simplified with `or`
21 ╭─[data/useless_has_attr.nix:5:4]
22
23 5 │ (if x ? a.b then x.a.b else default)
24 · ─────────────────┬────────────────
25 · ╰────────────────── Consider using x.a.b or default instead of this if expression
26───╯
27[W19] Warning: This `if` expression can be simplified with `or`
28 ╭─[data/useless_has_attr.nix:8:4]
29
30 8 │ (if x ? a then x.a else if b then c else d)
31 · ────────────────────┬────────────────────
32 · ╰────────────────────── Consider using x.a or (if b then c else d) instead of this if expression
33───╯
34
diff --git a/lib/src/lints.rs b/lib/src/lints.rs
index 582cabe..7d73277 100644
--- a/lib/src/lints.rs
+++ b/lib/src/lints.rs
@@ -19,4 +19,5 @@ lints! {
19 faster_zipattrswith, 19 faster_zipattrswith,
20 deprecated_to_path, 20 deprecated_to_path,
21 bool_simplification, 21 bool_simplification,
22 useless_has_attr,
22} 23}
diff --git a/lib/src/lints/useless_has_attr.rs b/lib/src/lints/useless_has_attr.rs
new file mode 100644
index 0000000..aae560a
--- /dev/null
+++ b/lib/src/lints/useless_has_attr.rs
@@ -0,0 +1,83 @@
1use crate::{make, session::SessionInfo, Metadata, Report, Rule, Suggestion};
2
3use if_chain::if_chain;
4use macros::lint;
5use rnix::{
6 types::{BinOp, BinOpKind, IfElse, Select, TypedNode},
7 NodeOrToken, SyntaxElement, SyntaxKind,
8};
9
10/// ## What it does
11/// Checks for expressions that use the "has attribute" operator: `?`,
12/// where the `or` operator would suffice.
13///
14/// ## Why is this bad?
15/// The `or` operator is more readable.
16///
17/// ## Example
18/// ```nix
19/// if x ? a then x.a else some_default
20/// ```
21///
22/// Use `or` instead:
23///
24/// ```nix
25/// x.a or some_default
26/// ```
27#[lint(
28 name = "useless_has_attr",
29 note = "This `if` expression can be simplified with `or`",
30 code = 19,
31 match_with = SyntaxKind::NODE_IF_ELSE
32)]
33struct UselessHasAttr;
34
35impl Rule for UselessHasAttr {
36 fn validate(&self, node: &SyntaxElement, _sess: &SessionInfo) -> Option<Report> {
37 if_chain! {
38 if let NodeOrToken::Node(node) = node;
39 if let Some(if_else_expr) = IfElse::cast(node.clone());
40 if let Some(condition_expr) = if_else_expr.condition();
41 if let Some(default_expr) = if_else_expr.else_body();
42 if let Some(cond_bin_expr) = BinOp::cast(condition_expr.clone());
43 if let Some(BinOpKind::IsSet) = cond_bin_expr.operator();
44
45 // set ? attr_path
46 // ^^^--------------- lhs
47 // ^^^^^^^^^^--- rhs
48 if let Some(set) = cond_bin_expr.lhs();
49 if let Some(attr_path) = cond_bin_expr.rhs();
50
51 // check if body of the `if` expression is of the form `set.attr_path`
52 if let Some(body_expr) = if_else_expr.body();
53 if let Some(body_select_expr) = Select::cast(body_expr.clone());
54 let expected_body = make::select(&set, &attr_path);
55
56 // text comparison will do for now
57 if body_select_expr.node().text() == expected_body.node().text();
58 then {
59 let at = node.text_range();
60 // `or` is tightly binding, we need to parenthesize non-literal exprs
61 let default_with_parens = match default_expr.kind() {
62 SyntaxKind::NODE_LIST
63 | SyntaxKind::NODE_PAREN
64 | SyntaxKind::NODE_STRING
65 | SyntaxKind::NODE_ATTR_SET
66 | SyntaxKind::NODE_IDENT => default_expr,
67 _ => make::parenthesize(&default_expr).node().clone(),
68 };
69 let replacement = make::or_default(&set, &attr_path, &default_with_parens).node().clone();
70 let message = format!(
71 "Consider using `{}` instead of this `if` expression",
72 replacement
73 );
74 Some(
75 self.report()
76 .suggest(at, message, Suggestion::new(at, replacement)),
77 )
78 } else {
79 None
80 }
81 }
82 }
83}
diff --git a/lib/src/make.rs b/lib/src/make.rs
index c36fa7e..2c33ac8 100644
--- a/lib/src/make.rs
+++ b/lib/src/make.rs
@@ -88,3 +88,7 @@ pub fn empty() -> types::Root {
88pub fn binary(lhs: &SyntaxNode, op: &str, rhs: &SyntaxNode) -> types::BinOp { 88pub fn binary(lhs: &SyntaxNode, op: &str, rhs: &SyntaxNode) -> types::BinOp {
89 ast_from_text(&format!("{} {} {}", lhs, op, rhs)) 89 ast_from_text(&format!("{} {} {}", lhs, op, rhs))
90} 90}
91
92pub fn or_default(set: &SyntaxNode, index: &SyntaxNode, default: &SyntaxNode) -> types::OrDefault {
93 ast_from_text(&format!("{}.{} or {}", set, index, default))
94}