autocxx_engine/conversion/parse/
parse_bindgen.rs1use indexmap::map::IndexMap as HashMap;
10use indexmap::set::IndexSet as HashSet;
11
12use crate::{
13 conversion::{
14 api::{Api, ApiName, NullPhase, StructDetails, SubclassName, TypedefKind, UnanalyzedApi},
15 apivec::ApiVec,
16 check_for_fatal_attrs,
17 convert_error::LocatedConvertErrorFromRust,
18 type_helpers::type_is_reference,
19 utilities::generate_utilities,
20 ConvertError, ConvertErrorFromCpp,
21 },
22 known_types, minisyn,
23 types::{Namespace, QualifiedName},
24 ParseCallbackResults,
25};
26use crate::{
27 conversion::{
28 convert_error::{ConvertErrorWithContext, ErrorContext},
29 error_reporter::report_any_error,
30 },
31 types::validate_ident_ok_for_cxx,
32};
33use autocxx_parser::{IncludeCppConfig, RustPath};
34use syn::{parse_quote, Fields, Ident, Item, Type, TypePath, UseTree};
35
36use super::parse_foreign_mod::ParseForeignMod;
37
38pub(crate) struct ParseBindgen<'a> {
40 config: &'a IncludeCppConfig,
41 apis: ApiVec<NullPhase>,
42 parse_callback_results: &'a ParseCallbackResults,
43}
44
45fn api_name(ns: &Namespace, id: Ident, callback_results: &ParseCallbackResults) -> ApiName {
46 let qn = QualifiedName::new(ns, minisyn::Ident(id.clone()));
47 ApiName::new_with_cpp_name(ns, id.into(), callback_results.get_original_name(&qn))
49}
50
51pub(crate) fn api_name_qualified(
52 ns: &Namespace,
53 id: Ident,
54 callback_results: &ParseCallbackResults,
55) -> Result<ApiName, ConvertErrorWithContext> {
56 match validate_ident_ok_for_cxx(&id.to_string()) {
57 Err(e) => {
58 let ctx = ErrorContext::new_for_item(id.into());
59 Err(ConvertErrorWithContext(
60 ConvertErrorFromCpp::InvalidIdent(e),
61 Some(ctx),
62 ))
63 }
64 Ok(..) => Ok(api_name(ns, id, callback_results)),
65 }
66}
67
68impl<'a> ParseBindgen<'a> {
69 pub(crate) fn new(
70 config: &'a IncludeCppConfig,
71 parse_callback_results: &'a ParseCallbackResults,
72 ) -> Self {
73 ParseBindgen {
74 config,
75 apis: ApiVec::new(),
76 parse_callback_results,
77 }
78 }
79
80 pub(crate) fn parse_items(
83 mut self,
84 items: &[Item],
85 source_file_contents: &str,
86 ) -> Result<ApiVec<NullPhase>, ConvertError> {
87 let items = Self::find_items_in_root(items).map_err(ConvertError::Cpp)?;
88 if !self.config.exclude_utilities() {
89 generate_utilities(&mut self.apis, self.config);
90 }
91 self.add_apis_from_config(source_file_contents)
92 .map_err(ConvertError::Rust)?;
93 let root_ns = Namespace::new();
94 self.parse_mod_items(items, root_ns);
95 self.confirm_all_generate_directives_obeyed()
96 .map_err(ConvertError::Cpp)?;
97 self.replace_extern_cpp_types();
98 Ok(self.apis)
99 }
100
101 fn add_apis_from_config(
104 &mut self,
105 source_file_contents: &str,
106 ) -> Result<(), LocatedConvertErrorFromRust> {
107 self.apis
108 .extend(self.config.subclasses.iter().map(|sc| Api::Subclass {
109 name: SubclassName::new(sc.subclass.clone().into()),
110 superclass: QualifiedName::new_from_cpp_name(&sc.superclass),
111 }));
112 for fun in &self.config.extern_rust_funs {
113 let id = fun.sig.ident.clone();
114 self.apis.push(Api::RustFn {
115 name: ApiName::new_in_root_namespace(id.into()),
116 details: fun.clone(),
117 deps: super::extern_fun_signatures::assemble_extern_fun_deps(
118 &fun.sig,
119 source_file_contents,
120 )?,
121 })
122 }
123 let unique_rust_types: HashSet<&RustPath> = self.config.rust_types.iter().collect();
124 self.apis.extend(unique_rust_types.into_iter().map(|path| {
125 let id = path.get_final_ident();
126 Api::RustType {
127 name: ApiName::new_in_root_namespace(id.clone().into()),
128 path: path.clone(),
129 }
130 }));
131 self.apis.extend(
132 self.config
133 .concretes
134 .0
135 .iter()
136 .map(|(cpp_definition, rust_id)| {
137 let name = ApiName::new_in_root_namespace(rust_id.clone().into());
138 Api::ConcreteType {
139 name,
140 cpp_definition: cpp_definition.clone(),
141 rs_definition: None,
142 }
143 }),
144 );
145 Ok(())
146 }
147
148 fn replace_extern_cpp_types(&mut self) {
151 let pod_requests: HashSet<_> = self.config.get_pod_requests().iter().collect();
152 let replacements: HashMap<_, _> = self
153 .config
154 .externs
155 .0
156 .iter()
157 .map(|(cpp_definition, details)| {
158 let qn = QualifiedName::new_from_cpp_name(cpp_definition);
159 let pod = pod_requests.contains(&qn.to_cpp_name());
160 (
161 qn.clone(),
162 Api::ExternCppType {
163 name: ApiName::new_from_qualified_name(qn),
164 details: details.clone(),
165 pod,
166 },
167 )
168 })
169 .collect();
170 self.apis
171 .retain(|api| !replacements.contains_key(api.name()));
172 self.apis.extend(replacements.into_iter().map(|(_, v)| v));
173 }
174
175 fn find_items_in_root(items: &[Item]) -> Result<Option<&Vec<Item>>, ConvertErrorFromCpp> {
176 for item in items {
177 if let Item::Mod(root_mod) = item {
178 assert!(root_mod.ident == "root");
182 if let Some((_, items)) = &root_mod.content {
183 return Ok(Some(items));
184 }
185 }
186 }
187 Ok(None)
188 }
189
190 fn parse_mod_items(&mut self, items: Option<&Vec<Item>>, ns: Namespace) {
193 let mut mod_converter = ParseForeignMod::new(ns.clone(), self.parse_callback_results);
196 let mut more_apis = ApiVec::new();
197 let empty_vec = vec![];
198 for item in items.unwrap_or(&empty_vec) {
199 report_any_error(&ns, &mut more_apis, || {
200 self.parse_item(item, &mut mod_converter, &ns)
201 });
202 }
203 self.apis.append(&mut more_apis);
204 mod_converter.finished(&mut self.apis);
205 }
206
207 fn parse_item(
208 &mut self,
209 item: &Item,
210 mod_converter: &mut ParseForeignMod,
211 ns: &Namespace,
212 ) -> Result<(), ConvertErrorWithContext> {
213 match item {
214 Item::ForeignMod(fm) => {
215 mod_converter.convert_foreign_mod_items(&fm.items);
216 Ok(())
217 }
218 Item::Struct(s) => {
219 if s.ident.to_string().ends_with("__bindgen_vtable") {
220 return Ok(());
221 }
222 let name = api_name_qualified(ns, s.ident.clone(), self.parse_callback_results)?;
225 if known_types().is_known_subtitute_type(&name.name) {
226 return Ok(());
230 }
231 let mut err = check_for_fatal_attrs(self.parse_callback_results, &name.name).err();
232 let api = if (ns.is_empty() && self.config.is_rust_type(&s.ident))
233 || known_types().is_known_type(&name.name)
234 {
235 None
236 } else if Self::spot_forward_declaration(&s.fields)
237 || (Self::spot_zero_length_struct(&s.fields) && err.is_some())
238 {
239 if err.is_none() && name.cpp_name().is_nested() {
249 err = Some(ConvertErrorWithContext(
250 ConvertErrorFromCpp::ForwardDeclaredNestedType,
251 Some(ErrorContext::new_for_item(s.ident.clone().into())),
252 ));
253 }
254 Some(UnanalyzedApi::ForwardDeclaration { name, err })
255 } else {
256 let has_rvalue_reference_fields = Self::spot_rvalue_reference_fields(&s.fields);
257 Some(UnanalyzedApi::Struct {
258 name,
259 details: Box::new(StructDetails {
260 item: s.clone().into(),
261 has_rvalue_reference_fields,
262 }),
263 analysis: (),
264 })
265 };
266 if let Some(api) = api {
267 if !self.config.is_on_blocklist(&api.name().to_cpp_name()) {
268 self.apis.push(api);
269 }
270 }
271 Ok(())
272 }
273 Item::Enum(e) => {
274 let api = UnanalyzedApi::Enum {
275 name: api_name_qualified(ns, e.ident.clone(), self.parse_callback_results)?,
276 item: e.clone().into(),
277 };
278 if !self.config.is_on_blocklist(&api.name().to_cpp_name()) {
279 self.apis.push(api);
280 }
281 Ok(())
282 }
283 Item::Impl(imp) => {
284 mod_converter.convert_impl_items(imp.clone());
292 Ok(())
293 }
294 Item::Mod(itm) => {
295 if let Some((_, items)) = &itm.content {
296 let new_ns = ns.push(itm.ident.to_string());
297 self.parse_mod_items(Some(items), new_ns);
298 }
299 Ok(())
300 }
301 Item::Use(use_item) => {
302 let mut segs = Vec::new();
303 let mut tree = &use_item.tree;
304 loop {
305 match tree {
306 UseTree::Path(up) => {
307 segs.push(up.ident.clone());
308 tree = &up.tree;
309 }
310 UseTree::Name(un) if un.ident == "root" => break, UseTree::Rename(urn) => {
313 let old_id = &urn.ident;
314 let new_id = &urn.rename;
315 if new_id == "bindgen_cchar16_t" {
316 return Ok(());
317 }
318 let new_tyname = QualifiedName::new(ns, new_id.clone().into());
319 assert!(segs.remove(0) == "self", "Path didn't start with self");
320 assert!(
321 segs.remove(0) == "super",
322 "Path didn't start with self::super"
323 );
324 let old_path: TypePath = parse_quote! {
329 #(#segs)::* :: #old_id
330 };
331 let old_tyname = QualifiedName::from_type_path(&old_path);
332 if new_tyname == old_tyname {
333 return Err(ConvertErrorWithContext(
334 ConvertErrorFromCpp::InfinitelyRecursiveTypedef(new_tyname),
335 Some(ErrorContext::new_for_item(new_id.clone().into())),
336 ));
337 }
338 self.apis.push(UnanalyzedApi::Typedef {
339 name: api_name(ns, new_id.clone(), self.parse_callback_results),
340 item: TypedefKind::Use(Box::new(Type::Path(old_path).into())),
341 old_tyname: Some(old_tyname),
342 analysis: (),
343 });
344 break;
345 }
346 _ => return Ok(()),
347 }
348 }
349 Ok(())
350 }
351 Item::Const(const_item) => {
352 let mut enum_type_name_valid = true;
356 if let Type::Path(p) = &*const_item.ty {
357 if let Some(p) = &p.path.segments.last() {
358 if validate_ident_ok_for_cxx(&p.ident.to_string()).is_err() {
359 enum_type_name_valid = false;
360 }
361 }
362 }
363 if enum_type_name_valid {
364 self.apis.push(UnanalyzedApi::Const {
365 name: api_name(ns, const_item.ident.clone(), self.parse_callback_results),
366 const_item: const_item.clone().into(),
367 });
368 }
369 Ok(())
370 }
371 Item::Type(ity) => {
372 self.apis.push(UnanalyzedApi::Typedef {
375 name: api_name(ns, ity.ident.clone(), self.parse_callback_results),
376 item: TypedefKind::Type(ity.clone().into()),
377 old_tyname: None,
378 analysis: (),
379 });
380 Ok(())
381 }
382 _ => Err(ConvertErrorWithContext(
383 ConvertErrorFromCpp::UnexpectedItemInMod,
384 None,
385 )),
386 }
387 }
388
389 fn spot_forward_declaration(s: &Fields) -> bool {
390 Self::spot_field(s, "_unused")
391 }
392
393 fn spot_zero_length_struct(s: &Fields) -> bool {
394 Self::spot_field(s, "_address")
395 }
396
397 fn spot_field(s: &Fields, desired_id: &str) -> bool {
398 s.iter()
399 .filter_map(|f| f.ident.as_ref())
400 .any(|id| id == desired_id)
401 }
402
403 fn spot_rvalue_reference_fields(s: &Fields) -> bool {
404 s.iter().any(|f| type_is_reference(&f.ty, true))
405 }
406
407 fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertErrorFromCpp> {
408 let api_names: HashSet<_> = self
409 .apis
410 .iter()
411 .map(|api| api.name().to_cpp_name())
412 .collect();
413 for generate_directive in self.config.must_generate_list() {
414 if !api_names.contains(&generate_directive) {
415 return Err(ConvertErrorFromCpp::DidNotGenerateAnything(
416 generate_directive,
417 ));
418 }
419 }
420 Ok(())
421 }
422}