blob: 995b21fa4d27095ebeba3ee861cb9980e601b335 [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
99 let mut doc = Doc::new();
100 attrs::parse(&item.attrs, &mut doc, None)?;
101
102 for variant in &item.variants {
103 match &variant.fields {
104 Fields::Unit => {}
105 _ => {
106 return Err(Error::new_spanned(
107 variant,
108 "enums with data are not allowed",
109 ))
110 }
111 }
112 }
113
114 Ok(Api::Enum(Enum {
115 doc,
116 enum_token: item.enum_token,
117 ident: item.ident,
118 brace_token: item.brace_token,
119 variants: item
120 .variants
121 .into_iter()
122 .map(parse_variant)
123 .collect::<Result<_>>()?,
124 }))
125}
126
127fn parse_variant(variant: RustVariant) -> Result<Variant> {
128 match &variant.discriminant {
129 None => Ok(Variant {
130 ident: variant.ident,
131 discriminant: None,
132 }),
133 Some((
134 _,
135 Expr::Lit(ExprLit {
136 lit: Lit::Int(n), ..
137 }),
138 )) => match n.base10_digits().parse() {
139 Ok(val) => Ok(Variant {
140 ident: variant.ident,
141 discriminant: Some(val),
142 }),
143 Err(_) => Err(Error::new_spanned(
144 variant,
145 "cannot parse enum discriminant as an integer",
146 )),
147 },
148 _ => Err(Error::new_spanned(
149 variant,
150 "enums with non-integer literal discriminants are not supported",
151 )),
152 }
153}
154
David Tolnay7db73692019-10-20 14:51:12 -0400155fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result<Vec<Api>> {
156 let lang = parse_lang(foreign_mod.abi)?;
157 let api_type = match lang {
158 Lang::Cxx => Api::CxxType,
159 Lang::Rust => Api::RustType,
160 };
161 let api_function = match lang {
162 Lang::Cxx => Api::CxxFunction,
163 Lang::Rust => Api::RustFunction,
164 };
165
Joel Galensone1e969d2020-04-21 12:50:20 -0700166 let mut items = Vec::new();
167 for foreign in &foreign_mod.items {
168 match foreign {
David Tolnaya1f29c42020-04-22 18:01:38 -0700169 ForeignItem::Type(foreign) => {
170 let ety = parse_extern_type(foreign)?;
171 items.push(api_type(ety));
172 }
David Tolnay7db73692019-10-20 14:51:12 -0400173 ForeignItem::Fn(foreign) => {
David Tolnaya1f29c42020-04-22 18:01:38 -0700174 let efn = parse_extern_fn(foreign, lang)?;
David Tolnay7db73692019-10-20 14:51:12 -0400175 items.push(api_function(efn));
176 }
177 ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => {
178 let include = foreign.mac.parse_body()?;
179 items.push(Api::Include(include));
180 }
181 _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")),
182 }
183 }
David Tolnaya1f29c42020-04-22 18:01:38 -0700184
185 let mut types = items.iter().filter_map(|item| match item {
186 Api::CxxType(ty) | Api::RustType(ty) => Some(ty),
187 _ => None,
188 });
189 if let (Some(single_type), None) = (types.next(), types.next()) {
190 let single_type = single_type.ident.clone();
191 for item in &mut items {
192 if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item {
193 if let Some(receiver) = &mut efn.receiver {
194 if receiver.ty == "Self" {
195 receiver.ty = single_type.clone();
196 }
197 }
198 }
199 }
200 }
201
David Tolnay7db73692019-10-20 14:51:12 -0400202 Ok(items)
203}
204
205fn parse_lang(abi: Abi) -> Result<Lang> {
206 let name = match &abi.name {
207 Some(name) => name,
208 None => {
209 return Err(Error::new_spanned(
210 abi,
211 "ABI name is required, extern \"C\" or extern \"Rust\"",
212 ));
213 }
214 };
215 match name.value().as_str() {
David Tolnay0b76aea2020-03-18 12:54:24 -0700216 "C" | "C++" => Ok(Lang::Cxx),
David Tolnay7db73692019-10-20 14:51:12 -0400217 "Rust" => Ok(Lang::Rust),
218 _ => Err(Error::new_spanned(abi, "unrecognized ABI")),
219 }
220}
221
222fn parse_extern_type(foreign_type: &ForeignItemType) -> Result<ExternType> {
223 let doc = attrs::parse_doc(&foreign_type.attrs)?;
224 let type_token = foreign_type.type_token;
225 let ident = foreign_type.ident.clone();
226 Ok(ExternType {
227 doc,
228 type_token,
229 ident,
230 })
231}
232
David Tolnaya1f29c42020-04-22 18:01:38 -0700233fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result<ExternFn> {
David Tolnay7db73692019-10-20 14:51:12 -0400234 let generics = &foreign_fn.sig.generics;
235 if !generics.params.is_empty() || generics.where_clause.is_some() {
236 return Err(Error::new_spanned(
237 foreign_fn,
238 "extern function with generic parameters is not supported yet",
239 ));
240 }
241 if let Some(variadic) = &foreign_fn.sig.variadic {
242 return Err(Error::new_spanned(
243 variadic,
244 "variadic function is not supported yet",
245 ));
246 }
247
248 let mut receiver = None;
David Tolnaye3a48152020-04-08 19:38:05 -0700249 let mut args = Punctuated::new();
250 for arg in foreign_fn.sig.inputs.pairs() {
251 let (arg, comma) = arg.into_tuple();
David Tolnay7db73692019-10-20 14:51:12 -0400252 match arg {
David Tolnay1dd11a12020-04-22 12:31:48 -0700253 FnArg::Receiver(arg) => {
David Tolnaya1f29c42020-04-22 18:01:38 -0700254 if let Some((ampersand, lifetime)) = &arg.reference {
255 receiver = Some(Receiver {
256 ampersand: *ampersand,
257 lifetime: lifetime.clone(),
258 mutability: arg.mutability,
259 var: arg.self_token,
260 ty: Token![Self](arg.self_token.span).into(),
261 shorthand: true,
262 });
263 continue;
Joel Galensone1e969d2020-04-21 12:50:20 -0700264 }
David Tolnay1dd11a12020-04-22 12:31:48 -0700265 return Err(Error::new_spanned(arg, "unsupported signature"));
David Tolnay7db73692019-10-20 14:51:12 -0400266 }
267 FnArg::Typed(arg) => {
268 let ident = match arg.pat.as_ref() {
269 Pat::Ident(pat) => pat.ident.clone(),
Joel Galensonba676072020-04-27 15:55:45 -0700270 Pat::Wild(pat) => {
271 Ident::new(&format!("_{}", args.len()), pat.underscore_token.span)
272 }
David Tolnay7db73692019-10-20 14:51:12 -0400273 _ => return Err(Error::new_spanned(arg, "unsupported signature")),
274 };
275 let ty = parse_type(&arg.ty)?;
276 if ident != "self" {
David Tolnaye3a48152020-04-08 19:38:05 -0700277 args.push_value(Var { ident, ty });
278 if let Some(comma) = comma {
279 args.push_punct(*comma);
280 }
David Tolnay7db73692019-10-20 14:51:12 -0400281 continue;
282 }
283 if let Type::Ref(reference) = ty {
284 if let Type::Ident(ident) = reference.inner {
285 receiver = Some(Receiver {
David Tolnayfb6e3862020-04-20 01:33:23 -0700286 ampersand: reference.ampersand,
David Tolnay0bd50fa2020-04-22 15:31:33 -0700287 lifetime: reference.lifetime,
David Tolnay7db73692019-10-20 14:51:12 -0400288 mutability: reference.mutability,
David Tolnay05e11cc2020-04-20 02:13:56 -0700289 var: Token![self](ident.span()),
290 ty: ident,
David Tolnay62d360c2020-04-22 16:26:21 -0700291 shorthand: false,
David Tolnay7db73692019-10-20 14:51:12 -0400292 });
293 continue;
294 }
295 }
296 return Err(Error::new_spanned(arg, "unsupported method receiver"));
297 }
298 }
299 }
David Tolnay59b7ede2020-03-16 00:30:23 -0700300
David Tolnaye3a48152020-04-08 19:38:05 -0700301 let mut throws_tokens = None;
302 let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?;
303 let throws = throws_tokens.is_some();
David Tolnay7db73692019-10-20 14:51:12 -0400304 let doc = attrs::parse_doc(&foreign_fn.attrs)?;
305 let fn_token = foreign_fn.sig.fn_token;
306 let ident = foreign_fn.sig.ident.clone();
David Tolnaye3a48152020-04-08 19:38:05 -0700307 let paren_token = foreign_fn.sig.paren_token;
David Tolnay7db73692019-10-20 14:51:12 -0400308 let semi_token = foreign_fn.semi_token;
David Tolnayd95b1192020-03-18 20:07:46 -0700309
David Tolnay7db73692019-10-20 14:51:12 -0400310 Ok(ExternFn {
David Tolnay6cde49f2020-03-16 12:25:45 -0700311 lang,
David Tolnay7db73692019-10-20 14:51:12 -0400312 doc,
David Tolnay7db73692019-10-20 14:51:12 -0400313 ident,
David Tolnay16448732020-03-18 12:39:36 -0700314 sig: Signature {
315 fn_token,
316 receiver,
317 args,
318 ret,
319 throws,
David Tolnaye3a48152020-04-08 19:38:05 -0700320 paren_token,
321 throws_tokens,
David Tolnay16448732020-03-18 12:39:36 -0700322 },
David Tolnay7db73692019-10-20 14:51:12 -0400323 semi_token,
324 })
325}
326
327fn parse_type(ty: &RustType) -> Result<Type> {
David Tolnay59b7ede2020-03-16 00:30:23 -0700328 match ty {
David Tolnayb40b9db2020-03-18 13:50:31 -0700329 RustType::Reference(ty) => parse_type_reference(ty),
330 RustType::Path(ty) => parse_type_path(ty),
David Tolnayeebe9b72020-04-14 16:32:18 -0700331 RustType::Slice(ty) => parse_type_slice(ty),
David Tolnayc071b892020-03-18 16:59:53 -0700332 RustType::BareFn(ty) => parse_type_fn(ty),
David Tolnayb40b9db2020-03-18 13:50:31 -0700333 RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)),
334 _ => Err(Error::new_spanned(ty, "unsupported type")),
335 }
336}
337
338fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
339 let inner = parse_type(&ty.elem)?;
340 let which = match &inner {
341 Type::Ident(ident) if ident == "str" => {
342 if ty.mutability.is_some() {
343 return Err(Error::new_spanned(ty, "unsupported type"));
344 } else {
345 Type::Str
David Tolnay7db73692019-10-20 14:51:12 -0400346 }
347 }
David Tolnayeebe9b72020-04-14 16:32:18 -0700348 Type::Slice(slice) => match &slice.inner {
349 Type::Ident(ident) if ident == U8 && ty.mutability.is_none() => Type::SliceRefU8,
350 _ => Type::Ref,
David Tolnayeb952ba2020-04-14 15:02:24 -0700351 },
David Tolnayb40b9db2020-03-18 13:50:31 -0700352 _ => Type::Ref,
353 };
354 Ok(which(Box::new(Ref {
355 ampersand: ty.and_token,
David Tolnay0bd50fa2020-04-22 15:31:33 -0700356 lifetime: ty.lifetime.clone(),
David Tolnayb40b9db2020-03-18 13:50:31 -0700357 mutability: ty.mutability,
358 inner,
359 })))
360}
361
362fn parse_type_path(ty: &TypePath) -> Result<Type> {
363 let path = &ty.path;
364 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
365 let segment = &path.segments[0];
366 let ident = segment.ident.clone();
367 match &segment.arguments {
368 PathArguments::None => return Ok(Type::Ident(ident)),
369 PathArguments::AngleBracketed(generic) => {
370 if ident == "UniquePtr" && generic.args.len() == 1 {
371 if let GenericArgument::Type(arg) = &generic.args[0] {
372 let inner = parse_type(arg)?;
373 return Ok(Type::UniquePtr(Box::new(Ty1 {
374 name: ident,
375 langle: generic.lt_token,
376 inner,
377 rangle: generic.gt_token,
378 })));
379 }
David Tolnaye1dcdf72020-04-24 17:40:55 -0700380 } else if ident == "CxxVector" && generic.args.len() == 1 {
Myron Ahneba35cf2020-02-05 19:41:51 +0700381 if let GenericArgument::Type(arg) = &generic.args[0] {
382 let inner = parse_type(arg)?;
David Tolnay4377a9e2020-04-24 15:20:26 -0700383 return Ok(Type::CxxVector(Box::new(Ty1 {
Myron Ahneba35cf2020-02-05 19:41:51 +0700384 name: ident,
385 langle: generic.lt_token,
386 inner,
387 rangle: generic.gt_token,
388 })));
389 }
David Tolnayb40b9db2020-03-18 13:50:31 -0700390 } else if ident == "Box" && generic.args.len() == 1 {
391 if let GenericArgument::Type(arg) = &generic.args[0] {
392 let inner = parse_type(arg)?;
393 return Ok(Type::RustBox(Box::new(Ty1 {
394 name: ident,
395 langle: generic.lt_token,
396 inner,
397 rangle: generic.gt_token,
398 })));
399 }
Myron Ahneba35cf2020-02-05 19:41:51 +0700400 } else if ident == "Vec" && generic.args.len() == 1 {
401 if let GenericArgument::Type(arg) = &generic.args[0] {
402 let inner = parse_type(arg)?;
403 return Ok(Type::RustVec(Box::new(Ty1 {
404 name: ident,
405 langle: generic.lt_token,
406 inner,
407 rangle: generic.gt_token,
408 })));
409 }
David Tolnayb40b9db2020-03-18 13:50:31 -0700410 }
411 }
412 PathArguments::Parenthesized(_) => {}
David Tolnayfb134ed2020-03-15 23:17:48 -0700413 }
David Tolnay7db73692019-10-20 14:51:12 -0400414 }
415 Err(Error::new_spanned(ty, "unsupported type"))
416}
417
David Tolnayeebe9b72020-04-14 16:32:18 -0700418fn parse_type_slice(ty: &TypeSlice) -> Result<Type> {
419 let inner = parse_type(&ty.elem)?;
420 Ok(Type::Slice(Box::new(Slice {
421 bracket: ty.bracket_token,
422 inner,
423 })))
424}
425
David Tolnayc071b892020-03-18 16:59:53 -0700426fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
427 if ty.lifetimes.is_some() {
428 return Err(Error::new_spanned(
429 ty,
430 "function pointer with lifetime parameters is not supported yet",
431 ));
432 }
433 if ty.variadic.is_some() {
434 return Err(Error::new_spanned(
435 ty,
436 "variadic function pointer is not supported yet",
437 ));
438 }
439 let args = ty
440 .inputs
441 .iter()
442 .enumerate()
443 .map(|(i, arg)| {
444 let ty = parse_type(&arg.ty)?;
445 let ident = match &arg.name {
446 Some(ident) => ident.0.clone(),
447 None => format_ident!("_{}", i),
448 };
449 Ok(Var { ident, ty })
450 })
451 .collect::<Result<_>>()?;
David Tolnaye3a48152020-04-08 19:38:05 -0700452 let mut throws_tokens = None;
453 let ret = parse_return_type(&ty.output, &mut throws_tokens)?;
454 let throws = throws_tokens.is_some();
David Tolnayc071b892020-03-18 16:59:53 -0700455 Ok(Type::Fn(Box::new(Signature {
456 fn_token: ty.fn_token,
457 receiver: None,
458 args,
459 ret,
460 throws,
David Tolnaye3a48152020-04-08 19:38:05 -0700461 paren_token: ty.paren_token,
462 throws_tokens,
David Tolnayc071b892020-03-18 16:59:53 -0700463 })))
464}
465
David Tolnaye3a48152020-04-08 19:38:05 -0700466fn parse_return_type(
467 ty: &ReturnType,
468 throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>,
469) -> Result<Option<Type>> {
David Tolnayc071b892020-03-18 16:59:53 -0700470 let mut ret = match ty {
471 ReturnType::Default => return Ok(None),
472 ReturnType::Type(_, ret) => ret.as_ref(),
473 };
474 if let RustType::Path(ty) = ret {
475 let path = &ty.path;
476 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
477 let segment = &path.segments[0];
478 let ident = segment.ident.clone();
479 if let PathArguments::AngleBracketed(generic) = &segment.arguments {
480 if ident == "Result" && generic.args.len() == 1 {
481 if let GenericArgument::Type(arg) = &generic.args[0] {
482 ret = arg;
David Tolnaye3a48152020-04-08 19:38:05 -0700483 *throws_tokens =
484 Some((kw::Result(ident.span()), generic.lt_token, generic.gt_token));
David Tolnayc071b892020-03-18 16:59:53 -0700485 }
486 }
487 }
488 }
489 }
490 match parse_type(ret)? {
491 Type::Void(_) => Ok(None),
492 ty => Ok(Some(ty)),
493 }
494}