1use proc_macro2::TokenTree;
2use syn::{parse_quote, Attribute};
3
4use crate::{Argument, Method, Params};
5
6use super::{InputData, ReceiverStyle};
7impl InputData {
8 pub fn parse_inherent_impl(item: &mut syn::ItemImpl, params: Params) -> syn::Result<InputData> {
9 let returnval_mode = params.returnval.is_some();
10
11 if let Some(x) = item.defaultness {
12 return Err(syn::Error::new_spanned(x, "Default impls not supported"));
13 }
14 if let Some(x) = item.unsafety {
15 return Err(syn::Error::new_spanned(
16 x,
17 "Handling `unsafe` is not implemented",
18 ));
19 }
20 if let Some((_, path, _)) = &item.trait_ {
21 return Err(syn::Error::new_spanned(
22 path,
23 "Trait impls are not supported, only inherent impls",
24 ));
25 }
26 let generics = item.generics.clone();
27 let (name, struct_args) = match &*item.self_ty {
28 syn::Type::Path(p) => {
29 if p.qself.is_some() {
30 return Err(syn::Error::new_spanned(
31 p,
32 "Impl has some tricky type. This is not supported",
33 ));
34 }
35 if p.path.segments.len() != 1 {
36 return Err(syn::Error::new_spanned(
37 p,
38 "Impl type must be a single ident with optional arguments",
39 ));
40 }
41 let segment = p.path.segments[0].clone();
42 (segment.ident, segment.arguments)
43 }
44 _ => return Err(syn::Error::new_spanned(
45 &*item.self_ty,
46 "Type for `impl` should be a simple identifier without any paths or other tricks.",
47 )),
48 };
49
50 let mut methods = Vec::with_capacity(item.items.len());
51
52 for item in &mut item.items {
53 if let syn::ImplItem::Method(method) = item {
54 if method.defaultness.is_some() {
55 panic!("`default` not supported");
56 }
57
58 methods.push(parse_method(
59 &mut method.sig,
60 &mut method.attrs,
61 returnval_mode,
62 ¶ms.context,
63 )?);
64 }
65 }
66
67 Ok(InputData {
68 name,
69 generics,
70 struct_args,
71 methods,
72 params,
73 })
74 }
75}
76
77fn parse_method(
78 method_signature: &mut syn::Signature,
79 attrs: &mut Vec<syn::Attribute>,
80 returnval_mode: bool,
81 context: &Option<(syn::Ident, syn::Type)>,
82) -> syn::Result<Method> {
83 let mut enum_attr = vec![];
84 let mut return_attr = vec![];
85 let mut doc_attr = vec![];
86 let r#async = method_signature.asyncness.is_some();
87 if let Some(x) = method_signature.constness {
88 return Err(syn::Error::new_spanned(x, "ctrlgen does not support const"));
89 }
90 if let Some(x) = method_signature.unsafety {
91 return Err(syn::Error::new_spanned(
92 x,
93 "ctrlgen does not support unsafe",
94 ));
95 }
96 if let Some(x) = &method_signature.abi {
97 return Err(syn::Error::new_spanned(
98 x,
99 "ctrlgen does not support custom ABI in trait methods",
100 ));
101 }
102 if !method_signature.generics.params.is_empty() {
103 return Err(syn::Error::new_spanned(
104 &method_signature.generics,
105 "ctrlgen does not support generics or lifetimes in trait methods",
106 ));
107 }
108 if let Some(x) = &method_signature.variadic {
109 return Err(syn::Error::new_spanned(
110 x,
111 "ctrlgen does not support variadics",
112 ));
113 }
114 if !returnval_mode && !matches!(method_signature.output, syn::ReturnType::Default) {
115 return Err(syn::Error::new_spanned(
116 &method_signature.output,
117 "Specify `returnval` parameter to ctrlgen macro to handle methods with return types.",
118 ));
119 }
120 for a in attrs.iter() {
121 match a.path.get_ident() {
122 Some(x) if x == "ctrlgen_enum_attr" || x == "ctrlgen_return_attr" => {
123 let g = match a.tokens.clone().into_iter().next() {
124 Some(TokenTree::Group(g)) => g,
125 _ => {
126 return Err(syn::Error::new_spanned(
127 a,
128 "Input of `ctrlgen_{{enum|return}}_attr` should be single [...] group",
129 ));
130 }
131 };
132 let attr: Attribute = parse_quote! { # #g };
133 match x {
134 x if x == "ctrlgen_enum_attr" => enum_attr.push(attr),
135 x if x == "ctrlgen_return_attr" => return_attr.push(attr),
136 _ => unreachable!(),
137 }
138 }
139 Some(x) if x == "doc" => {
140 doc_attr.push(a.clone());
141 }
142 _ => (),
143 }
144 }
145 attrs.retain(|a| !matches!(a.path.get_ident(), Some(x) if x == "ctrlgen_enum_attr" || x == "ctrlgen_return_attr"));
146 let mut args = Vec::with_capacity(method_signature.inputs.len());
147 let mut receiver_style = None;
148 let ret = match &method_signature.output {
149 syn::ReturnType::Default => None,
150 syn::ReturnType::Type(_, t) => Some(*t.clone()),
151 };
152 let mut is_first_arg = true;
153 for input_args in &mut method_signature.inputs {
154 match input_args {
155 syn::FnArg::Receiver(r) => {
156 receiver_style = if let Some(rr) = &r.reference {
157 if let Some(x) = &rr.1 {
158 return Err(syn::Error::new_spanned(
159 x,
160 "ctrlgen does not support explicit lifetimes",
161 ));
162 }
163 if r.mutability.is_some() {
164 Some(ReceiverStyle::Mut)
165 } else {
166 Some(ReceiverStyle::Ref)
167 }
168 } else {
169 Some(ReceiverStyle::Move)
170 }
171 }
172 syn::FnArg::Typed(arg) => {
173 if is_first_arg {
174 is_first_arg = false;
175 if let Some((ctx_name, _ctx_ty)) = context {
176 if let syn::Pat::Ident(pat) = &*arg.pat {
177 let arg_name = &pat.ident;
178 if *arg_name != ctx_name.to_string() {
179 return Err(syn::Error::new_spanned(&arg, format!("Expected first argument to be the context {ctx_name}. Got {arg_name}")));
180 }
181 }
182 continue;
184 }
185 }
186
187 let mut enum_attr = vec![];
188 let mut to_owned = false;
189 for a in attrs.iter() {
190 match a.path.get_ident() {
191 Some(x) if x == "ctrlgen_enum_attr" => {
192 match a.tokens.clone().into_iter().next() {
193 Some(TokenTree::Group(g)) => {
194 enum_attr.push(g);
195 }
196 _ => return Err(syn::Error::new_spanned(
197 a,
198 "Input of `ctrlgen_enum_attr` should be a single [...] group",
199 )),
200 }
201 }
202 Some(x) if x == "ctrlgen_to_owned" => {
203 if !a.tokens.is_empty() {
204 return Err(syn::Error::new_spanned(
205 a,
206 "`ctrlgen_to_owned` does not accept any additional arguments",
207 ));
208 }
209 to_owned = true;
210 }
211 _ => (),
212 }
213 }
214 arg.attrs.retain(|a| match a.path.get_ident() {
215 Some(x) if x == "ctrlgen_enum_attr" => false,
216 Some(x) if x == "ctrlgen_to_owned" => false,
217 _ => true,
218 });
219 match &*arg.pat {
220 syn::Pat::Ident(pi) => {
221 if pi.by_ref.is_some() {
222 return Err(syn::Error::new_spanned(pi, "ctrlgen does not support `ref` in argument names"));
223 }
224 if returnval_mode && pi.ident == "ret" {
225 return Err(syn::Error::new_spanned(&pi.ident, format!("In `returnval` mode, method's arguments cannot be named literally `ret`. Rename it away in `{}`.", method_signature.ident)));
226 }
227 args.push(Argument { name: pi.ident.clone(), ty: *arg.ty.clone(), enum_attr, to_owned });
228 }
229 _ => return Err(syn::Error::new_spanned(arg, "ctrlgen does not support method arguments that are patterns, not just simple identifiers")),
230 }
231 }
232 }
233 }
234 if receiver_style.is_none() {
235 return Err(syn::Error::new_spanned(
236 method_signature,
237 "ctrlgen does not support methods that do not accept `self`",
238 ));
239 }
240 if is_first_arg && context.is_some() {
241 return Err(syn::Error::new_spanned(
242 &method_signature,
243 "Every method needs to take the context arg when specified",
244 ));
245 }
246 Ok(Method {
247 args,
248 name: method_signature.ident.clone(),
249 receiver_style: receiver_style.unwrap(),
250 ret,
251 enum_attr,
252 return_attr,
253 doc_attr,
254 r#async,
255 })
256}