aboutsummaryrefslogtreecommitdiff
path: root/macros/src/metadata.rs
diff options
context:
space:
mode:
Diffstat (limited to 'macros/src/metadata.rs')
-rw-r--r--macros/src/metadata.rs177
1 files changed, 177 insertions, 0 deletions
diff --git a/macros/src/metadata.rs b/macros/src/metadata.rs
new file mode 100644
index 0000000..b41eb9c
--- /dev/null
+++ b/macros/src/metadata.rs
@@ -0,0 +1,177 @@
1use std::collections::HashMap;
2
3use proc_macro2::TokenStream as TokenStream2;
4use quote::{format_ident, quote};
5use syn::{
6 parse::{Parse, ParseStream, Result},
7 punctuated::Punctuated,
8 Expr, ExprArray, Ident, Lit, Path, Token,
9};
10
11struct KeyValue {
12 key: Ident,
13 _eq: Token![=],
14 value: Expr,
15}
16
17impl Parse for KeyValue {
18 fn parse(input: ParseStream) -> Result<Self> {
19 Ok(Self {
20 key: input.parse()?,
21 _eq: input.parse()?,
22 value: input.parse()?,
23 })
24 }
25}
26
27pub struct RawLintMeta(HashMap<Ident, Expr>);
28
29impl Parse for RawLintMeta {
30 fn parse(input: ParseStream) -> Result<Self> {
31 Ok(Self(
32 Punctuated::<KeyValue, Token![,]>::parse_terminated(input)?
33 .into_iter()
34 .map(|item| (item.key, item.value))
35 .collect(),
36 ))
37 }
38}
39
40pub struct LintMeta<'μ> {
41 name: &'μ Lit,
42 note: &'μ Lit,
43 code: &'μ Lit,
44 match_with: MatchWith<'μ>,
45}
46
47enum MatchWith<'π> {
48 Path(&'π Path),
49 Array(&'π ExprArray),
50}
51
52fn extract<'λ>(id: &str, raw: &'λ RawLintMeta) -> &'λ Expr {
53 raw.0
54 .get(&format_ident!("{}", id))
55 .unwrap_or_else(|| panic!("`{}` not present", id))
56}
57
58fn as_lit(e: &Expr) -> &Lit {
59 match e {
60 Expr::Lit(l) => &l.lit,
61 _ => panic!("expected a literal"),
62 }
63}
64
65impl<'μ> LintMeta<'μ> {
66 fn from_raw(raw: &'μ RawLintMeta) -> Self {
67 let name = as_lit(extract("name", raw));
68 let note = as_lit(extract("note", raw));
69 let code = as_lit(extract("code", raw));
70 let match_with_expr = extract("match_with", raw);
71 let match_with = match match_with_expr {
72 Expr::Path(p) => MatchWith::Path(&p.path),
73 Expr::Array(a) => MatchWith::Array(a),
74 _ => panic!("`match_with` is neither a path nor an array"),
75 };
76 Self {
77 name,
78 note,
79 code,
80 match_with,
81 }
82 }
83
84 fn generate_name_fn(&self) -> TokenStream2 {
85 let name_str = self.name;
86 return quote! {
87 fn name(&self) -> &'static str {
88 #name_str
89 }
90 };
91 }
92
93 fn generate_note_fn(&self) -> TokenStream2 {
94 let note_str = self.note;
95 return quote! {
96 fn note(&self) -> &'static str {
97 #note_str
98 }
99 };
100 }
101
102 fn generate_code_fn(&self) -> TokenStream2 {
103 let code_int = self.code;
104 return quote! {
105 fn code(&self) -> u32 {
106 #code_int
107 }
108 };
109 }
110
111 fn generate_match_with_fn(&self) -> TokenStream2 {
112 match self.match_with {
113 MatchWith::Path(p) => {
114 quote! {
115 fn match_with(&self, with: &SyntaxKind) -> bool {
116 #p == *with
117 }
118 }
119 }
120 MatchWith::Array(a) => {
121 quote! {
122 fn match_with(&self, with: &SyntaxKind) -> bool {
123 #a.contains(with)
124 }
125 }
126 }
127 }
128 }
129
130 fn generate_match_kind_fn(&self) -> TokenStream2 {
131 match self.match_with {
132 MatchWith::Path(p) => {
133 quote! {
134 fn match_kind(&self) -> Vec<SyntaxKind> {
135 vec![#p]
136 }
137 }
138 }
139 MatchWith::Array(a) => {
140 quote! {
141 fn match_kind(&self) -> Vec<SyntaxKind> {
142 #a.to_vec()
143 }
144 }
145 }
146 }
147 }
148
149 fn generate_report_fn(&self) -> TokenStream2 {
150 quote! {
151 fn report(&self) -> crate::Report {
152 crate::Report::new(self.note(), self.code())
153 }
154 }
155 }
156}
157
158pub fn generate_meta_impl(struct_name: &Ident, meta: &RawLintMeta) -> TokenStream2 {
159 let not_raw = LintMeta::from_raw(&meta);
160 let name_fn = not_raw.generate_name_fn();
161 let note_fn = not_raw.generate_note_fn();
162 let code_fn = not_raw.generate_code_fn();
163 let match_with_fn = not_raw.generate_match_with_fn();
164 let match_kind = not_raw.generate_match_kind_fn();
165 let report_fn = not_raw.generate_report_fn();
166
167 quote! {
168 impl crate::Metadata for #struct_name {
169 #name_fn
170 #note_fn
171 #code_fn
172 #match_with_fn
173 #match_kind
174 #report_fn
175 }
176 }
177}