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