blob: a0dddbd1cec064680996e210f5e62cd01fe13026 [file] [log] [blame]
David Tolnayeebe9b72020-04-14 16:32:18 -07001use crate::syntax::Atom::*;
David Tolnay7db73692019-10-20 14:51:12 -04002use crate::syntax::{
Joel Galensonc03402a2020-04-23 17:31:09 -07003 attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice,
4 Struct, Ty1, Type, Var, Variant,
David Tolnay7db73692019-10-20 14:51:12 -04005};
David Tolnayc071b892020-03-18 16:59:53 -07006use quote::{format_ident, quote};
David Tolnaye3a48152020-04-08 19:38:05 -07007use syn::punctuated::Punctuated;
David Tolnay7db73692019-10-20 14:51:12 -04008use syn::{
Joel Galensonc03402a2020-04-23 17:31:09 -07009 Abi, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
10 GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, PathArguments,
11 Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
12 Variant as RustVariant,
David Tolnay7db73692019-10-20 14:51:12 -040013};
14
David Tolnaye3a48152020-04-08 19:38:05 -070015pub mod kw {
16 syn::custom_keyword!(Result);
17}
18
David Tolnay7db73692019-10-20 14:51:12 -040019pub fn parse_items(items: Vec<Item>) -> Result<Vec<Api>> {
20 let mut apis = Vec::new();
21 for item in items {
22 match item {
23 Item::Struct(item) => {
24 let strct = parse_struct(item)?;
25 apis.push(strct);
26 }
Joel Galensonc03402a2020-04-23 17:31:09 -070027 Item::Enum(item) => {
28 let enm = parse_enum(item)?;
29 apis.push(enm);
30 }
David Tolnay7db73692019-10-20 14:51:12 -040031 Item::ForeignMod(foreign_mod) => {
32 let functions = parse_foreign_mod(foreign_mod)?;
33 apis.extend(functions);
34 }
35 Item::Use(item) => return Err(Error::new_spanned(item, error::USE_NOT_ALLOWED)),
36 _ => return Err(Error::new_spanned(item, "unsupported item")),
37 }
38 }
39 Ok(apis)
40}
41
42fn parse_struct(item: ItemStruct) -> Result<Api> {
43 let generics = &item.generics;
44 if !generics.params.is_empty() || generics.where_clause.is_some() {
45 let struct_token = item.struct_token;
46 let ident = &item.ident;
47 let where_clause = &generics.where_clause;
48 let span = quote!(#struct_token #ident #generics #where_clause);
49 return Err(Error::new_spanned(
50 span,
51 "struct with generic parameters is not supported yet",
52 ));
53 }
54
55 let mut doc = Doc::new();
56 let mut derives = Vec::new();
57 attrs::parse(&item.attrs, &mut doc, Some(&mut derives))?;
David Tolnay09462ac2020-03-20 14:58:41 -070058
59 let fields = match item.fields {
60 Fields::Named(fields) => fields,
61 Fields::Unit => return Err(Error::new_spanned(item, "unit structs are not supported")),
62 Fields::Unnamed(_) => {
63 return Err(Error::new_spanned(item, "tuple structs are not supported"))
64 }
65 };
66
67 Ok(Api::Struct(Struct {
68 doc,
69 derives,
70 struct_token: item.struct_token,
71 ident: item.ident,
72 brace_token: fields.brace_token,
73 fields: fields
74 .named
75 .into_iter()
76 .map(|field| {
77 Ok(Var {
78 ident: field.ident.unwrap(),
79 ty: parse_type(&field.ty)?,
David Tolnay7db73692019-10-20 14:51:12 -040080 })
David Tolnay09462ac2020-03-20 14:58:41 -070081 })
82 .collect::<Result<_>>()?,
83 }))
David Tolnay7db73692019-10-20 14:51:12 -040084}
85
Joel Galensonc03402a2020-04-23 17:31:09 -070086fn parse_enum(item: ItemEnum) -> Result<Api> {
87 let generics = &item.generics;
88 if !generics.params.is_empty() || generics.where_clause.is_some() {
89 let enum_token = item.enum_token;
90 let ident = &item.ident;
91 let where_clause = &generics.where_clause;
92 let span = quote!(#enum_token #ident #generics #where_clause);
93 return Err(Error::new_spanned(
94 span,
95 "enums with generic parameters are not allowed",
96 ));
97 }
98
David Tolnayd7984c22020-04-30 20:09:31 -070099 let doc = attrs::parse_doc(&item.attrs)?;
Joel Galensonc03402a2020-04-23 17:31:09 -0700100
101 for variant in &item.variants {
102 match &variant.fields {
103 Fields::Unit => {}
104 _ => {
105 return Err(Error::new_spanned(
106 variant,
David Tolnayd7984c22020-04-30 20:09:31 -0700107 "enums with data are not supported yet",
Joel Galensonc03402a2020-04-23 17:31:09 -0700108 ))
109 }
110 }
111 }
112
113 Ok(Api::Enum(Enum {
114 doc,
115 enum_token: item.enum_token,
116 ident: item.ident,
117 brace_token: item.brace_token,
118 variants: item
119 .variants
120 .into_iter()
121 .map(parse_variant)
122 .collect::<Result<_>>()?,
123 }))
124}
125
126fn parse_variant(variant: RustVariant) -> Result<Variant> {
127 match &variant.discriminant {
128 None => Ok(Variant {
129 ident: variant.ident,
130 discriminant: None,
131 }),
132 Some((
133 _,
134 Expr::Lit(ExprLit {
135 lit: Lit::Int(n), ..
136 }),
David Tolnayd7984c22020-04-30 20:09:31 -0700137 )) => match n.base10_parse() {
Joel Galensonc03402a2020-04-23 17:31:09 -0700138 Ok(val) => Ok(Variant {
139 ident: variant.ident,
140 discriminant: Some(val),
141 }),
142 Err(_) => Err(Error::new_spanned(
143 variant,
144 "cannot parse enum discriminant as an integer",
145 )),
146 },
147 _ => Err(Error::new_spanned(
148 variant,
David Tolnayd7984c22020-04-30 20:09:31 -0700149 "enums with non-integer literal discriminants are not supported yet",
Joel Galensonc03402a2020-04-23 17:31:09 -0700150 )),
151 }
152}
153
David Tolnay7db73692019-10-20 14:51:12 -0400154fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result<Vec<Api>> {
155 let lang = parse_lang(foreign_mod.abi)?;
156 let api_type = match lang {
157 Lang::Cxx => Api::CxxType,
158 Lang::Rust => Api::RustType,
159 };
160 let api_function = match lang {
161 Lang::Cxx => Api::CxxFunction,
162 Lang::Rust => Api::RustFunction,
163 };
164
Joel Galensone1e969d2020-04-21 12:50:20 -0700165 let mut items = Vec::new();
166 for foreign in &foreign_mod.items {
167 match foreign {
David Tolnaya1f29c42020-04-22 18:01:38 -0700168 ForeignItem::Type(foreign) => {
169 let ety = parse_extern_type(foreign)?;
170 items.push(api_type(ety));
171 }
David Tolnay7db73692019-10-20 14:51:12 -0400172 ForeignItem::Fn(foreign) => {
David Tolnaya1f29c42020-04-22 18:01:38 -0700173 let efn = parse_extern_fn(foreign, lang)?;
David Tolnay7db73692019-10-20 14:51:12 -0400174 items.push(api_function(efn));
175 }
176 ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => {
177 let include = foreign.mac.parse_body()?;
178 items.push(Api::Include(include));
179 }
180 _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")),
181 }
182 }
David Tolnaya1f29c42020-04-22 18:01:38 -0700183
184 let mut types = items.iter().filter_map(|item| match item {
185 Api::CxxType(ty) | Api::RustType(ty) => Some(ty),
186 _ => None,
187 });
188 if let (Some(single_type), None) = (types.next(), types.next()) {
189 let single_type = single_type.ident.clone();
190 for item in &mut items {
191 if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item {
192 if let Some(receiver) = &mut efn.receiver {
193 if receiver.ty == "Self" {
194 receiver.ty = single_type.clone();
195 }
196 }
197 }
198 }
199 }
200
David Tolnay7db73692019-10-20 14:51:12 -0400201 Ok(items)
202}
203
204fn parse_lang(abi: Abi) -> Result<Lang> {
205 let name = match &abi.name {
206 Some(name) => name,
207 None => {
208 return Err(Error::new_spanned(
209 abi,
210 "ABI name is required, extern \"C\" or extern \"Rust\"",
211 ));
212 }
213 };
214 match name.value().as_str() {
David Tolnay0b76aea2020-03-18 12:54:24 -0700215 "C" | "C++" => Ok(Lang::Cxx),
David Tolnay7db73692019-10-20 14:51:12 -0400216 "Rust" => Ok(Lang::Rust),
217 _ => Err(Error::new_spanned(abi, "unrecognized ABI")),
218 }
219}
220
221fn parse_extern_type(foreign_type: &ForeignItemType) -> Result<ExternType> {
222 let doc = attrs::parse_doc(&foreign_type.attrs)?;
223 let type_token = foreign_type.type_token;
224 let ident = foreign_type.ident.clone();
225 Ok(ExternType {
226 doc,
227 type_token,
228 ident,
229 })
230}
231
David Tolnaya1f29c42020-04-22 18:01:38 -0700232fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result<ExternFn> {
David Tolnay7db73692019-10-20 14:51:12 -0400233 let generics = &foreign_fn.sig.generics;
234 if !generics.params.is_empty() || generics.where_clause.is_some() {
235 return Err(Error::new_spanned(
236 foreign_fn,
237 "extern function with generic parameters is not supported yet",
238 ));
239 }
240 if let Some(variadic) = &foreign_fn.sig.variadic {
241 return Err(Error::new_spanned(
242 variadic,
243 "variadic function is not supported yet",
244 ));
245 }
246
247 let mut receiver = None;
David Tolnaye3a48152020-04-08 19:38:05 -0700248 let mut args = Punctuated::new();
249 for arg in foreign_fn.sig.inputs.pairs() {
250 let (arg, comma) = arg.into_tuple();
David Tolnay7db73692019-10-20 14:51:12 -0400251 match arg {
David Tolnay1dd11a12020-04-22 12:31:48 -0700252 FnArg::Receiver(arg) => {
David Tolnaya1f29c42020-04-22 18:01:38 -0700253 if let Some((ampersand, lifetime)) = &arg.reference {
254 receiver = Some(Receiver {
255 ampersand: *ampersand,
256 lifetime: lifetime.clone(),
257 mutability: arg.mutability,
258 var: arg.self_token,
259 ty: Token![Self](arg.self_token.span).into(),
260 shorthand: true,
261 });
262 continue;
Joel Galensone1e969d2020-04-21 12:50:20 -0700263 }
David Tolnay1dd11a12020-04-22 12:31:48 -0700264 return Err(Error::new_spanned(arg, "unsupported signature"));
David Tolnay7db73692019-10-20 14:51:12 -0400265 }
266 FnArg::Typed(arg) => {
267 let ident = match arg.pat.as_ref() {
268 Pat::Ident(pat) => pat.ident.clone(),
Joel Galensonba676072020-04-27 15:55:45 -0700269 Pat::Wild(pat) => {
270 Ident::new(&format!("_{}", args.len()), pat.underscore_token.span)
271 }
David Tolnay7db73692019-10-20 14:51:12 -0400272 _ => return Err(Error::new_spanned(arg, "unsupported signature")),
273 };
274 let ty = parse_type(&arg.ty)?;
275 if ident != "self" {
David Tolnaye3a48152020-04-08 19:38:05 -0700276 args.push_value(Var { ident, ty });
277 if let Some(comma) = comma {
278 args.push_punct(*comma);
279 }
David Tolnay7db73692019-10-20 14:51:12 -0400280 continue;
281 }
282 if let Type::Ref(reference) = ty {
283 if let Type::Ident(ident) = reference.inner {
284 receiver = Some(Receiver {
David Tolnayfb6e3862020-04-20 01:33:23 -0700285 ampersand: reference.ampersand,
David Tolnay0bd50fa2020-04-22 15:31:33 -0700286 lifetime: reference.lifetime,
David Tolnay7db73692019-10-20 14:51:12 -0400287 mutability: reference.mutability,
David Tolnay05e11cc2020-04-20 02:13:56 -0700288 var: Token![self](ident.span()),
289 ty: ident,
David Tolnay62d360c2020-04-22 16:26:21 -0700290 shorthand: false,
David Tolnay7db73692019-10-20 14:51:12 -0400291 });
292 continue;
293 }
294 }
295 return Err(Error::new_spanned(arg, "unsupported method receiver"));
296 }
297 }
298 }
David Tolnay59b7ede2020-03-16 00:30:23 -0700299
David Tolnaye3a48152020-04-08 19:38:05 -0700300 let mut throws_tokens = None;
301 let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?;
302 let throws = throws_tokens.is_some();
David Tolnay7db73692019-10-20 14:51:12 -0400303 let doc = attrs::parse_doc(&foreign_fn.attrs)?;
304 let fn_token = foreign_fn.sig.fn_token;
305 let ident = foreign_fn.sig.ident.clone();
David Tolnaye3a48152020-04-08 19:38:05 -0700306 let paren_token = foreign_fn.sig.paren_token;
David Tolnay7db73692019-10-20 14:51:12 -0400307 let semi_token = foreign_fn.semi_token;
David Tolnayd95b1192020-03-18 20:07:46 -0700308
David Tolnay7db73692019-10-20 14:51:12 -0400309 Ok(ExternFn {
David Tolnay6cde49f2020-03-16 12:25:45 -0700310 lang,
David Tolnay7db73692019-10-20 14:51:12 -0400311 doc,
David Tolnay7db73692019-10-20 14:51:12 -0400312 ident,
David Tolnay16448732020-03-18 12:39:36 -0700313 sig: Signature {
314 fn_token,
315 receiver,
316 args,
317 ret,
318 throws,
David Tolnaye3a48152020-04-08 19:38:05 -0700319 paren_token,
320 throws_tokens,
David Tolnay16448732020-03-18 12:39:36 -0700321 },
David Tolnay7db73692019-10-20 14:51:12 -0400322 semi_token,
323 })
324}
325
326fn parse_type(ty: &RustType) -> Result<Type> {
David Tolnay59b7ede2020-03-16 00:30:23 -0700327 match ty {
David Tolnayb40b9db2020-03-18 13:50:31 -0700328 RustType::Reference(ty) => parse_type_reference(ty),
329 RustType::Path(ty) => parse_type_path(ty),
David Tolnayeebe9b72020-04-14 16:32:18 -0700330 RustType::Slice(ty) => parse_type_slice(ty),
David Tolnayc071b892020-03-18 16:59:53 -0700331 RustType::BareFn(ty) => parse_type_fn(ty),
David Tolnayb40b9db2020-03-18 13:50:31 -0700332 RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)),
333 _ => Err(Error::new_spanned(ty, "unsupported type")),
334 }
335}
336
337fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
338 let inner = parse_type(&ty.elem)?;
339 let which = match &inner {
340 Type::Ident(ident) if ident == "str" => {
341 if ty.mutability.is_some() {
342 return Err(Error::new_spanned(ty, "unsupported type"));
343 } else {
344 Type::Str
David Tolnay7db73692019-10-20 14:51:12 -0400345 }
346 }
David Tolnayeebe9b72020-04-14 16:32:18 -0700347 Type::Slice(slice) => match &slice.inner {
348 Type::Ident(ident) if ident == U8 && ty.mutability.is_none() => Type::SliceRefU8,
349 _ => Type::Ref,
David Tolnayeb952ba2020-04-14 15:02:24 -0700350 },
David Tolnayb40b9db2020-03-18 13:50:31 -0700351 _ => Type::Ref,
352 };
353 Ok(which(Box::new(Ref {
354 ampersand: ty.and_token,
David Tolnay0bd50fa2020-04-22 15:31:33 -0700355 lifetime: ty.lifetime.clone(),
David Tolnayb40b9db2020-03-18 13:50:31 -0700356 mutability: ty.mutability,
357 inner,
358 })))
359}
360
361fn parse_type_path(ty: &TypePath) -> Result<Type> {
362 let path = &ty.path;
363 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
364 let segment = &path.segments[0];
365 let ident = segment.ident.clone();
366 match &segment.arguments {
367 PathArguments::None => return Ok(Type::Ident(ident)),
368 PathArguments::AngleBracketed(generic) => {
369 if ident == "UniquePtr" && generic.args.len() == 1 {
370 if let GenericArgument::Type(arg) = &generic.args[0] {
371 let inner = parse_type(arg)?;
372 return Ok(Type::UniquePtr(Box::new(Ty1 {
373 name: ident,
374 langle: generic.lt_token,
375 inner,
376 rangle: generic.gt_token,
377 })));
378 }
David Tolnaye1dcdf72020-04-24 17:40:55 -0700379 } else if ident == "CxxVector" && generic.args.len() == 1 {
Myron Ahneba35cf2020-02-05 19:41:51 +0700380 if let GenericArgument::Type(arg) = &generic.args[0] {
381 let inner = parse_type(arg)?;
David Tolnay4377a9e2020-04-24 15:20:26 -0700382 return Ok(Type::CxxVector(Box::new(Ty1 {
Myron Ahneba35cf2020-02-05 19:41:51 +0700383 name: ident,
384 langle: generic.lt_token,
385 inner,
386 rangle: generic.gt_token,
387 })));
388 }
David Tolnayb40b9db2020-03-18 13:50:31 -0700389 } else if ident == "Box" && generic.args.len() == 1 {
390 if let GenericArgument::Type(arg) = &generic.args[0] {
391 let inner = parse_type(arg)?;
392 return Ok(Type::RustBox(Box::new(Ty1 {
393 name: ident,
394 langle: generic.lt_token,
395 inner,
396 rangle: generic.gt_token,
397 })));
398 }
Myron Ahneba35cf2020-02-05 19:41:51 +0700399 } else if ident == "Vec" && generic.args.len() == 1 {
400 if let GenericArgument::Type(arg) = &generic.args[0] {
401 let inner = parse_type(arg)?;
402 return Ok(Type::RustVec(Box::new(Ty1 {
403 name: ident,
404 langle: generic.lt_token,
405 inner,
406 rangle: generic.gt_token,
407 })));
408 }
David Tolnayb40b9db2020-03-18 13:50:31 -0700409 }
410 }
411 PathArguments::Parenthesized(_) => {}
David Tolnayfb134ed2020-03-15 23:17:48 -0700412 }
David Tolnay7db73692019-10-20 14:51:12 -0400413 }
414 Err(Error::new_spanned(ty, "unsupported type"))
415}
416
David Tolnayeebe9b72020-04-14 16:32:18 -0700417fn parse_type_slice(ty: &TypeSlice) -> Result<Type> {
418 let inner = parse_type(&ty.elem)?;
419 Ok(Type::Slice(Box::new(Slice {
420 bracket: ty.bracket_token,
421 inner,
422 })))
423}
424
David Tolnayc071b892020-03-18 16:59:53 -0700425fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
426 if ty.lifetimes.is_some() {
427 return Err(Error::new_spanned(
428 ty,
429 "function pointer with lifetime parameters is not supported yet",
430 ));
431 }
432 if ty.variadic.is_some() {
433 return Err(Error::new_spanned(
434 ty,
435 "variadic function pointer is not supported yet",
436 ));
437 }
438 let args = ty
439 .inputs
440 .iter()
441 .enumerate()
442 .map(|(i, arg)| {
443 let ty = parse_type(&arg.ty)?;
444 let ident = match &arg.name {
445 Some(ident) => ident.0.clone(),
446 None => format_ident!("_{}", i),
447 };
448 Ok(Var { ident, ty })
449 })
450 .collect::<Result<_>>()?;
David Tolnaye3a48152020-04-08 19:38:05 -0700451 let mut throws_tokens = None;
452 let ret = parse_return_type(&ty.output, &mut throws_tokens)?;
453 let throws = throws_tokens.is_some();
David Tolnayc071b892020-03-18 16:59:53 -0700454 Ok(Type::Fn(Box::new(Signature {
455 fn_token: ty.fn_token,
456 receiver: None,
457 args,
458 ret,
459 throws,
David Tolnaye3a48152020-04-08 19:38:05 -0700460 paren_token: ty.paren_token,
461 throws_tokens,
David Tolnayc071b892020-03-18 16:59:53 -0700462 })))
463}
464
David Tolnaye3a48152020-04-08 19:38:05 -0700465fn parse_return_type(
466 ty: &ReturnType,
467 throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>,
468) -> Result<Option<Type>> {
David Tolnayc071b892020-03-18 16:59:53 -0700469 let mut ret = match ty {
470 ReturnType::Default => return Ok(None),
471 ReturnType::Type(_, ret) => ret.as_ref(),
472 };
473 if let RustType::Path(ty) = ret {
474 let path = &ty.path;
475 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
476 let segment = &path.segments[0];
477 let ident = segment.ident.clone();
478 if let PathArguments::AngleBracketed(generic) = &segment.arguments {
479 if ident == "Result" && generic.args.len() == 1 {
480 if let GenericArgument::Type(arg) = &generic.args[0] {
481 ret = arg;
David Tolnaye3a48152020-04-08 19:38:05 -0700482 *throws_tokens =
483 Some((kw::Result(ident.span()), generic.lt_token, generic.gt_token));
David Tolnayc071b892020-03-18 16:59:53 -0700484 }
485 }
486 }
487 }
488 }
489 match parse_type(ret)? {
490 Type::Void(_) => Ok(None),
491 ty => Ok(Some(ty)),
492 }
493}