aboutsummaryrefslogtreecommitdiff
path: root/macros/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'macros/src/lib.rs')
-rw-r--r--macros/src/lib.rs173
1 files changed, 11 insertions, 162 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 127b4cb..86fa509 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -1,44 +1,12 @@
1use std::collections::HashMap; 1mod explain;
2mod metadata;
2 3
4use explain::generate_explain_impl;
5use metadata::{generate_meta_impl, RawLintMeta};
3use proc_macro::TokenStream; 6use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2; 7use proc_macro2::TokenStream as TokenStream2;
5 8use quote::quote;
6use quote::{format_ident, quote}; 9use syn::{parse_macro_input, Ident, ItemStruct};
7use syn::{
8 parse::{Parse, ParseStream, Result as ParseResult},
9 parse_macro_input,
10 punctuated::Punctuated,
11 Expr, Ident, ItemStruct, Lit, Token,
12};
13
14struct KeyValue {
15 key: Ident,
16 _eq: Token![=],
17 value: Expr,
18}
19
20impl Parse for KeyValue {
21 fn parse(input: ParseStream) -> ParseResult<Self> {
22 Ok(Self {
23 key: input.parse()?,
24 _eq: input.parse()?,
25 value: input.parse()?,
26 })
27 }
28}
29
30struct LintMeta(HashMap<Ident, Expr>);
31
32impl Parse for LintMeta {
33 fn parse(input: ParseStream) -> ParseResult<Self> {
34 Ok(Self(
35 Punctuated::<KeyValue, Token![,]>::parse_terminated(input)?
36 .into_iter()
37 .map(|item| (item.key, item.value))
38 .collect(),
39 ))
40 }
41}
42 10
43fn generate_self_impl(struct_name: &Ident) -> TokenStream2 { 11fn generate_self_impl(struct_name: &Ident) -> TokenStream2 {
44 quote! { 12 quote! {
@@ -50,136 +18,16 @@ fn generate_self_impl(struct_name: &Ident) -> TokenStream2 {
50 } 18 }
51} 19}
52 20
53fn generate_meta_impl(struct_name: &Ident, meta: &LintMeta) -> TokenStream2 {
54 let name_fn = generate_name_fn(meta);
55 let note_fn = generate_note_fn(meta);
56 let code_fn = generate_code_fn(meta);
57 let report_fn = generate_report_fn();
58 let match_with_fn = generate_match_with_fn(meta);
59 let match_kind = generate_match_kind(meta);
60 quote! {
61 impl Metadata for #struct_name {
62 #name_fn
63 #note_fn
64 #code_fn
65 #report_fn
66 #match_with_fn
67 #match_kind
68 }
69 }
70}
71
72fn generate_name_fn(meta: &LintMeta) -> TokenStream2 {
73 let name = meta
74 .0
75 .get(&format_ident!("name"))
76 .unwrap_or_else(|| panic!("`name` not present"));
77 if let syn::Expr::Lit(name_lit) = name {
78 if let Lit::Str(name_str) = &name_lit.lit {
79 return quote! {
80 fn name() -> &'static str {
81 #name_str
82 }
83 };
84 }
85 }
86 panic!("Invalid value for `name`");
87}
88
89fn generate_note_fn(meta: &LintMeta) -> TokenStream2 {
90 let note = meta
91 .0
92 .get(&format_ident!("note"))
93 .unwrap_or_else(|| panic!("`note` not present"));
94 if let syn::Expr::Lit(note_lit) = note {
95 if let Lit::Str(note_str) = &note_lit.lit {
96 return quote! {
97 fn note() -> &'static str {
98 #note_str
99 }
100 };
101 }
102 }
103 panic!("Invalid value for `note`");
104}
105
106fn generate_code_fn(meta: &LintMeta) -> TokenStream2 {
107 let code = meta
108 .0
109 .get(&format_ident!("code"))
110 .unwrap_or_else(|| panic!("`code` not present"));
111 if let syn::Expr::Lit(code_lit) = code {
112 if let Lit::Int(code_int) = &code_lit.lit {
113 return quote! {
114 fn code() -> u32 {
115 #code_int
116 }
117 };
118 }
119 }
120 panic!("Invalid value for `note`");
121}
122
123fn generate_report_fn() -> TokenStream2 {
124 quote! {
125 fn report() -> Report {
126 Report::new(Self::note(), Self::code())
127 }
128 }
129}
130
131fn generate_match_with_fn(meta: &LintMeta) -> TokenStream2 {
132 let match_with_lit = meta
133 .0
134 .get(&format_ident!("match_with"))
135 .unwrap_or_else(|| panic!("`match_with` not present"));
136 if let syn::Expr::Path(match_path) = match_with_lit {
137 quote! {
138 fn match_with(&self, with: &SyntaxKind) -> bool {
139 #match_path == *with
140 }
141 }
142 } else if let syn::Expr::Array(array_expr) = match_with_lit {
143 quote! {
144 fn match_with(&self, with: &SyntaxKind) -> bool {
145 #array_expr.contains(with)
146 }
147 }
148 } else {
149 panic!("`match_with` has non-path value")
150 }
151}
152
153fn generate_match_kind(meta: &LintMeta) -> TokenStream2 {
154 let match_with_lit = meta
155 .0
156 .get(&format_ident!("match_with"))
157 .unwrap_or_else(|| panic!("`match_with` not present"));
158 if let syn::Expr::Path(match_path) = match_with_lit {
159 quote! {
160 fn match_kind(&self) -> Vec<SyntaxKind> {
161 vec![#match_path]
162 }
163 }
164 } else if let syn::Expr::Array(array_expr) = match_with_lit {
165 quote! {
166 fn match_kind(&self) -> Vec<SyntaxKind> {
167 #array_expr.to_vec()
168 }
169 }
170 } else {
171 panic!("`match_with` has non-path value")
172 }
173}
174
175#[proc_macro_attribute] 21#[proc_macro_attribute]
176pub fn lint(attr: TokenStream, item: TokenStream) -> TokenStream { 22pub fn lint(attr: TokenStream, item: TokenStream) -> TokenStream {
177 let struct_item = parse_macro_input!(item as ItemStruct); 23 let struct_item = parse_macro_input!(item as ItemStruct);
178 let meta = parse_macro_input!(attr as LintMeta); 24 let meta = parse_macro_input!(attr as RawLintMeta);
179 25
180 let struct_name = &struct_item.ident; 26 let struct_name = &struct_item.ident;
181 let self_impl = generate_self_impl(struct_name); 27 let self_impl = generate_self_impl(struct_name);
182 let meta_impl = generate_meta_impl(struct_name, &meta); 28 let meta_impl = generate_meta_impl(struct_name, &meta);
29 let explain_impl = generate_explain_impl(&struct_item);
30
183 (quote! { 31 (quote! {
184 #struct_item 32 #struct_item
185 33
@@ -189,8 +37,9 @@ pub fn lint(attr: TokenStream, item: TokenStream) -> TokenStream {
189 37
190 #self_impl 38 #self_impl
191 #meta_impl 39 #meta_impl
40 #explain_impl
192 41
193 impl Lint for #struct_name {} 42 impl crate::Lint for #struct_name {}
194 }) 43 })
195 .into() 44 .into()
196} 45}