blob: 8bf0a4aed730d86aac58852c75bf1d6d29d1d109 [file] [log] [blame]
David Tolnay52759782020-05-03 23:59:40 -07001use crate::syntax::report::Errors;
David Tolnayeebe9b72020-04-14 16:32:18 -07002use crate::syntax::Atom::*;
David Tolnay7db73692019-10-20 14:51:12 -04003use crate::syntax::{
Joel Galensonc03402a2020-04-23 17:31:09 -07004 attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice,
5 Struct, Ty1, Type, Var, Variant,
David Tolnay7db73692019-10-20 14:51:12 -04006};
David Tolnayc071b892020-03-18 16:59:53 -07007use quote::{format_ident, quote};
David Tolnaye3a48152020-04-08 19:38:05 -07008use syn::punctuated::Punctuated;
David Tolnay7db73692019-10-20 14:51:12 -04009use syn::{
Joel Galensonc03402a2020-04-23 17:31:09 -070010 Abi, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
11 GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, PathArguments,
12 Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
13 Variant as RustVariant,
David Tolnay7db73692019-10-20 14:51:12 -040014};
15
David Tolnaye3a48152020-04-08 19:38:05 -070016pub mod kw {
17 syn::custom_keyword!(Result);
18}
19
David Tolnay52759782020-05-03 23:59:40 -070020pub fn parse_items(cx: &mut Errors, items: Vec<Item>) -> Vec<Api> {
David Tolnay7db73692019-10-20 14:51:12 -040021 let mut apis = Vec::new();
22 for item in items {
23 match item {
David Tolnay52759782020-05-03 23:59:40 -070024 Item::Struct(item) => match parse_struct(item) {
25 Ok(strct) => apis.push(strct),
26 Err(err) => cx.push(err),
27 },
28 Item::Enum(item) => match parse_enum(item) {
29 Ok(enm) => apis.push(enm),
30 Err(err) => cx.push(err),
31 },
32 Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis),
33 Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED),
34 _ => cx.error(item, "unsupported item"),
David Tolnay7db73692019-10-20 14:51:12 -040035 }
36 }
David Tolnay52759782020-05-03 23:59:40 -070037 apis
David Tolnay7db73692019-10-20 14:51:12 -040038}
39
40fn parse_struct(item: ItemStruct) -> Result<Api> {
41 let generics = &item.generics;
42 if !generics.params.is_empty() || generics.where_clause.is_some() {
43 let struct_token = item.struct_token;
44 let ident = &item.ident;
45 let where_clause = &generics.where_clause;
46 let span = quote!(#struct_token #ident #generics #where_clause);
47 return Err(Error::new_spanned(
48 span,
49 "struct with generic parameters is not supported yet",
50 ));
51 }
52
53 let mut doc = Doc::new();
54 let mut derives = Vec::new();
55 attrs::parse(&item.attrs, &mut doc, Some(&mut derives))?;
David Tolnay09462ac2020-03-20 14:58:41 -070056
57 let fields = match item.fields {
58 Fields::Named(fields) => fields,
59 Fields::Unit => return Err(Error::new_spanned(item, "unit structs are not supported")),
60 Fields::Unnamed(_) => {
61 return Err(Error::new_spanned(item, "tuple structs are not supported"))
62 }
63 };
64
65 Ok(Api::Struct(Struct {
66 doc,
67 derives,
68 struct_token: item.struct_token,
69 ident: item.ident,
70 brace_token: fields.brace_token,
71 fields: fields
72 .named
73 .into_iter()
74 .map(|field| {
75 Ok(Var {
76 ident: field.ident.unwrap(),
77 ty: parse_type(&field.ty)?,
David Tolnay7db73692019-10-20 14:51:12 -040078 })
David Tolnay09462ac2020-03-20 14:58:41 -070079 })
80 .collect::<Result<_>>()?,
81 }))
David Tolnay7db73692019-10-20 14:51:12 -040082}
83
Joel Galensonc03402a2020-04-23 17:31:09 -070084fn parse_enum(item: ItemEnum) -> Result<Api> {
85 let generics = &item.generics;
86 if !generics.params.is_empty() || generics.where_clause.is_some() {
87 let enum_token = item.enum_token;
88 let ident = &item.ident;
89 let where_clause = &generics.where_clause;
90 let span = quote!(#enum_token #ident #generics #where_clause);
91 return Err(Error::new_spanned(
92 span,
93 "enums with generic parameters are not allowed",
94 ));
95 }
96
David Tolnayd7984c22020-04-30 20:09:31 -070097 let doc = attrs::parse_doc(&item.attrs)?;
Joel Galensonc03402a2020-04-23 17:31:09 -070098
99 for variant in &item.variants {
100 match &variant.fields {
101 Fields::Unit => {}
102 _ => {
103 return Err(Error::new_spanned(
104 variant,
David Tolnayd7984c22020-04-30 20:09:31 -0700105 "enums with data are not supported yet",
Joel Galensonc03402a2020-04-23 17:31:09 -0700106 ))
107 }
108 }
109 }
110
111 Ok(Api::Enum(Enum {
112 doc,
113 enum_token: item.enum_token,
114 ident: item.ident,
115 brace_token: item.brace_token,
116 variants: item
117 .variants
118 .into_iter()
119 .map(parse_variant)
120 .collect::<Result<_>>()?,
121 }))
122}
123
124fn parse_variant(variant: RustVariant) -> Result<Variant> {
125 match &variant.discriminant {
126 None => Ok(Variant {
127 ident: variant.ident,
128 discriminant: None,
129 }),
130 Some((
131 _,
132 Expr::Lit(ExprLit {
133 lit: Lit::Int(n), ..
134 }),
David Tolnayd7984c22020-04-30 20:09:31 -0700135 )) => match n.base10_parse() {
Joel Galensonc03402a2020-04-23 17:31:09 -0700136 Ok(val) => Ok(Variant {
137 ident: variant.ident,
138 discriminant: Some(val),
139 }),
140 Err(_) => Err(Error::new_spanned(
141 variant,
142 "cannot parse enum discriminant as an integer",
143 )),
144 },
145 _ => Err(Error::new_spanned(
146 variant,
David Tolnayd7984c22020-04-30 20:09:31 -0700147 "enums with non-integer literal discriminants are not supported yet",
Joel Galensonc03402a2020-04-23 17:31:09 -0700148 )),
149 }
150}
151
David Tolnay52759782020-05-03 23:59:40 -0700152fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec<Api>) {
153 let lang = match parse_lang(foreign_mod.abi) {
154 Ok(lang) => lang,
155 Err(err) => return cx.push(err),
156 };
David Tolnay7db73692019-10-20 14:51:12 -0400157 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 Tolnay52759782020-05-03 23:59:40 -0700169 ForeignItem::Type(foreign) => match parse_extern_type(foreign) {
170 Ok(ety) => items.push(api_type(ety)),
171 Err(err) => cx.push(err),
172 },
173 ForeignItem::Fn(foreign) => match parse_extern_fn(foreign, lang) {
174 Ok(efn) => items.push(api_function(efn)),
175 Err(err) => cx.push(err),
176 },
David Tolnay7db73692019-10-20 14:51:12 -0400177 ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => {
David Tolnay52759782020-05-03 23:59:40 -0700178 match foreign.mac.parse_body() {
179 Ok(include) => items.push(Api::Include(include)),
180 Err(err) => cx.push(err),
181 }
David Tolnay7db73692019-10-20 14:51:12 -0400182 }
David Tolnay52759782020-05-03 23:59:40 -0700183 _ => cx.error(foreign, "unsupported foreign item"),
David Tolnay7db73692019-10-20 14:51:12 -0400184 }
185 }
David Tolnaya1f29c42020-04-22 18:01:38 -0700186
187 let mut types = items.iter().filter_map(|item| match item {
188 Api::CxxType(ty) | Api::RustType(ty) => Some(ty),
189 _ => None,
190 });
191 if let (Some(single_type), None) = (types.next(), types.next()) {
192 let single_type = single_type.ident.clone();
193 for item in &mut items {
194 if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item {
195 if let Some(receiver) = &mut efn.receiver {
196 if receiver.ty == "Self" {
197 receiver.ty = single_type.clone();
198 }
199 }
200 }
201 }
202 }
203
David Tolnay52759782020-05-03 23:59:40 -0700204 out.extend(items);
David Tolnay7db73692019-10-20 14:51:12 -0400205}
206
207fn parse_lang(abi: Abi) -> Result<Lang> {
208 let name = match &abi.name {
209 Some(name) => name,
210 None => {
211 return Err(Error::new_spanned(
212 abi,
213 "ABI name is required, extern \"C\" or extern \"Rust\"",
214 ));
215 }
216 };
217 match name.value().as_str() {
David Tolnay0b76aea2020-03-18 12:54:24 -0700218 "C" | "C++" => Ok(Lang::Cxx),
David Tolnay7db73692019-10-20 14:51:12 -0400219 "Rust" => Ok(Lang::Rust),
220 _ => Err(Error::new_spanned(abi, "unrecognized ABI")),
221 }
222}
223
224fn parse_extern_type(foreign_type: &ForeignItemType) -> Result<ExternType> {
225 let doc = attrs::parse_doc(&foreign_type.attrs)?;
226 let type_token = foreign_type.type_token;
227 let ident = foreign_type.ident.clone();
228 Ok(ExternType {
229 doc,
230 type_token,
231 ident,
232 })
233}
234
David Tolnaya1f29c42020-04-22 18:01:38 -0700235fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result<ExternFn> {
David Tolnay7db73692019-10-20 14:51:12 -0400236 let generics = &foreign_fn.sig.generics;
237 if !generics.params.is_empty() || generics.where_clause.is_some() {
238 return Err(Error::new_spanned(
239 foreign_fn,
240 "extern function with generic parameters is not supported yet",
241 ));
242 }
243 if let Some(variadic) = &foreign_fn.sig.variadic {
244 return Err(Error::new_spanned(
245 variadic,
246 "variadic function is not supported yet",
247 ));
248 }
249
250 let mut receiver = None;
David Tolnaye3a48152020-04-08 19:38:05 -0700251 let mut args = Punctuated::new();
252 for arg in foreign_fn.sig.inputs.pairs() {
253 let (arg, comma) = arg.into_tuple();
David Tolnay7db73692019-10-20 14:51:12 -0400254 match arg {
David Tolnay1dd11a12020-04-22 12:31:48 -0700255 FnArg::Receiver(arg) => {
David Tolnaya1f29c42020-04-22 18:01:38 -0700256 if let Some((ampersand, lifetime)) = &arg.reference {
257 receiver = Some(Receiver {
258 ampersand: *ampersand,
259 lifetime: lifetime.clone(),
260 mutability: arg.mutability,
261 var: arg.self_token,
262 ty: Token![Self](arg.self_token.span).into(),
263 shorthand: true,
264 });
265 continue;
Joel Galensone1e969d2020-04-21 12:50:20 -0700266 }
David Tolnay1dd11a12020-04-22 12:31:48 -0700267 return Err(Error::new_spanned(arg, "unsupported signature"));
David Tolnay7db73692019-10-20 14:51:12 -0400268 }
269 FnArg::Typed(arg) => {
270 let ident = match arg.pat.as_ref() {
271 Pat::Ident(pat) => pat.ident.clone(),
Joel Galensonba676072020-04-27 15:55:45 -0700272 Pat::Wild(pat) => {
273 Ident::new(&format!("_{}", args.len()), pat.underscore_token.span)
274 }
David Tolnay7db73692019-10-20 14:51:12 -0400275 _ => return Err(Error::new_spanned(arg, "unsupported signature")),
276 };
277 let ty = parse_type(&arg.ty)?;
278 if ident != "self" {
David Tolnaye3a48152020-04-08 19:38:05 -0700279 args.push_value(Var { ident, ty });
280 if let Some(comma) = comma {
281 args.push_punct(*comma);
282 }
David Tolnay7db73692019-10-20 14:51:12 -0400283 continue;
284 }
285 if let Type::Ref(reference) = ty {
286 if let Type::Ident(ident) = reference.inner {
287 receiver = Some(Receiver {
David Tolnayfb6e3862020-04-20 01:33:23 -0700288 ampersand: reference.ampersand,
David Tolnay0bd50fa2020-04-22 15:31:33 -0700289 lifetime: reference.lifetime,
David Tolnay7db73692019-10-20 14:51:12 -0400290 mutability: reference.mutability,
David Tolnay05e11cc2020-04-20 02:13:56 -0700291 var: Token![self](ident.span()),
292 ty: ident,
David Tolnay62d360c2020-04-22 16:26:21 -0700293 shorthand: false,
David Tolnay7db73692019-10-20 14:51:12 -0400294 });
295 continue;
296 }
297 }
298 return Err(Error::new_spanned(arg, "unsupported method receiver"));
299 }
300 }
301 }
David Tolnay59b7ede2020-03-16 00:30:23 -0700302
David Tolnaye3a48152020-04-08 19:38:05 -0700303 let mut throws_tokens = None;
304 let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?;
305 let throws = throws_tokens.is_some();
David Tolnay7db73692019-10-20 14:51:12 -0400306 let doc = attrs::parse_doc(&foreign_fn.attrs)?;
307 let fn_token = foreign_fn.sig.fn_token;
308 let ident = foreign_fn.sig.ident.clone();
David Tolnaye3a48152020-04-08 19:38:05 -0700309 let paren_token = foreign_fn.sig.paren_token;
David Tolnay7db73692019-10-20 14:51:12 -0400310 let semi_token = foreign_fn.semi_token;
David Tolnayd95b1192020-03-18 20:07:46 -0700311
David Tolnay7db73692019-10-20 14:51:12 -0400312 Ok(ExternFn {
David Tolnay6cde49f2020-03-16 12:25:45 -0700313 lang,
David Tolnay7db73692019-10-20 14:51:12 -0400314 doc,
David Tolnay7db73692019-10-20 14:51:12 -0400315 ident,
David Tolnay16448732020-03-18 12:39:36 -0700316 sig: Signature {
317 fn_token,
318 receiver,
319 args,
320 ret,
321 throws,
David Tolnaye3a48152020-04-08 19:38:05 -0700322 paren_token,
323 throws_tokens,
David Tolnay16448732020-03-18 12:39:36 -0700324 },
David Tolnay7db73692019-10-20 14:51:12 -0400325 semi_token,
326 })
327}
328
329fn parse_type(ty: &RustType) -> Result<Type> {
David Tolnay59b7ede2020-03-16 00:30:23 -0700330 match ty {
David Tolnayb40b9db2020-03-18 13:50:31 -0700331 RustType::Reference(ty) => parse_type_reference(ty),
332 RustType::Path(ty) => parse_type_path(ty),
David Tolnayeebe9b72020-04-14 16:32:18 -0700333 RustType::Slice(ty) => parse_type_slice(ty),
David Tolnayc071b892020-03-18 16:59:53 -0700334 RustType::BareFn(ty) => parse_type_fn(ty),
David Tolnayb40b9db2020-03-18 13:50:31 -0700335 RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)),
336 _ => Err(Error::new_spanned(ty, "unsupported type")),
337 }
338}
339
340fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
341 let inner = parse_type(&ty.elem)?;
342 let which = match &inner {
343 Type::Ident(ident) if ident == "str" => {
344 if ty.mutability.is_some() {
345 return Err(Error::new_spanned(ty, "unsupported type"));
346 } else {
347 Type::Str
David Tolnay7db73692019-10-20 14:51:12 -0400348 }
349 }
David Tolnayeebe9b72020-04-14 16:32:18 -0700350 Type::Slice(slice) => match &slice.inner {
351 Type::Ident(ident) if ident == U8 && ty.mutability.is_none() => Type::SliceRefU8,
352 _ => Type::Ref,
David Tolnayeb952ba2020-04-14 15:02:24 -0700353 },
David Tolnayb40b9db2020-03-18 13:50:31 -0700354 _ => Type::Ref,
355 };
356 Ok(which(Box::new(Ref {
357 ampersand: ty.and_token,
David Tolnay0bd50fa2020-04-22 15:31:33 -0700358 lifetime: ty.lifetime.clone(),
David Tolnayb40b9db2020-03-18 13:50:31 -0700359 mutability: ty.mutability,
360 inner,
361 })))
362}
363
364fn parse_type_path(ty: &TypePath) -> Result<Type> {
365 let path = &ty.path;
366 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
367 let segment = &path.segments[0];
368 let ident = segment.ident.clone();
369 match &segment.arguments {
370 PathArguments::None => return Ok(Type::Ident(ident)),
371 PathArguments::AngleBracketed(generic) => {
372 if ident == "UniquePtr" && generic.args.len() == 1 {
373 if let GenericArgument::Type(arg) = &generic.args[0] {
374 let inner = parse_type(arg)?;
375 return Ok(Type::UniquePtr(Box::new(Ty1 {
376 name: ident,
377 langle: generic.lt_token,
378 inner,
379 rangle: generic.gt_token,
380 })));
381 }
David Tolnaye1dcdf72020-04-24 17:40:55 -0700382 } else if ident == "CxxVector" && generic.args.len() == 1 {
Myron Ahneba35cf2020-02-05 19:41:51 +0700383 if let GenericArgument::Type(arg) = &generic.args[0] {
384 let inner = parse_type(arg)?;
David Tolnay4377a9e2020-04-24 15:20:26 -0700385 return Ok(Type::CxxVector(Box::new(Ty1 {
Myron Ahneba35cf2020-02-05 19:41:51 +0700386 name: ident,
387 langle: generic.lt_token,
388 inner,
389 rangle: generic.gt_token,
390 })));
391 }
David Tolnayb40b9db2020-03-18 13:50:31 -0700392 } else if ident == "Box" && generic.args.len() == 1 {
393 if let GenericArgument::Type(arg) = &generic.args[0] {
394 let inner = parse_type(arg)?;
395 return Ok(Type::RustBox(Box::new(Ty1 {
396 name: ident,
397 langle: generic.lt_token,
398 inner,
399 rangle: generic.gt_token,
400 })));
401 }
Myron Ahneba35cf2020-02-05 19:41:51 +0700402 } else if ident == "Vec" && generic.args.len() == 1 {
403 if let GenericArgument::Type(arg) = &generic.args[0] {
404 let inner = parse_type(arg)?;
405 return Ok(Type::RustVec(Box::new(Ty1 {
406 name: ident,
407 langle: generic.lt_token,
408 inner,
409 rangle: generic.gt_token,
410 })));
411 }
David Tolnayb40b9db2020-03-18 13:50:31 -0700412 }
413 }
414 PathArguments::Parenthesized(_) => {}
David Tolnayfb134ed2020-03-15 23:17:48 -0700415 }
David Tolnay7db73692019-10-20 14:51:12 -0400416 }
417 Err(Error::new_spanned(ty, "unsupported type"))
418}
419
David Tolnayeebe9b72020-04-14 16:32:18 -0700420fn parse_type_slice(ty: &TypeSlice) -> Result<Type> {
421 let inner = parse_type(&ty.elem)?;
422 Ok(Type::Slice(Box::new(Slice {
423 bracket: ty.bracket_token,
424 inner,
425 })))
426}
427
David Tolnayc071b892020-03-18 16:59:53 -0700428fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
429 if ty.lifetimes.is_some() {
430 return Err(Error::new_spanned(
431 ty,
432 "function pointer with lifetime parameters is not supported yet",
433 ));
434 }
435 if ty.variadic.is_some() {
436 return Err(Error::new_spanned(
437 ty,
438 "variadic function pointer is not supported yet",
439 ));
440 }
441 let args = ty
442 .inputs
443 .iter()
444 .enumerate()
445 .map(|(i, arg)| {
446 let ty = parse_type(&arg.ty)?;
447 let ident = match &arg.name {
448 Some(ident) => ident.0.clone(),
449 None => format_ident!("_{}", i),
450 };
451 Ok(Var { ident, ty })
452 })
453 .collect::<Result<_>>()?;
David Tolnaye3a48152020-04-08 19:38:05 -0700454 let mut throws_tokens = None;
455 let ret = parse_return_type(&ty.output, &mut throws_tokens)?;
456 let throws = throws_tokens.is_some();
David Tolnayc071b892020-03-18 16:59:53 -0700457 Ok(Type::Fn(Box::new(Signature {
458 fn_token: ty.fn_token,
459 receiver: None,
460 args,
461 ret,
462 throws,
David Tolnaye3a48152020-04-08 19:38:05 -0700463 paren_token: ty.paren_token,
464 throws_tokens,
David Tolnayc071b892020-03-18 16:59:53 -0700465 })))
466}
467
David Tolnaye3a48152020-04-08 19:38:05 -0700468fn parse_return_type(
469 ty: &ReturnType,
470 throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>,
471) -> Result<Option<Type>> {
David Tolnayc071b892020-03-18 16:59:53 -0700472 let mut ret = match ty {
473 ReturnType::Default => return Ok(None),
474 ReturnType::Type(_, ret) => ret.as_ref(),
475 };
476 if let RustType::Path(ty) = ret {
477 let path = &ty.path;
478 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
479 let segment = &path.segments[0];
480 let ident = segment.ident.clone();
481 if let PathArguments::AngleBracketed(generic) = &segment.arguments {
482 if ident == "Result" && generic.args.len() == 1 {
483 if let GenericArgument::Type(arg) = &generic.args[0] {
484 ret = arg;
David Tolnaye3a48152020-04-08 19:38:05 -0700485 *throws_tokens =
486 Some((kw::Result(ident.span()), generic.lt_token, generic.gt_token));
David Tolnayc071b892020-03-18 16:59:53 -0700487 }
488 }
489 }
490 }
491 }
492 match parse_type(ret)? {
493 Type::Void(_) => Ok(None),
494 ty => Ok(Some(ty)),
495 }
496}