aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/src/lib.rs69
-rw-r--r--lib/src/lints.rs7
-rw-r--r--lib/src/lints/bool_comparison.rs8
-rw-r--r--macros/src/lib.rs56
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 @@
1pub mod lints; 1mod lints;
2pub use lints::LINTS;
2 3
3use rnix::{SyntaxElement, SyntaxKind, TextRange}; 4use rnix::{SyntaxElement, SyntaxKind, TextRange};
4use std::ops::Deref; 5use std::default::Default;
5 6
6pub trait Rule { 7pub trait Rule {
7 fn validate(&self, node: &SyntaxElement) -> Option<Diagnostic>; 8 fn validate(&self, node: &SyntaxElement) -> Option<Report>;
8} 9}
9 10
10#[derive(Debug)] 11#[derive(Debug)]
@@ -19,29 +20,55 @@ impl Diagnostic {
19 } 20 }
20} 21}
21 22
23#[derive(Debug, Default)]
24pub struct Report {
25 pub diagnostics: Vec<Diagnostic>,
26}
27
28impl Report {
29 pub fn new() -> Self {
30 Self::default()
31 }
32 pub fn diagnostic(mut self, at: TextRange, message: String) -> Self {
33 self.diagnostics.push(Diagnostic::new(at, message));
34 self
35 }
36}
37
22pub trait Metadata { 38pub trait Metadata {
23 fn name(&self) -> &str; 39 fn name(&self) -> &str;
24 fn note(&self) -> &str; 40 fn note(&self) -> &str;
25 fn match_with(&self, with: &SyntaxKind) -> bool; 41 fn match_with(&self, with: &SyntaxKind) -> bool;
42 fn match_kind(&self) -> SyntaxKind;
26} 43}
27 44
28pub trait Lint: Metadata + Rule + Send + Sync {} 45pub trait Lint: Metadata + Rule + Send + Sync {}
29 46
30// #[macro_export] 47#[macro_export]
31// macro_rules! lint_map { 48macro_rules! lint_map {
32// ($($s:ident),*,) => { 49 ($($s:ident),*,) => {
33// lint_map($($s),*); 50 lint_map!($($s),*);
34// } 51 };
35// ($($s:ident),*) => { 52 ($($s:ident),*) => {
36// use ::std::collections::HashMap; 53 use ::std::collections::HashMap;
37// use rnix::SyntaxKind; 54 use ::rnix::SyntaxKind;
38// $( 55 $(
39// mod $s; 56 mod $s;
40// )* 57 )*
41// ::lazy_static::lazy_static! { 58 ::lazy_static::lazy_static! {
42// pub static ref RULES: HashMap<SyntaxKind, &'static Box<dyn $crate::Lint>> = { 59 pub static ref LINTS: HashMap<SyntaxKind, Vec<&'static Box<dyn $crate::Lint>>> = {
43// vec![$(&*$s::LINT,)*] 60 let mut map = HashMap::new();
44// } 61 $(
45// } 62 {
46// } 63 let temp_lint = &*$s::LINT;
47// } 64 let temp_match = temp_lint.match_kind();
65 map.entry(temp_match)
66 .and_modify(|v: &mut Vec<_>| v.push(temp_lint))
67 .or_insert_with(|| vec![temp_lint]);
68 }
69 )*
70 map
71 };
72 }
73 }
74}
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 @@
1pub mod bool_comparison; 1use crate::lint_map;
2pub mod with_list; 2
3lint_map! {
4 bool_comparison,
5}
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 @@
1use crate::{Diagnostic, Lint, Metadata, Rule}; 1use crate::{Lint, Metadata, Report, Rule};
2 2
3use if_chain::if_chain; 3use if_chain::if_chain;
4use macros::lint; 4use macros::lint;
@@ -10,12 +10,12 @@ use rnix::{
10#[lint( 10#[lint(
11 name = "bool_comparison", 11 name = "bool_comparison",
12 note = "Unnecessary comparison with boolean", 12 note = "Unnecessary comparison with boolean",
13 match_with = "SyntaxKind::NODE_BIN_OP" 13 match_with = SyntaxKind::NODE_BIN_OP
14)] 14)]
15struct BoolComparison; 15struct BoolComparison;
16 16
17impl Rule for BoolComparison { 17impl Rule for BoolComparison {
18 fn validate(&self, node: &SyntaxElement) -> Option<Diagnostic> { 18 fn validate(&self, node: &SyntaxElement) -> Option<Report> {
19 if_chain! { 19 if_chain! {
20 if let NodeOrToken::Node(bin_op_node) = node; 20 if let NodeOrToken::Node(bin_op_node) = node;
21 if let Some(bin_expr) = BinOp::cast(bin_op_node.clone()); 21 if let Some(bin_expr) = BinOp::cast(bin_op_node.clone());
@@ -37,7 +37,7 @@ impl Rule for BoolComparison {
37 non_bool_side, 37 non_bool_side,
38 bool_side 38 bool_side
39 ); 39 );
40 dbg!(Some(Diagnostic::new(at, message))) 40 Some(Report::new().diagnostic(at, message))
41 } else { 41 } else {
42 None 42 None
43 } 43 }
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::{
8 parse::{Parse, ParseStream, Result as ParseResult}, 8 parse::{Parse, ParseStream, Result as ParseResult},
9 parse_macro_input, 9 parse_macro_input,
10 punctuated::Punctuated, 10 punctuated::Punctuated,
11 Ident, ItemStruct, Lit, Path, Token, 11 Expr, Ident, ItemStruct, Lit, Token,
12}; 12};
13 13
14struct KeyValue { 14struct KeyValue {
15 key: Ident, 15 key: Ident,
16 _eq: Token![=], 16 _eq: Token![=],
17 value: Lit, 17 value: Expr,
18} 18}
19 19
20impl Parse for KeyValue { 20impl Parse for KeyValue {
@@ -27,7 +27,7 @@ impl Parse for KeyValue {
27 } 27 }
28} 28}
29 29
30struct LintMeta(HashMap<Ident, Lit>); 30struct LintMeta(HashMap<Ident, Expr>);
31 31
32impl Parse for LintMeta { 32impl Parse for LintMeta {
33 fn parse(input: ParseStream) -> ParseResult<Self> { 33 fn parse(input: ParseStream) -> ParseResult<Self> {
@@ -54,11 +54,13 @@ fn generate_meta_impl(struct_name: &Ident, meta: &LintMeta) -> TokenStream2 {
54 let name_fn = generate_name_fn(meta); 54 let name_fn = generate_name_fn(meta);
55 let note_fn = generate_note_fn(meta); 55 let note_fn = generate_note_fn(meta);
56 let match_with_fn = generate_match_with_fn(meta); 56 let match_with_fn = generate_match_with_fn(meta);
57 let match_kind = generate_match_kind(meta);
57 quote! { 58 quote! {
58 impl Metadata for #struct_name { 59 impl Metadata for #struct_name {
59 #name_fn 60 #name_fn
60 #note_fn 61 #note_fn
61 #match_with_fn 62 #match_with_fn
63 #match_kind
62 } 64 }
63 } 65 }
64} 66}
@@ -68,11 +70,16 @@ fn generate_name_fn(meta: &LintMeta) -> TokenStream2 {
68 .0 70 .0
69 .get(&format_ident!("name")) 71 .get(&format_ident!("name"))
70 .unwrap_or_else(|| panic!("`name` not present")); 72 .unwrap_or_else(|| panic!("`name` not present"));
71 quote! { 73 if let syn::Expr::Lit(name_lit) = name {
72 fn name(&self) -> &str { 74 if let Lit::Str(name_str) = &name_lit.lit {
73 #name 75 return quote! {
76 fn name(&self) -> &str {
77 #name_str
78 }
79 };
74 } 80 }
75 } 81 }
82 panic!("Invalid value for `name`");
76} 83}
77 84
78fn generate_note_fn(meta: &LintMeta) -> TokenStream2 { 85fn generate_note_fn(meta: &LintMeta) -> TokenStream2 {
@@ -80,11 +87,16 @@ fn generate_note_fn(meta: &LintMeta) -> TokenStream2 {
80 .0 87 .0
81 .get(&format_ident!("note")) 88 .get(&format_ident!("note"))
82 .unwrap_or_else(|| panic!("`note` not present")); 89 .unwrap_or_else(|| panic!("`note` not present"));
83 quote! { 90 if let syn::Expr::Lit(note_lit) = note {
84 fn note(&self) -> &str { 91 if let Lit::Str(note_str) = &note_lit.lit {
85 #note 92 return quote! {
93 fn note(&self) -> &str {
94 #note_str
95 }
96 };
86 } 97 }
87 } 98 }
99 panic!("Invalid value for `note`");
88} 100}
89 101
90fn generate_match_with_fn(meta: &LintMeta) -> TokenStream2 { 102fn generate_match_with_fn(meta: &LintMeta) -> TokenStream2 {
@@ -92,18 +104,30 @@ fn generate_match_with_fn(meta: &LintMeta) -> TokenStream2 {
92 .0 104 .0
93 .get(&format_ident!("match_with")) 105 .get(&format_ident!("match_with"))
94 .unwrap_or_else(|| panic!("`match_with` not present")); 106 .unwrap_or_else(|| panic!("`match_with` not present"));
95 if let Lit::Str(match_with) = match_with_lit { 107 if let syn::Expr::Path(match_path) = match_with_lit {
96 let path: Path = match_with
97 .parse()
98 .ok()
99 .unwrap_or_else(|| panic!("`match_with` does not contain valid path"));
100 quote! { 108 quote! {
101 fn match_with(&self, with: &SyntaxKind) -> bool { 109 fn match_with(&self, with: &SyntaxKind) -> bool {
102 *with == #path 110 #match_path == *with
111 }
112 }
113 } else {
114 panic!("`match_with` has non-path value")
115 }
116}
117
118fn generate_match_kind(meta: &LintMeta) -> TokenStream2 {
119 let match_with_lit = meta
120 .0
121 .get(&format_ident!("match_with"))
122 .unwrap_or_else(|| panic!("`match_with` not present"));
123 if let syn::Expr::Path(match_path) = match_with_lit {
124 quote! {
125 fn match_kind(&self) -> SyntaxKind {
126 #match_path
103 } 127 }
104 } 128 }
105 } else { 129 } else {
106 panic!("`match_with` has non-literal value") 130 panic!("`match_with` has non-path value")
107 } 131 }
108} 132}
109 133