From 2ee7db0b3b6df787c7e62d3614f97a0e45d12a12 Mon Sep 17 00:00:00 2001 From: Akshay Date: Wed, 15 Sep 2021 16:19:43 +0530 Subject: implement lint_map --- lib/src/lib.rs | 69 ++++++++++++++++++++++++++++------------ lib/src/lints.rs | 7 ++-- lib/src/lints/bool_comparison.rs | 8 ++--- macros/src/lib.rs | 56 ++++++++++++++++++++++---------- 4 files changed, 97 insertions(+), 43 deletions(-) diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 537f4b3..c06f02d 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,10 +1,11 @@ -pub mod lints; +mod lints; +pub use lints::LINTS; use rnix::{SyntaxElement, SyntaxKind, TextRange}; -use std::ops::Deref; +use std::default::Default; pub trait Rule { - fn validate(&self, node: &SyntaxElement) -> Option; + fn validate(&self, node: &SyntaxElement) -> Option; } #[derive(Debug)] @@ -19,29 +20,55 @@ impl Diagnostic { } } +#[derive(Debug, Default)] +pub struct Report { + pub diagnostics: Vec, +} + +impl Report { + pub fn new() -> Self { + Self::default() + } + pub fn diagnostic(mut self, at: TextRange, message: String) -> Self { + self.diagnostics.push(Diagnostic::new(at, message)); + self + } +} + pub trait Metadata { fn name(&self) -> &str; fn note(&self) -> &str; fn match_with(&self, with: &SyntaxKind) -> bool; + fn match_kind(&self) -> SyntaxKind; } 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,)*] -// } -// } -// } -// } +#[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 LINTS: HashMap>> = { + let mut map = HashMap::new(); + $( + { + let temp_lint = &*$s::LINT; + let temp_match = temp_lint.match_kind(); + map.entry(temp_match) + .and_modify(|v: &mut Vec<_>| v.push(temp_lint)) + .or_insert_with(|| vec![temp_lint]); + } + )* + map + }; + } + } +} diff --git a/lib/src/lints.rs b/lib/src/lints.rs index b0df71b..15d9063 100644 --- a/lib/src/lints.rs +++ b/lib/src/lints.rs @@ -1,2 +1,5 @@ -pub mod bool_comparison; -pub mod with_list; +use crate::lint_map; + +lint_map! { + bool_comparison, +} diff --git a/lib/src/lints/bool_comparison.rs b/lib/src/lints/bool_comparison.rs index 4476b31..918126f 100644 --- a/lib/src/lints/bool_comparison.rs +++ b/lib/src/lints/bool_comparison.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, Lint, Metadata, Rule}; +use crate::{Lint, Metadata, Report, Rule}; use if_chain::if_chain; use macros::lint; @@ -10,12 +10,12 @@ use rnix::{ #[lint( name = "bool_comparison", note = "Unnecessary comparison with boolean", - match_with = "SyntaxKind::NODE_BIN_OP" + match_with = SyntaxKind::NODE_BIN_OP )] struct BoolComparison; impl Rule for BoolComparison { - fn validate(&self, node: &SyntaxElement) -> Option { + 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()); @@ -37,7 +37,7 @@ impl Rule for BoolComparison { non_bool_side, bool_side ); - dbg!(Some(Diagnostic::new(at, message))) + Some(Report::new().diagnostic(at, message)) } else { None } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index a420d56..42f7565 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -8,13 +8,13 @@ use syn::{ parse::{Parse, ParseStream, Result as ParseResult}, parse_macro_input, punctuated::Punctuated, - Ident, ItemStruct, Lit, Path, Token, + Expr, Ident, ItemStruct, Lit, Token, }; struct KeyValue { key: Ident, _eq: Token![=], - value: Lit, + value: Expr, } impl Parse for KeyValue { @@ -27,7 +27,7 @@ impl Parse for KeyValue { } } -struct LintMeta(HashMap); +struct LintMeta(HashMap); impl Parse for LintMeta { fn parse(input: ParseStream) -> ParseResult { @@ -54,11 +54,13 @@ fn generate_meta_impl(struct_name: &Ident, meta: &LintMeta) -> TokenStream2 { let name_fn = generate_name_fn(meta); let note_fn = generate_note_fn(meta); let match_with_fn = generate_match_with_fn(meta); + let match_kind = generate_match_kind(meta); quote! { impl Metadata for #struct_name { #name_fn #note_fn #match_with_fn + #match_kind } } } @@ -68,11 +70,16 @@ fn generate_name_fn(meta: &LintMeta) -> TokenStream2 { .0 .get(&format_ident!("name")) .unwrap_or_else(|| panic!("`name` not present")); - quote! { - fn name(&self) -> &str { - #name + if let syn::Expr::Lit(name_lit) = name { + if let Lit::Str(name_str) = &name_lit.lit { + return quote! { + fn name(&self) -> &str { + #name_str + } + }; } } + panic!("Invalid value for `name`"); } fn generate_note_fn(meta: &LintMeta) -> TokenStream2 { @@ -80,11 +87,16 @@ fn generate_note_fn(meta: &LintMeta) -> TokenStream2 { .0 .get(&format_ident!("note")) .unwrap_or_else(|| panic!("`note` not present")); - quote! { - fn note(&self) -> &str { - #note + if let syn::Expr::Lit(note_lit) = note { + if let Lit::Str(note_str) = ¬e_lit.lit { + return quote! { + fn note(&self) -> &str { + #note_str + } + }; } } + panic!("Invalid value for `note`"); } fn generate_match_with_fn(meta: &LintMeta) -> TokenStream2 { @@ -92,18 +104,30 @@ fn generate_match_with_fn(meta: &LintMeta) -> TokenStream2 { .0 .get(&format_ident!("match_with")) .unwrap_or_else(|| panic!("`match_with` not present")); - if let Lit::Str(match_with) = match_with_lit { - let path: Path = match_with - .parse() - .ok() - .unwrap_or_else(|| panic!("`match_with` does not contain valid path")); + if let syn::Expr::Path(match_path) = match_with_lit { quote! { fn match_with(&self, with: &SyntaxKind) -> bool { - *with == #path + #match_path == *with + } + } + } else { + panic!("`match_with` has non-path value") + } +} + +fn generate_match_kind(meta: &LintMeta) -> TokenStream2 { + let match_with_lit = meta + .0 + .get(&format_ident!("match_with")) + .unwrap_or_else(|| panic!("`match_with` not present")); + if let syn::Expr::Path(match_path) = match_with_lit { + quote! { + fn match_kind(&self) -> SyntaxKind { + #match_path } } } else { - panic!("`match_with` has non-literal value") + panic!("`match_with` has non-path value") } } -- cgit v1.2.3