From 4152367f5dee2a70ff49f4aff4040d5d433b8e44 Mon Sep 17 00:00:00 2001 From: Akshay Date: Mon, 13 Sep 2021 22:20:25 +0530 Subject: add demo lint: bool_comparison --- Cargo.lock | 179 +++++++++++++++++++++++++++++++++++++++ lib/Cargo.toml | 4 + lib/src/lib.rs | 50 +++++++++-- lib/src/lints.rs | 2 + lib/src/lints/bool_comparison.rs | 78 +++++++++++++++++ 5 files changed, 308 insertions(+), 5 deletions(-) create mode 100644 Cargo.lock create mode 100644 lib/src/lints.rs create mode 100644 lib/src/lints/bool_comparison.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..73188a4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,179 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ariadne" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7080ae01b2f0c312065d4914cd0f0de045eb8832e9415b355106a6cff3073cb4" +dependencies = [ + "yansi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bin" +version = "0.1.0" +dependencies = [ + "ariadne", + "lib", +] + +[[package]] +name = "cbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b6ad25ae296159fb0da12b970b2fe179b234584d7cd294c891e2bbb284466b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "countme" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328b822bdcba4d4e402be8d9adb6eebf269f969f8eadef977a553ff3c4fbcb58" + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lib" +version = "0.1.0" +dependencies = [ + "if_chain", + "lazy_static", + "macros", + "rnix", +] + +[[package]] +name = "macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rnix" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b37f8af07a0354606141df076458660af7e22238e4117a041c21c548080addd" +dependencies = [ + "cbitset", + "rowan", + "smol_str", +] + +[[package]] +name = "rowan" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b36e449f3702f3b0c821411db1cbdf30fb451726a9456dce5dabcd44420043" +dependencies = [ + "countme", + "hashbrown", + "memoffset", + "rustc-hash", + "text-size", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "smol_str" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b203e79e90905594272c1c97c7af701533d42adaab0beb3859018e477d54a3b0" + +[[package]] +name = "syn" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "text-size" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "yansi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" 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" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +rnix = "0.9.0" +if_chain = "1.0" +macros = { path = "../macros" } +lazy_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 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); +pub mod lints; + +use rnix::{SyntaxElement, SyntaxKind, TextRange}; +use std::ops::Deref; + +pub trait Rule { + fn validate(&self, node: &SyntaxElement) -> Option; +} + +#[derive(Debug)] +pub struct Diagnostic { + pub at: TextRange, + pub message: String, +} + +impl Diagnostic { + pub fn new(at: TextRange, message: String) -> Self { + Self { at, message } } } + +pub trait Metadata { + fn name(&self) -> &str; + fn note(&self) -> &str; + fn match_with(&self, with: &SyntaxKind) -> bool; +} + +pub trait Lint: Metadata + Rule + Send + Sync {} + +// #[macro_export] +// macro_rules! lint_map { +// ($($s:ident),*,) => { +// lint_map($($s),*); +// } +// ($($s:ident),*) => { +// use ::std::collections::HashMap; +// use rnix::SyntaxKind; +// $( +// mod $s; +// )* +// ::lazy_static::lazy_static! { +// pub static ref RULES: HashMap> = { +// vec![$(&*$s::LINT,)*] +// } +// } +// } +// } 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 @@ +pub mod bool_comparison; +pub 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 @@ +use crate::{Diagnostic, Lint, Metadata, Rule}; + +use if_chain::if_chain; +use macros::lint; +use rnix::{ + types::{BinOp, BinOpKind, Ident, TokenWrapper, TypedNode}, + NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, +}; + +#[lint( + name = "bool_comparison", + note = "Unnecessary comparison with boolean", + match_with = "SyntaxKind::NODE_BIN_OP" +)] +struct BoolComparison; + +impl Rule for BoolComparison { + fn validate(&self, node: &SyntaxElement) -> Option { + if_chain! { + if let NodeOrToken::Node(bin_op_node) = node; + if let Some(bin_expr) = BinOp::cast(bin_op_node.clone()); + if let Some(lhs) = bin_expr.lhs(); + if let Some(rhs) = bin_expr.rhs(); + + if let BinOpKind::Equal | BinOpKind::NotEqual = bin_expr.operator(); + let (non_bool_side, bool_side) = if is_boolean_ident(&lhs) { + (rhs, lhs) + } else if is_boolean_ident(&rhs) { + (lhs, rhs) + } else { + return None + }; + then { + let at = node.text_range(); + let message = format!( + "Comparing `{}` with boolean literal `{}`", + non_bool_side, + bool_side + ); + dbg!(Some(Diagnostic::new(at, message))) + } else { + None + } + } + } +} + +// not entirely accurate, underhanded nix programmers might write `true = false` +fn is_boolean_ident(node: &SyntaxNode) -> bool { + if let Some(ident_expr) = Ident::cast(node.clone()) { + ident_expr.as_str() == "true" || ident_expr.as_str() == "false" + } else { + false + } +} + +// #[cfg(test)] +// mod tests { +// use super::*; +// use rnix::{parser, WalkEvent}; +// +// #[test] +// fn trivial() { +// let src = r#" +// a == true +// "#; +// let parsed = rnix::parse(src).as_result().ok().unwrap(); +// let _ = parsed +// .node() +// .preorder_with_tokens() +// .filter_map(|event| match event { +// WalkEvent::Enter(t) => Some(t), +// _ => None, +// }) +// .map(|node| BoolComparison.validate(&node)) +// .collect::>(); +// } +// } -- cgit v1.2.3