diff options
Diffstat (limited to 'macros/src/lib.rs')
-rw-r--r-- | macros/src/lib.rs | 173 |
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 @@ | |||
1 | use std::collections::HashMap; | 1 | mod explain; |
2 | mod metadata; | ||
2 | 3 | ||
4 | use explain::generate_explain_impl; | ||
5 | use metadata::{generate_meta_impl, RawLintMeta}; | ||
3 | use proc_macro::TokenStream; | 6 | use proc_macro::TokenStream; |
4 | use proc_macro2::TokenStream as TokenStream2; | 7 | use proc_macro2::TokenStream as TokenStream2; |
5 | 8 | use quote::quote; | |
6 | use quote::{format_ident, quote}; | 9 | use syn::{parse_macro_input, Ident, ItemStruct}; |
7 | use syn::{ | ||
8 | parse::{Parse, ParseStream, Result as ParseResult}, | ||
9 | parse_macro_input, | ||
10 | punctuated::Punctuated, | ||
11 | Expr, Ident, ItemStruct, Lit, Token, | ||
12 | }; | ||
13 | |||
14 | struct KeyValue { | ||
15 | key: Ident, | ||
16 | _eq: Token![=], | ||
17 | value: Expr, | ||
18 | } | ||
19 | |||
20 | impl 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 | |||
30 | struct LintMeta(HashMap<Ident, Expr>); | ||
31 | |||
32 | impl 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 | ||
43 | fn generate_self_impl(struct_name: &Ident) -> TokenStream2 { | 11 | fn 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 | ||
53 | fn 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 | |||
72 | fn 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 | |||
89 | fn 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) = ¬e_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 | |||
106 | fn 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 | |||
123 | fn generate_report_fn() -> TokenStream2 { | ||
124 | quote! { | ||
125 | fn report() -> Report { | ||
126 | Report::new(Self::note(), Self::code()) | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | fn 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 | |||
153 | fn 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] |
176 | pub fn lint(attr: TokenStream, item: TokenStream) -> TokenStream { | 22 | pub 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 | } |