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