diff options
-rw-r--r-- | lib/src/lib.rs | 69 | ||||
-rw-r--r-- | lib/src/lints.rs | 7 | ||||
-rw-r--r-- | lib/src/lints/bool_comparison.rs | 8 | ||||
-rw-r--r-- | 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 @@ | |||
1 | pub mod lints; | 1 | mod lints; |
2 | pub use lints::LINTS; | ||
2 | 3 | ||
3 | use rnix::{SyntaxElement, SyntaxKind, TextRange}; | 4 | use rnix::{SyntaxElement, SyntaxKind, TextRange}; |
4 | use std::ops::Deref; | 5 | use std::default::Default; |
5 | 6 | ||
6 | pub trait Rule { | 7 | pub 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)] | ||
24 | pub struct Report { | ||
25 | pub diagnostics: Vec<Diagnostic>, | ||
26 | } | ||
27 | |||
28 | impl 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 | |||
22 | pub trait Metadata { | 38 | pub 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 | ||
28 | pub trait Lint: Metadata + Rule + Send + Sync {} | 45 | pub trait Lint: Metadata + Rule + Send + Sync {} |
29 | 46 | ||
30 | // #[macro_export] | 47 | #[macro_export] |
31 | // macro_rules! lint_map { | 48 | macro_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 @@ | |||
1 | pub mod bool_comparison; | 1 | use crate::lint_map; |
2 | pub mod with_list; | 2 | |
3 | lint_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 @@ | |||
1 | use crate::{Diagnostic, Lint, Metadata, Rule}; | 1 | use crate::{Lint, Metadata, Report, Rule}; |
2 | 2 | ||
3 | use if_chain::if_chain; | 3 | use if_chain::if_chain; |
4 | use macros::lint; | 4 | use 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 | )] |
15 | struct BoolComparison; | 15 | struct BoolComparison; |
16 | 16 | ||
17 | impl Rule for BoolComparison { | 17 | impl 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 | ||
14 | struct KeyValue { | 14 | struct KeyValue { |
15 | key: Ident, | 15 | key: Ident, |
16 | _eq: Token![=], | 16 | _eq: Token![=], |
17 | value: Lit, | 17 | value: Expr, |
18 | } | 18 | } |
19 | 19 | ||
20 | impl Parse for KeyValue { | 20 | impl Parse for KeyValue { |
@@ -27,7 +27,7 @@ impl Parse for KeyValue { | |||
27 | } | 27 | } |
28 | } | 28 | } |
29 | 29 | ||
30 | struct LintMeta(HashMap<Ident, Lit>); | 30 | struct LintMeta(HashMap<Ident, Expr>); |
31 | 31 | ||
32 | impl Parse for LintMeta { | 32 | impl 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 | ||
78 | fn generate_note_fn(meta: &LintMeta) -> TokenStream2 { | 85 | fn 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) = ¬e_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 | ||
90 | fn generate_match_with_fn(meta: &LintMeta) -> TokenStream2 { | 102 | fn 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 | |||
118 | fn 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 | ||