blob: e07ee250767a7ba61c8e77d38cf4a822d1478026 [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;
6use quote::quote;
7use syn::{
8 Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item,
9 ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType,
10};
11
David Tolnay7db73692019-10-20 14:51:12 -040012pub fn parse_items(items: Vec<Item>) -> Result<Vec<Api>> {
13 let mut apis = Vec::new();
14 for item in items {
15 match item {
16 Item::Struct(item) => {
17 let strct = parse_struct(item)?;
18 apis.push(strct);
19 }
20 Item::ForeignMod(foreign_mod) => {
21 let functions = parse_foreign_mod(foreign_mod)?;
22 apis.extend(functions);
23 }
24 Item::Use(item) => return Err(Error::new_spanned(item, error::USE_NOT_ALLOWED)),
25 _ => return Err(Error::new_spanned(item, "unsupported item")),
26 }
27 }
28 Ok(apis)
29}
30
31fn parse_struct(item: ItemStruct) -> Result<Api> {
32 let generics = &item.generics;
33 if !generics.params.is_empty() || generics.where_clause.is_some() {
34 let struct_token = item.struct_token;
35 let ident = &item.ident;
36 let where_clause = &generics.where_clause;
37 let span = quote!(#struct_token #ident #generics #where_clause);
38 return Err(Error::new_spanned(
39 span,
40 "struct with generic parameters is not supported yet",
41 ));
42 }
43
44 let mut doc = Doc::new();
45 let mut derives = Vec::new();
46 attrs::parse(&item.attrs, &mut doc, Some(&mut derives))?;
47 check_reserved_name(&item.ident)?;
48 match item.fields {
49 Fields::Named(fields) => Ok(Api::Struct(Struct {
50 doc,
51 derives,
52 struct_token: item.struct_token,
53 ident: item.ident,
54 fields: fields
55 .named
56 .into_iter()
57 .map(|field| {
58 Ok(Var {
59 ident: field.ident.unwrap(),
60 ty: parse_type(&field.ty)?,
61 })
62 })
63 .collect::<Result<_>>()?,
64 })),
65 Fields::Unit => Err(Error::new_spanned(item, "unit structs are not supported")),
66 Fields::Unnamed(_) => Err(Error::new_spanned(item, "tuple structs are not supported")),
67 }
68}
69
70fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result<Vec<Api>> {
71 let lang = parse_lang(foreign_mod.abi)?;
72 let api_type = match lang {
73 Lang::Cxx => Api::CxxType,
74 Lang::Rust => Api::RustType,
75 };
76 let api_function = match lang {
77 Lang::Cxx => Api::CxxFunction,
78 Lang::Rust => Api::RustFunction,
79 };
80
81 let mut items = Vec::new();
82 for foreign in &foreign_mod.items {
83 match foreign {
84 ForeignItem::Type(foreign) => {
85 check_reserved_name(&foreign.ident)?;
86 let ety = parse_extern_type(foreign)?;
87 items.push(api_type(ety));
88 }
89 ForeignItem::Fn(foreign) => {
David Tolnay6cde49f2020-03-16 12:25:45 -070090 let efn = parse_extern_fn(foreign, lang)?;
David Tolnay7db73692019-10-20 14:51:12 -040091 items.push(api_function(efn));
92 }
93 ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => {
94 let include = foreign.mac.parse_body()?;
95 items.push(Api::Include(include));
96 }
97 _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")),
98 }
99 }
100 Ok(items)
101}
102
103fn parse_lang(abi: Abi) -> Result<Lang> {
104 let name = match &abi.name {
105 Some(name) => name,
106 None => {
107 return Err(Error::new_spanned(
108 abi,
109 "ABI name is required, extern \"C\" or extern \"Rust\"",
110 ));
111 }
112 };
113 match name.value().as_str() {
114 "C" => Ok(Lang::Cxx),
115 "Rust" => Ok(Lang::Rust),
116 _ => Err(Error::new_spanned(abi, "unrecognized ABI")),
117 }
118}
119
120fn parse_extern_type(foreign_type: &ForeignItemType) -> Result<ExternType> {
121 let doc = attrs::parse_doc(&foreign_type.attrs)?;
122 let type_token = foreign_type.type_token;
123 let ident = foreign_type.ident.clone();
124 Ok(ExternType {
125 doc,
126 type_token,
127 ident,
128 })
129}
130
David Tolnay6cde49f2020-03-16 12:25:45 -0700131fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result<ExternFn> {
David Tolnay7db73692019-10-20 14:51:12 -0400132 let generics = &foreign_fn.sig.generics;
133 if !generics.params.is_empty() || generics.where_clause.is_some() {
134 return Err(Error::new_spanned(
135 foreign_fn,
136 "extern function with generic parameters is not supported yet",
137 ));
138 }
139 if let Some(variadic) = &foreign_fn.sig.variadic {
140 return Err(Error::new_spanned(
141 variadic,
142 "variadic function is not supported yet",
143 ));
144 }
145
146 let mut receiver = None;
147 let mut args = Vec::new();
148 for arg in &foreign_fn.sig.inputs {
149 match arg {
150 FnArg::Receiver(receiver) => {
151 return Err(Error::new_spanned(receiver, "unsupported signature"))
152 }
153 FnArg::Typed(arg) => {
154 let ident = match arg.pat.as_ref() {
155 Pat::Ident(pat) => pat.ident.clone(),
156 _ => return Err(Error::new_spanned(arg, "unsupported signature")),
157 };
158 let ty = parse_type(&arg.ty)?;
159 if ident != "self" {
160 args.push(Var { ident, ty });
161 continue;
162 }
163 if let Type::Ref(reference) = ty {
164 if let Type::Ident(ident) = reference.inner {
165 receiver = Some(Receiver {
166 mutability: reference.mutability,
167 ident,
168 });
169 continue;
170 }
171 }
172 return Err(Error::new_spanned(arg, "unsupported method receiver"));
173 }
174 }
175 }
David Tolnay59b7ede2020-03-16 00:30:23 -0700176
177 let mut throws = false;
David Tolnay7db73692019-10-20 14:51:12 -0400178 let ret = match &foreign_fn.sig.output {
179 ReturnType::Default => None,
David Tolnay59b7ede2020-03-16 00:30:23 -0700180 ReturnType::Type(_, ret) => {
181 let mut ret = ret.as_ref();
182 if let RustType::Path(ty) = ret {
183 let path = &ty.path;
184 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
185 let segment = &path.segments[0];
186 let ident = segment.ident.clone();
187 if let PathArguments::AngleBracketed(generic) = &segment.arguments {
188 if ident == "Result" && generic.args.len() == 1 {
189 if let GenericArgument::Type(arg) = &generic.args[0] {
190 ret = arg;
191 throws = true;
192 }
193 }
194 }
195 }
196 }
197 match parse_type(ret)? {
198 Type::Void(_) => None,
199 ty => Some(ty),
200 }
201 }
David Tolnay7db73692019-10-20 14:51:12 -0400202 };
David Tolnay59b7ede2020-03-16 00:30:23 -0700203
David Tolnay7db73692019-10-20 14:51:12 -0400204 let doc = attrs::parse_doc(&foreign_fn.attrs)?;
205 let fn_token = foreign_fn.sig.fn_token;
206 let ident = foreign_fn.sig.ident.clone();
207 let semi_token = foreign_fn.semi_token;
208 Ok(ExternFn {
David Tolnay6cde49f2020-03-16 12:25:45 -0700209 lang,
David Tolnay7db73692019-10-20 14:51:12 -0400210 doc,
David Tolnay7db73692019-10-20 14:51:12 -0400211 ident,
David Tolnay16448732020-03-18 12:39:36 -0700212 sig: Signature {
213 fn_token,
214 receiver,
215 args,
216 ret,
217 throws,
218 },
David Tolnay7db73692019-10-20 14:51:12 -0400219 semi_token,
220 })
221}
222
223fn parse_type(ty: &RustType) -> Result<Type> {
David Tolnay59b7ede2020-03-16 00:30:23 -0700224 match ty {
David Tolnay7db73692019-10-20 14:51:12 -0400225 RustType::Reference(ty) => {
226 let inner = parse_type(&ty.elem)?;
227 let which = match &inner {
228 Type::Ident(ident) if ident == "str" => {
229 if ty.mutability.is_some() {
230 return Err(Error::new_spanned(ty, "unsupported type"));
231 } else {
232 Type::Str
233 }
234 }
235 _ => Type::Ref,
236 };
237 return Ok(which(Box::new(Ref {
238 ampersand: ty.and_token,
239 mutability: ty.mutability,
240 inner,
241 })));
242 }
243 RustType::Path(ty) => {
244 let path = &ty.path;
245 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
246 let segment = &path.segments[0];
247 let ident = segment.ident.clone();
248 match &segment.arguments {
249 PathArguments::None => return Ok(Type::Ident(ident)),
250 PathArguments::AngleBracketed(generic) => {
251 if ident == "UniquePtr" && generic.args.len() == 1 {
252 if let GenericArgument::Type(arg) = &generic.args[0] {
253 let inner = parse_type(arg)?;
254 return Ok(Type::UniquePtr(Box::new(Ty1 {
255 name: ident,
256 langle: generic.lt_token,
257 inner,
258 rangle: generic.gt_token,
259 })));
260 }
261 } else if ident == "Box" && generic.args.len() == 1 {
262 if let GenericArgument::Type(arg) = &generic.args[0] {
263 let inner = parse_type(arg)?;
264 return Ok(Type::RustBox(Box::new(Ty1 {
265 name: ident,
266 langle: generic.lt_token,
267 inner,
268 rangle: generic.gt_token,
269 })));
270 }
271 }
272 }
273 PathArguments::Parenthesized(_) => {}
274 }
275 }
276 }
David Tolnayfb134ed2020-03-15 23:17:48 -0700277 RustType::Tuple(ty) if ty.elems.is_empty() => {
David Tolnayd0bb3642020-03-15 23:27:11 -0700278 return Ok(Type::Void(ty.paren_token.span));
David Tolnayfb134ed2020-03-15 23:17:48 -0700279 }
David Tolnay7db73692019-10-20 14:51:12 -0400280 _ => {}
281 }
282 Err(Error::new_spanned(ty, "unsupported type"))
283}
284
285fn check_reserved_name(ident: &Ident) -> Result<()> {
286 if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() {
287 Err(Error::new(ident.span(), "reserved name"))
288 } else {
289 Ok(())
290 }
291}