aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-09-13 17:50:25 +0100
committerAkshay <[email protected]>2021-09-13 17:50:25 +0100
commit4152367f5dee2a70ff49f4aff4040d5d433b8e44 (patch)
treefea606b3121702ae2f714f51697135e3c7b9a9ee /lib
parent7a3c3822ba4a368b0475bc5de89ced78fa8b3cb5 (diff)
add demo lint: bool_comparison
Diffstat (limited to 'lib')
-rw-r--r--lib/Cargo.toml4
-rw-r--r--lib/src/lib.rs50
-rw-r--r--lib/src/lints.rs2
-rw-r--r--lib/src/lints/bool_comparison.rs78
4 files changed, 129 insertions, 5 deletions
diff --git a/lib/Cargo.toml b/lib/Cargo.toml
index 49e2e49..e371c5d 100644
--- a/lib/Cargo.toml
+++ b/lib/Cargo.toml
@@ -6,3 +6,7 @@ edition = "2018"
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 7
8[dependencies] 8[dependencies]
9rnix = "0.9.0"
10if_chain = "1.0"
11macros = { path = "../macros" }
12lazy_static = "1.0"
diff --git a/lib/src/lib.rs b/lib/src/lib.rs
index 31e1bb2..537f4b3 100644
--- a/lib/src/lib.rs
+++ b/lib/src/lib.rs
@@ -1,7 +1,47 @@
1#[cfg(test)] 1pub mod lints;
2mod tests { 2
3 #[test] 3use rnix::{SyntaxElement, SyntaxKind, TextRange};
4 fn it_works() { 4use std::ops::Deref;
5 assert_eq!(2 + 2, 4); 5
6pub trait Rule {
7 fn validate(&self, node: &SyntaxElement) -> Option<Diagnostic>;
8}
9
10#[derive(Debug)]
11pub struct Diagnostic {
12 pub at: TextRange,
13 pub message: String,
14}
15
16impl Diagnostic {
17 pub fn new(at: TextRange, message: String) -> Self {
18 Self { at, message }
6 } 19 }
7} 20}
21
22pub trait Metadata {
23 fn name(&self) -> &str;
24 fn note(&self) -> &str;
25 fn match_with(&self, with: &SyntaxKind) -> bool;
26}
27
28pub trait Lint: Metadata + Rule + Send + Sync {}
29
30// #[macro_export]
31// macro_rules! lint_map {
32// ($($s:ident),*,) => {
33// lint_map($($s),*);
34// }
35// ($($s:ident),*) => {
36// use ::std::collections::HashMap;
37// use rnix::SyntaxKind;
38// $(
39// mod $s;
40// )*
41// ::lazy_static::lazy_static! {
42// pub static ref RULES: HashMap<SyntaxKind, &'static Box<dyn $crate::Lint>> = {
43// vec![$(&*$s::LINT,)*]
44// }
45// }
46// }
47// }
diff --git a/lib/src/lints.rs b/lib/src/lints.rs
new file mode 100644
index 0000000..b0df71b
--- /dev/null
+++ b/lib/src/lints.rs
@@ -0,0 +1,2 @@
1pub mod bool_comparison;
2pub mod with_list;
diff --git a/lib/src/lints/bool_comparison.rs b/lib/src/lints/bool_comparison.rs
new file mode 100644
index 0000000..4476b31
--- /dev/null
+++ b/lib/src/lints/bool_comparison.rs
@@ -0,0 +1,78 @@
1use crate::{Diagnostic, Lint, Metadata, Rule};
2
3use if_chain::if_chain;
4use macros::lint;
5use rnix::{
6 types::{BinOp, BinOpKind, Ident, TokenWrapper, TypedNode},
7 NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
8};
9
10#[lint(
11 name = "bool_comparison",
12 note = "Unnecessary comparison with boolean",
13 match_with = "SyntaxKind::NODE_BIN_OP"
14)]
15struct BoolComparison;
16
17impl Rule for BoolComparison {
18 fn validate(&self, node: &SyntaxElement) -> Option<Diagnostic> {
19 if_chain! {
20 if let NodeOrToken::Node(bin_op_node) = node;
21 if let Some(bin_expr) = BinOp::cast(bin_op_node.clone());
22 if let Some(lhs) = bin_expr.lhs();
23 if let Some(rhs) = bin_expr.rhs();
24
25 if let BinOpKind::Equal | BinOpKind::NotEqual = bin_expr.operator();
26 let (non_bool_side, bool_side) = if is_boolean_ident(&lhs) {
27 (rhs, lhs)
28 } else if is_boolean_ident(&rhs) {
29 (lhs, rhs)
30 } else {
31 return None
32 };
33 then {
34 let at = node.text_range();
35 let message = format!(
36 "Comparing `{}` with boolean literal `{}`",
37 non_bool_side,
38 bool_side
39 );
40 dbg!(Some(Diagnostic::new(at, message)))
41 } else {
42 None
43 }
44 }
45 }
46}
47
48// not entirely accurate, underhanded nix programmers might write `true = false`
49fn is_boolean_ident(node: &SyntaxNode) -> bool {
50 if let Some(ident_expr) = Ident::cast(node.clone()) {
51 ident_expr.as_str() == "true" || ident_expr.as_str() == "false"
52 } else {
53 false
54 }
55}
56
57// #[cfg(test)]
58// mod tests {
59// use super::*;
60// use rnix::{parser, WalkEvent};
61//
62// #[test]
63// fn trivial() {
64// let src = r#"
65// a == true
66// "#;
67// let parsed = rnix::parse(src).as_result().ok().unwrap();
68// let _ = parsed
69// .node()
70// .preorder_with_tokens()
71// .filter_map(|event| match event {
72// WalkEvent::Enter(t) => Some(t),
73// _ => None,
74// })
75// .map(|node| BoolComparison.validate(&node))
76// .collect::<Vec<_>>();
77// }
78// }