blob: 3d35f78863c098902788e03370fc7fe2f450e7bf [file] [log] [blame]
David Tolnay7db73692019-10-20 14:51:12 -04001use crate::syntax::{
David Tolnay16448732020-03-18 12:39:36 -07002 attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Struct,
3 Ty1, Type, Var,
David Tolnay7db73692019-10-20 14:51:12 -04004};
5use proc_macro2::Ident;
David Tolnayc071b892020-03-18 16:59:53 -07006use quote::{format_ident, quote};
David Tolnay7db73692019-10-20 14:51:12 -04007use syn::{
8 Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item,
David Tolnayc071b892020-03-18 16:59:53 -07009 ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType,
10 TypeBareFn, TypePath, TypeReference,
David Tolnay7db73692019-10-20 14:51:12 -040011};
12
David Tolnay7db73692019-10-20 14:51:12 -040013pub fn parse_items(items: Vec<Item>) -> Result<Vec<Api>> {
14 let mut apis = Vec::new();
15 for item in items {
16 match item {
17 Item::Struct(item) => {
18 let strct = parse_struct(item)?;
19 apis.push(strct);
20 }
21 Item::ForeignMod(foreign_mod) => {
22 let functions = parse_foreign_mod(foreign_mod)?;
23 apis.extend(functions);
24 }
25 Item::Use(item) => return Err(Error::new_spanned(item, error::USE_NOT_ALLOWED)),
26 _ => return Err(Error::new_spanned(item, "unsupported item")),
27 }
28 }
29 Ok(apis)
30}
31
32fn parse_struct(item: ItemStruct) -> Result<Api> {
33 let generics = &item.generics;
34 if !generics.params.is_empty() || generics.where_clause.is_some() {
35 let struct_token = item.struct_token;
36 let ident = &item.ident;
37 let where_clause = &generics.where_clause;
38 let span = quote!(#struct_token #ident #generics #where_clause);
39 return Err(Error::new_spanned(
40 span,
41 "struct with generic parameters is not supported yet",
42 ));
43 }
44
45 let mut doc = Doc::new();
46 let mut derives = Vec::new();
47 attrs::parse(&item.attrs, &mut doc, Some(&mut derives))?;
48 check_reserved_name(&item.ident)?;
49 match item.fields {
50 Fields::Named(fields) => Ok(Api::Struct(Struct {
51 doc,
52 derives,
53 struct_token: item.struct_token,
54 ident: item.ident,
55 fields: fields
56 .named
57 .into_iter()
58 .map(|field| {
59 Ok(Var {
60 ident: field.ident.unwrap(),
61 ty: parse_type(&field.ty)?,
62 })
63 })
64 .collect::<Result<_>>()?,
65 })),
66 Fields::Unit => Err(Error::new_spanned(item, "unit structs are not supported")),
67 Fields::Unnamed(_) => Err(Error::new_spanned(item, "tuple structs are not supported")),
68 }
69}
70
71fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result<Vec<Api>> {
72 let lang = parse_lang(foreign_mod.abi)?;
73 let api_type = match lang {
74 Lang::Cxx => Api::CxxType,
75 Lang::Rust => Api::RustType,
76 };
77 let api_function = match lang {
78 Lang::Cxx => Api::CxxFunction,
79 Lang::Rust => Api::RustFunction,
80 };
81
82 let mut items = Vec::new();
83 for foreign in &foreign_mod.items {
84 match foreign {
85 ForeignItem::Type(foreign) => {
86 check_reserved_name(&foreign.ident)?;
87 let ety = parse_extern_type(foreign)?;
88 items.push(api_type(ety));
89 }
90 ForeignItem::Fn(foreign) => {
David Tolnay6cde49f2020-03-16 12:25:45 -070091 let efn = parse_extern_fn(foreign, lang)?;
David Tolnay7db73692019-10-20 14:51:12 -040092 items.push(api_function(efn));
93 }
94 ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => {
95 let include = foreign.mac.parse_body()?;
96 items.push(Api::Include(include));
97 }
98 _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")),
99 }
100 }
101 Ok(items)
102}
103
104fn parse_lang(abi: Abi) -> Result<Lang> {
105 let name = match &abi.name {
106 Some(name) => name,
107 None => {
108 return Err(Error::new_spanned(
109 abi,
110 "ABI name is required, extern \"C\" or extern \"Rust\"",
111 ));
112 }
113 };
114 match name.value().as_str() {
David Tolnay0b76aea2020-03-18 12:54:24 -0700115 "C" | "C++" => Ok(Lang::Cxx),
David Tolnay7db73692019-10-20 14:51:12 -0400116 "Rust" => Ok(Lang::Rust),
117 _ => Err(Error::new_spanned(abi, "unrecognized ABI")),
118 }
119}
120
121fn parse_extern_type(foreign_type: &ForeignItemType) -> Result<ExternType> {
122 let doc = attrs::parse_doc(&foreign_type.attrs)?;
123 let type_token = foreign_type.type_token;
124 let ident = foreign_type.ident.clone();
125 Ok(ExternType {
126 doc,
127 type_token,
128 ident,
129 })
130}
131
David Tolnay6cde49f2020-03-16 12:25:45 -0700132fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result<ExternFn> {
David Tolnay7db73692019-10-20 14:51:12 -0400133 let generics = &foreign_fn.sig.generics;
134 if !generics.params.is_empty() || generics.where_clause.is_some() {
135 return Err(Error::new_spanned(
136 foreign_fn,
137 "extern function with generic parameters is not supported yet",
138 ));
139 }
140 if let Some(variadic) = &foreign_fn.sig.variadic {
141 return Err(Error::new_spanned(
142 variadic,
143 "variadic function is not supported yet",
144 ));
145 }
146
147 let mut receiver = None;
148 let mut args = Vec::new();
149 for arg in &foreign_fn.sig.inputs {
150 match arg {
151 FnArg::Receiver(receiver) => {
152 return Err(Error::new_spanned(receiver, "unsupported signature"))
153 }
154 FnArg::Typed(arg) => {
155 let ident = match arg.pat.as_ref() {
156 Pat::Ident(pat) => pat.ident.clone(),
157 _ => return Err(Error::new_spanned(arg, "unsupported signature")),
158 };
159 let ty = parse_type(&arg.ty)?;
160 if ident != "self" {
161 args.push(Var { ident, ty });
162 continue;
163 }
164 if let Type::Ref(reference) = ty {
165 if let Type::Ident(ident) = reference.inner {
166 receiver = Some(Receiver {
167 mutability: reference.mutability,
168 ident,
169 });
170 continue;
171 }
172 }
173 return Err(Error::new_spanned(arg, "unsupported method receiver"));
174 }
175 }
176 }
David Tolnay59b7ede2020-03-16 00:30:23 -0700177
178 let mut throws = false;
David Tolnayc071b892020-03-18 16:59:53 -0700179 let ret = parse_return_type(&foreign_fn.sig.output, &mut throws)?;
David Tolnay7db73692019-10-20 14:51:12 -0400180 let doc = attrs::parse_doc(&foreign_fn.attrs)?;
181 let fn_token = foreign_fn.sig.fn_token;
182 let ident = foreign_fn.sig.ident.clone();
David Tolnayd95b1192020-03-18 20:07:46 -0700183 let mut foreign_fn2 = foreign_fn.clone();
184 foreign_fn2.attrs.clear();
185 let tokens = quote!(#foreign_fn2);
David Tolnay7db73692019-10-20 14:51:12 -0400186 let semi_token = foreign_fn.semi_token;
David Tolnayd95b1192020-03-18 20:07:46 -0700187
David Tolnay7db73692019-10-20 14:51:12 -0400188 Ok(ExternFn {
David Tolnay6cde49f2020-03-16 12:25:45 -0700189 lang,
David Tolnay7db73692019-10-20 14:51:12 -0400190 doc,
David Tolnay7db73692019-10-20 14:51:12 -0400191 ident,
David Tolnay16448732020-03-18 12:39:36 -0700192 sig: Signature {
193 fn_token,
194 receiver,
195 args,
196 ret,
197 throws,
David Tolnayd95b1192020-03-18 20:07:46 -0700198 tokens,
David Tolnay16448732020-03-18 12:39:36 -0700199 },
David Tolnay7db73692019-10-20 14:51:12 -0400200 semi_token,
201 })
202}
203
204fn parse_type(ty: &RustType) -> Result<Type> {
David Tolnay59b7ede2020-03-16 00:30:23 -0700205 match ty {
David Tolnayb40b9db2020-03-18 13:50:31 -0700206 RustType::Reference(ty) => parse_type_reference(ty),
207 RustType::Path(ty) => parse_type_path(ty),
David Tolnayc071b892020-03-18 16:59:53 -0700208 RustType::BareFn(ty) => parse_type_fn(ty),
David Tolnayb40b9db2020-03-18 13:50:31 -0700209 RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)),
210 _ => Err(Error::new_spanned(ty, "unsupported type")),
211 }
212}
213
214fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
215 let inner = parse_type(&ty.elem)?;
216 let which = match &inner {
217 Type::Ident(ident) if ident == "str" => {
218 if ty.mutability.is_some() {
219 return Err(Error::new_spanned(ty, "unsupported type"));
220 } else {
221 Type::Str
David Tolnay7db73692019-10-20 14:51:12 -0400222 }
223 }
David Tolnayb40b9db2020-03-18 13:50:31 -0700224 _ => Type::Ref,
225 };
226 Ok(which(Box::new(Ref {
227 ampersand: ty.and_token,
228 mutability: ty.mutability,
229 inner,
230 })))
231}
232
233fn parse_type_path(ty: &TypePath) -> Result<Type> {
234 let path = &ty.path;
235 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
236 let segment = &path.segments[0];
237 let ident = segment.ident.clone();
238 match &segment.arguments {
239 PathArguments::None => return Ok(Type::Ident(ident)),
240 PathArguments::AngleBracketed(generic) => {
241 if ident == "UniquePtr" && generic.args.len() == 1 {
242 if let GenericArgument::Type(arg) = &generic.args[0] {
243 let inner = parse_type(arg)?;
244 return Ok(Type::UniquePtr(Box::new(Ty1 {
245 name: ident,
246 langle: generic.lt_token,
247 inner,
248 rangle: generic.gt_token,
249 })));
250 }
251 } else if ident == "Box" && generic.args.len() == 1 {
252 if let GenericArgument::Type(arg) = &generic.args[0] {
253 let inner = parse_type(arg)?;
254 return Ok(Type::RustBox(Box::new(Ty1 {
255 name: ident,
256 langle: generic.lt_token,
257 inner,
258 rangle: generic.gt_token,
259 })));
260 }
261 }
262 }
263 PathArguments::Parenthesized(_) => {}
David Tolnayfb134ed2020-03-15 23:17:48 -0700264 }
David Tolnay7db73692019-10-20 14:51:12 -0400265 }
266 Err(Error::new_spanned(ty, "unsupported type"))
267}
268
David Tolnayc071b892020-03-18 16:59:53 -0700269fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
270 if ty.lifetimes.is_some() {
271 return Err(Error::new_spanned(
272 ty,
273 "function pointer with lifetime parameters is not supported yet",
274 ));
275 }
276 if ty.variadic.is_some() {
277 return Err(Error::new_spanned(
278 ty,
279 "variadic function pointer is not supported yet",
280 ));
281 }
282 let args = ty
283 .inputs
284 .iter()
285 .enumerate()
286 .map(|(i, arg)| {
287 let ty = parse_type(&arg.ty)?;
288 let ident = match &arg.name {
289 Some(ident) => ident.0.clone(),
290 None => format_ident!("_{}", i),
291 };
292 Ok(Var { ident, ty })
293 })
294 .collect::<Result<_>>()?;
295 let mut throws = false;
296 let ret = parse_return_type(&ty.output, &mut throws)?;
297 let tokens = quote!(#ty);
298 Ok(Type::Fn(Box::new(Signature {
299 fn_token: ty.fn_token,
300 receiver: None,
301 args,
302 ret,
303 throws,
304 tokens,
305 })))
306}
307
308fn parse_return_type(ty: &ReturnType, throws: &mut bool) -> Result<Option<Type>> {
309 let mut ret = match ty {
310 ReturnType::Default => return Ok(None),
311 ReturnType::Type(_, ret) => ret.as_ref(),
312 };
313 if let RustType::Path(ty) = ret {
314 let path = &ty.path;
315 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
316 let segment = &path.segments[0];
317 let ident = segment.ident.clone();
318 if let PathArguments::AngleBracketed(generic) = &segment.arguments {
319 if ident == "Result" && generic.args.len() == 1 {
320 if let GenericArgument::Type(arg) = &generic.args[0] {
321 ret = arg;
322 *throws = true;
323 }
324 }
325 }
326 }
327 }
328 match parse_type(ret)? {
329 Type::Void(_) => Ok(None),
330 ty => Ok(Some(ty)),
331 }
332}
333
David Tolnay7db73692019-10-20 14:51:12 -0400334fn check_reserved_name(ident: &Ident) -> Result<()> {
335 if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() {
336 Err(Error::new(ident.span(), "reserved name"))
337 } else {
338 Ok(())
339 }
340}