blob: 67bb52b2048ed193090d68c6b9de883306fd9db3 [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 }
181 let ret = match &foreign_fn.sig.output {
182 ReturnType::Default => None,
David Tolnay64181b12020-03-15 23:42:54 -0700183 ReturnType::Type(_, ty) => match parse_type(ty)? {
184 Type::Void(_) => None,
185 ty => Some(ty),
186 },
David Tolnay7db73692019-10-20 14:51:12 -0400187 };
188 let doc = attrs::parse_doc(&foreign_fn.attrs)?;
189 let fn_token = foreign_fn.sig.fn_token;
190 let ident = foreign_fn.sig.ident.clone();
191 let semi_token = foreign_fn.semi_token;
192 Ok(ExternFn {
193 doc,
194 fn_token,
195 ident,
196 receiver,
197 args,
198 ret,
199 semi_token,
200 })
201}
202
203fn parse_type(ty: &RustType) -> Result<Type> {
204 match &ty {
205 RustType::Reference(ty) => {
206 let inner = parse_type(&ty.elem)?;
207 let which = match &inner {
208 Type::Ident(ident) if ident == "str" => {
209 if ty.mutability.is_some() {
210 return Err(Error::new_spanned(ty, "unsupported type"));
211 } else {
212 Type::Str
213 }
214 }
215 _ => Type::Ref,
216 };
217 return Ok(which(Box::new(Ref {
218 ampersand: ty.and_token,
219 mutability: ty.mutability,
220 inner,
221 })));
222 }
223 RustType::Path(ty) => {
224 let path = &ty.path;
225 if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
226 let segment = &path.segments[0];
227 let ident = segment.ident.clone();
228 match &segment.arguments {
229 PathArguments::None => return Ok(Type::Ident(ident)),
230 PathArguments::AngleBracketed(generic) => {
231 if ident == "UniquePtr" && generic.args.len() == 1 {
232 if let GenericArgument::Type(arg) = &generic.args[0] {
233 let inner = parse_type(arg)?;
234 return Ok(Type::UniquePtr(Box::new(Ty1 {
235 name: ident,
236 langle: generic.lt_token,
237 inner,
238 rangle: generic.gt_token,
239 })));
240 }
241 } else if ident == "Box" && generic.args.len() == 1 {
242 if let GenericArgument::Type(arg) = &generic.args[0] {
243 let inner = parse_type(arg)?;
244 return Ok(Type::RustBox(Box::new(Ty1 {
245 name: ident,
246 langle: generic.lt_token,
247 inner,
248 rangle: generic.gt_token,
249 })));
250 }
251 }
252 }
253 PathArguments::Parenthesized(_) => {}
254 }
255 }
256 }
David Tolnayfb134ed2020-03-15 23:17:48 -0700257 RustType::Tuple(ty) if ty.elems.is_empty() => {
David Tolnayd0bb3642020-03-15 23:27:11 -0700258 return Ok(Type::Void(ty.paren_token.span));
David Tolnayfb134ed2020-03-15 23:17:48 -0700259 }
David Tolnay7db73692019-10-20 14:51:12 -0400260 _ => {}
261 }
262 Err(Error::new_spanned(ty, "unsupported type"))
263}
264
265fn check_reserved_name(ident: &Ident) -> Result<()> {
266 if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() {
267 Err(Error::new(ident.span(), "reserved name"))
268 } else {
269 Ok(())
270 }
271}