blob: 58237f34a8ed1cbef7ead8ee2abc894dc80092db [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,
David Tolnayb40b9db2020-03-18 13:50:31 -07009 ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType, TypePath,
10 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 Tolnay7db73692019-10-20 14:51:12 -0400179 let ret = match &foreign_fn.sig.output {
180 ReturnType::Default => None,
David Tolnay59b7ede2020-03-16 00:30:23 -0700181 ReturnType::Type(_, ret) => {
182 let mut ret = ret.as_ref();
183 if let RustType::Path(ty) = ret {
184 let path = &ty.path;
185 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
186 let segment = &path.segments[0];
187 let ident = segment.ident.clone();
188 if let PathArguments::AngleBracketed(generic) = &segment.arguments {
189 if ident == "Result" && generic.args.len() == 1 {
190 if let GenericArgument::Type(arg) = &generic.args[0] {
191 ret = arg;
192 throws = true;
193 }
194 }
195 }
196 }
197 }
198 match parse_type(ret)? {
199 Type::Void(_) => None,
200 ty => Some(ty),
201 }
202 }
David Tolnay7db73692019-10-20 14:51:12 -0400203 };
David Tolnay59b7ede2020-03-16 00:30:23 -0700204
David Tolnay7db73692019-10-20 14:51:12 -0400205 let doc = attrs::parse_doc(&foreign_fn.attrs)?;
206 let fn_token = foreign_fn.sig.fn_token;
207 let ident = foreign_fn.sig.ident.clone();
David Tolnayd95b1192020-03-18 20:07:46 -0700208 let mut foreign_fn2 = foreign_fn.clone();
209 foreign_fn2.attrs.clear();
210 let tokens = quote!(#foreign_fn2);
David Tolnay7db73692019-10-20 14:51:12 -0400211 let semi_token = foreign_fn.semi_token;
David Tolnayd95b1192020-03-18 20:07:46 -0700212
David Tolnay7db73692019-10-20 14:51:12 -0400213 Ok(ExternFn {
David Tolnay6cde49f2020-03-16 12:25:45 -0700214 lang,
David Tolnay7db73692019-10-20 14:51:12 -0400215 doc,
David Tolnay7db73692019-10-20 14:51:12 -0400216 ident,
David Tolnay16448732020-03-18 12:39:36 -0700217 sig: Signature {
218 fn_token,
219 receiver,
220 args,
221 ret,
222 throws,
David Tolnayd95b1192020-03-18 20:07:46 -0700223 tokens,
David Tolnay16448732020-03-18 12:39:36 -0700224 },
David Tolnay7db73692019-10-20 14:51:12 -0400225 semi_token,
226 })
227}
228
229fn parse_type(ty: &RustType) -> Result<Type> {
David Tolnay59b7ede2020-03-16 00:30:23 -0700230 match ty {
David Tolnayb40b9db2020-03-18 13:50:31 -0700231 RustType::Reference(ty) => parse_type_reference(ty),
232 RustType::Path(ty) => parse_type_path(ty),
233 RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)),
234 _ => Err(Error::new_spanned(ty, "unsupported type")),
235 }
236}
237
238fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
239 let inner = parse_type(&ty.elem)?;
240 let which = match &inner {
241 Type::Ident(ident) if ident == "str" => {
242 if ty.mutability.is_some() {
243 return Err(Error::new_spanned(ty, "unsupported type"));
244 } else {
245 Type::Str
David Tolnay7db73692019-10-20 14:51:12 -0400246 }
247 }
David Tolnayb40b9db2020-03-18 13:50:31 -0700248 _ => Type::Ref,
249 };
250 Ok(which(Box::new(Ref {
251 ampersand: ty.and_token,
252 mutability: ty.mutability,
253 inner,
254 })))
255}
256
257fn parse_type_path(ty: &TypePath) -> Result<Type> {
258 let path = &ty.path;
259 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
260 let segment = &path.segments[0];
261 let ident = segment.ident.clone();
262 match &segment.arguments {
263 PathArguments::None => return Ok(Type::Ident(ident)),
264 PathArguments::AngleBracketed(generic) => {
265 if ident == "UniquePtr" && generic.args.len() == 1 {
266 if let GenericArgument::Type(arg) = &generic.args[0] {
267 let inner = parse_type(arg)?;
268 return Ok(Type::UniquePtr(Box::new(Ty1 {
269 name: ident,
270 langle: generic.lt_token,
271 inner,
272 rangle: generic.gt_token,
273 })));
274 }
275 } else if ident == "Box" && generic.args.len() == 1 {
276 if let GenericArgument::Type(arg) = &generic.args[0] {
277 let inner = parse_type(arg)?;
278 return Ok(Type::RustBox(Box::new(Ty1 {
279 name: ident,
280 langle: generic.lt_token,
281 inner,
282 rangle: generic.gt_token,
283 })));
284 }
285 }
286 }
287 PathArguments::Parenthesized(_) => {}
David Tolnayfb134ed2020-03-15 23:17:48 -0700288 }
David Tolnay7db73692019-10-20 14:51:12 -0400289 }
290 Err(Error::new_spanned(ty, "unsupported type"))
291}
292
293fn check_reserved_name(ident: &Ident) -> Result<()> {
294 if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() {
295 Err(Error::new(ident.span(), "reserved name"))
296 } else {
297 Ok(())
298 }
299}