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