blob: f49914540867e5f905290844aca2fa1a7161bdce [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use 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,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070016 convert_error::LocatedConvertErrorFromRust,
17 ConvertError, ConvertErrorFromCpp,
Brian Silverman4e662aa2022-05-11 23:10:19 -070018 },
19 types::Namespace,
20 types::QualifiedName,
21};
22use crate::{
23 conversion::{
24 convert_error::{ConvertErrorWithContext, ErrorContext},
25 error_reporter::report_any_error,
26 },
27 types::validate_ident_ok_for_cxx,
28};
29use autocxx_parser::{IncludeCppConfig, RustPath};
30use syn::{parse_quote, Fields, Ident, Item, Type, TypePath, UseTree};
31
32use super::{
33 super::utilities::generate_utilities, bindgen_semantic_attributes::BindgenSemanticAttributes,
34};
35
36use super::parse_foreign_mod::ParseForeignMod;
37
38/// Parses a bindgen mod in order to understand the APIs within it.
39pub(crate) struct ParseBindgen<'a> {
40 config: &'a IncludeCppConfig,
41 apis: ApiVec<NullPhase>,
42}
43
44fn api_name(ns: &Namespace, id: Ident, attrs: &BindgenSemanticAttributes) -> ApiName {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070045 ApiName::new_with_cpp_name(ns, id.into(), attrs.get_original_name())
Brian Silverman4e662aa2022-05-11 23:10:19 -070046}
47
48pub(crate) fn api_name_qualified(
49 ns: &Namespace,
50 id: Ident,
51 attrs: &BindgenSemanticAttributes,
52) -> Result<ApiName, ConvertErrorWithContext> {
53 match validate_ident_ok_for_cxx(&id.to_string()) {
54 Err(e) => {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070055 let ctx = ErrorContext::new_for_item(id.into());
56 Err(ConvertErrorWithContext(
57 ConvertErrorFromCpp::InvalidIdent(e),
58 Some(ctx),
59 ))
Brian Silverman4e662aa2022-05-11 23:10:19 -070060 }
61 Ok(..) => Ok(api_name(ns, id, attrs)),
62 }
63}
64
65impl<'a> ParseBindgen<'a> {
66 pub(crate) fn new(config: &'a IncludeCppConfig) -> Self {
67 ParseBindgen {
68 config,
69 apis: ApiVec::new(),
70 }
71 }
72
73 /// Parses items found in the `bindgen` output and returns a set of
74 /// `Api`s together with some other data.
75 pub(crate) fn parse_items(
76 mut self,
77 items: Vec<Item>,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070078 source_file_contents: &str,
Brian Silverman4e662aa2022-05-11 23:10:19 -070079 ) -> Result<ApiVec<NullPhase>, ConvertError> {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070080 let items = Self::find_items_in_root(items).map_err(ConvertError::Cpp)?;
Brian Silverman4e662aa2022-05-11 23:10:19 -070081 if !self.config.exclude_utilities() {
82 generate_utilities(&mut self.apis, self.config);
83 }
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070084 self.add_apis_from_config(source_file_contents)
85 .map_err(ConvertError::Rust)?;
Brian Silverman4e662aa2022-05-11 23:10:19 -070086 let root_ns = Namespace::new();
87 self.parse_mod_items(items, root_ns);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070088 self.confirm_all_generate_directives_obeyed()
89 .map_err(ConvertError::Cpp)?;
Brian Silverman4e662aa2022-05-11 23:10:19 -070090 self.replace_extern_cpp_types();
91 Ok(self.apis)
92 }
93
94 /// Some API items are not populated from bindgen output, but instead
95 /// directly from items in the config.
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070096 fn add_apis_from_config(
97 &mut self,
98 source_file_contents: &str,
99 ) -> Result<(), LocatedConvertErrorFromRust> {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700100 self.apis
101 .extend(self.config.subclasses.iter().map(|sc| Api::Subclass {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700102 name: SubclassName::new(sc.subclass.clone().into()),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700103 superclass: QualifiedName::new_from_cpp_name(&sc.superclass),
104 }));
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700105 for fun in &self.config.extern_rust_funs {
106 let id = fun.sig.ident.clone();
107 self.apis.push(Api::RustFn {
108 name: ApiName::new_in_root_namespace(id.into()),
109 details: fun.clone(),
110 deps: super::extern_fun_signatures::assemble_extern_fun_deps(
111 &fun.sig,
112 source_file_contents,
113 )?,
114 })
115 }
Brian Silverman4e662aa2022-05-11 23:10:19 -0700116 let unique_rust_types: HashSet<&RustPath> = self.config.rust_types.iter().collect();
117 self.apis.extend(unique_rust_types.into_iter().map(|path| {
118 let id = path.get_final_ident();
119 Api::RustType {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700120 name: ApiName::new_in_root_namespace(id.clone().into()),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700121 path: path.clone(),
122 }
123 }));
124 self.apis.extend(
125 self.config
126 .concretes
127 .0
128 .iter()
129 .map(|(cpp_definition, rust_id)| {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700130 let name = ApiName::new_in_root_namespace(rust_id.clone().into());
Brian Silverman4e662aa2022-05-11 23:10:19 -0700131 Api::ConcreteType {
132 name,
133 cpp_definition: cpp_definition.clone(),
134 rs_definition: None,
135 }
136 }),
137 );
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700138 Ok(())
Brian Silverman4e662aa2022-05-11 23:10:19 -0700139 }
140
141 /// We do this last, _after_ we've parsed all the APIs, because we might want to actually
142 /// replace some of the existing APIs (structs/enums/etc.) with replacements.
143 fn replace_extern_cpp_types(&mut self) {
144 let pod_requests: HashSet<_> = self.config.get_pod_requests().iter().collect();
145 let replacements: HashMap<_, _> = self
146 .config
147 .externs
148 .0
149 .iter()
150 .map(|(cpp_definition, details)| {
151 let qn = QualifiedName::new_from_cpp_name(cpp_definition);
152 let pod = pod_requests.contains(&qn.to_cpp_name());
153 (
154 qn.clone(),
155 Api::ExternCppType {
156 name: ApiName::new_from_qualified_name(qn),
157 details: details.clone(),
158 pod,
159 },
160 )
161 })
162 .collect();
163 self.apis
164 .retain(|api| !replacements.contains_key(api.name()));
165 self.apis.extend(replacements.into_iter().map(|(_, v)| v));
166 }
167
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700168 fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertErrorFromCpp> {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700169 for item in items {
170 match item {
171 Item::Mod(root_mod) => {
172 // With namespaces enabled, bindgen always puts everything
173 // in a mod called 'root'. We don't want to pass that
174 // onto cxx, so jump right into it.
175 assert!(root_mod.ident == "root");
176 if let Some((_, items)) = root_mod.content {
177 return Ok(items);
178 }
179 }
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700180 _ => return Err(ConvertErrorFromCpp::UnexpectedOuterItem),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700181 }
182 }
183 Ok(Vec::new())
184 }
185
186 /// Interpret the bindgen-generated .rs for a particular
187 /// mod, which corresponds to a C++ namespace.
188 fn parse_mod_items(&mut self, items: Vec<Item>, ns: Namespace) {
189 // This object maintains some state specific to this namespace, i.e.
190 // this particular mod.
191 let mut mod_converter = ParseForeignMod::new(ns.clone());
192 let mut more_apis = ApiVec::new();
193 for item in items {
194 report_any_error(&ns, &mut more_apis, || {
195 self.parse_item(item, &mut mod_converter, &ns)
196 });
197 }
198 self.apis.append(&mut more_apis);
199 mod_converter.finished(&mut self.apis);
200 }
201
202 fn parse_item(
203 &mut self,
204 item: Item,
205 mod_converter: &mut ParseForeignMod,
206 ns: &Namespace,
207 ) -> Result<(), ConvertErrorWithContext> {
208 match item {
209 Item::ForeignMod(fm) => {
210 mod_converter.convert_foreign_mod_items(fm.items);
211 Ok(())
212 }
213 Item::Struct(s) => {
214 if s.ident.to_string().ends_with("__bindgen_vtable") {
215 return Ok(());
216 }
217 let annotations = BindgenSemanticAttributes::new(&s.attrs);
218 // cxx::bridge can't cope with type aliases to generic
219 // types at the moment.
220 let name = api_name_qualified(ns, s.ident.clone(), &annotations)?;
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700221 let mut err = annotations.check_for_fatal_attrs(&s.ident).err();
Brian Silverman4e662aa2022-05-11 23:10:19 -0700222 let api = if ns.is_empty() && self.config.is_rust_type(&s.ident) {
223 None
224 } else if Self::spot_forward_declaration(&s.fields)
225 || (Self::spot_zero_length_struct(&s.fields) && err.is_some())
226 {
227 // Forward declarations are recorded especially because we can't
228 // store them in UniquePtr or similar.
229 // Templated forward declarations don't appear with an _unused field (which is what
230 // we spot in the previous clause) but instead with an _address field.
231 // So, solely in the case where we're storing up an error about such
232 // a templated type, we'll also treat such cases as forward declarations.
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700233 //
234 // We'll also at this point check for one specific problem with
235 // forward declarations.
236 if err.is_none() && name.cpp_name().contains("::") {
237 err = Some(ConvertErrorWithContext(
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700238 ConvertErrorFromCpp::ForwardDeclaredNestedType,
239 Some(ErrorContext::new_for_item(s.ident.into())),
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700240 ));
241 }
Brian Silverman4e662aa2022-05-11 23:10:19 -0700242 Some(UnanalyzedApi::ForwardDeclaration { name, err })
243 } else {
244 let has_rvalue_reference_fields = s.fields.iter().any(|f| {
245 BindgenSemanticAttributes::new(&f.attrs).has_attr("rvalue_reference")
246 });
247 Some(UnanalyzedApi::Struct {
248 name,
249 details: Box::new(StructDetails {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700250 layout: annotations.get_layout(),
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700251 item: s.into(),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700252 has_rvalue_reference_fields,
253 }),
254 analysis: (),
255 })
256 };
257 if let Some(api) = api {
258 if !self.config.is_on_blocklist(&api.name().to_cpp_name()) {
259 self.apis.push(api);
260 }
261 }
262 Ok(())
263 }
264 Item::Enum(e) => {
265 let annotations = BindgenSemanticAttributes::new(&e.attrs);
266 let api = UnanalyzedApi::Enum {
267 name: api_name_qualified(ns, e.ident.clone(), &annotations)?,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700268 item: e.into(),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700269 };
270 if !self.config.is_on_blocklist(&api.name().to_cpp_name()) {
271 self.apis.push(api);
272 }
273 Ok(())
274 }
275 Item::Impl(imp) => {
276 // We *mostly* ignore all impl blocks generated by bindgen.
277 // Methods also appear in 'extern "C"' blocks which
278 // we will convert instead. At that time we'll also construct
279 // synthetic impl blocks.
280 // We do however record which methods were spotted, since
281 // we have no other way of working out which functions are
282 // static methods vs plain functions.
283 mod_converter.convert_impl_items(imp);
284 Ok(())
285 }
286 Item::Mod(itm) => {
287 if let Some((_, items)) = itm.content {
288 let new_ns = ns.push(itm.ident.to_string());
289 self.parse_mod_items(items, new_ns);
290 }
291 Ok(())
292 }
293 Item::Use(use_item) => {
294 let mut segs = Vec::new();
295 let mut tree = &use_item.tree;
296 loop {
297 match tree {
298 UseTree::Path(up) => {
299 segs.push(up.ident.clone());
300 tree = &up.tree;
301 }
302 UseTree::Name(un) if un.ident == "root" => break, // we do not add this to any API since we generate equivalent
303 // use statements in our codegen phase.
304 UseTree::Rename(urn) => {
305 let old_id = &urn.ident;
306 let new_id = &urn.rename;
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700307 let new_tyname = QualifiedName::new(ns, new_id.clone().into());
Brian Silverman4e662aa2022-05-11 23:10:19 -0700308 assert!(segs.remove(0) == "self", "Path didn't start with self");
309 assert!(
310 segs.remove(0) == "super",
311 "Path didn't start with self::super"
312 );
313 // This is similar to the path encountered within 'tree'
314 // but without the self::super prefix which is unhelpful
315 // in our output mod, because we prefer relative paths
316 // (we're nested in another mod)
317 let old_path: TypePath = parse_quote! {
318 #(#segs)::* :: #old_id
319 };
320 let old_tyname = QualifiedName::from_type_path(&old_path);
321 if new_tyname == old_tyname {
322 return Err(ConvertErrorWithContext(
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700323 ConvertErrorFromCpp::InfinitelyRecursiveTypedef(new_tyname),
324 Some(ErrorContext::new_for_item(new_id.clone().into())),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700325 ));
326 }
327 let annotations = BindgenSemanticAttributes::new(&use_item.attrs);
328 self.apis.push(UnanalyzedApi::Typedef {
329 name: api_name(ns, new_id.clone(), &annotations),
330 item: TypedefKind::Use(
331 parse_quote! {
332 pub use #old_path as #new_id;
333 },
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700334 Box::new(Type::Path(old_path).into()),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700335 ),
336 old_tyname: Some(old_tyname),
337 analysis: (),
338 });
339 break;
340 }
341 _ => {
342 return Err(ConvertErrorWithContext(
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700343 ConvertErrorFromCpp::UnexpectedUseStatement(
Brian Silverman4e662aa2022-05-11 23:10:19 -0700344 segs.into_iter().last().map(|i| i.to_string()),
345 ),
346 None,
347 ))
348 }
349 }
350 }
351 Ok(())
352 }
353 Item::Const(const_item) => {
354 let annotations = BindgenSemanticAttributes::new(&const_item.attrs);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700355 // Bindgen generates const expressions for nested unnamed enums,
356 // but autcxx will refuse to expand those enums, making these consts
357 // invalid.
358 let mut enum_type_name_valid = true;
359 if let Type::Path(p) = &*const_item.ty {
360 if let Some(p) = &p.path.segments.last() {
361 if validate_ident_ok_for_cxx(&p.ident.to_string()).is_err() {
362 enum_type_name_valid = false;
363 }
364 }
365 }
366 if enum_type_name_valid {
367 self.apis.push(UnanalyzedApi::Const {
368 name: api_name(ns, const_item.ident.clone(), &annotations),
369 const_item: const_item.into(),
370 });
371 }
Brian Silverman4e662aa2022-05-11 23:10:19 -0700372 Ok(())
373 }
374 Item::Type(ity) => {
375 let annotations = BindgenSemanticAttributes::new(&ity.attrs);
376 // It's known that sometimes bindgen will give us duplicate typedefs with the
377 // same name - see test_issue_264.
378 self.apis.push(UnanalyzedApi::Typedef {
379 name: api_name(ns, ity.ident.clone(), &annotations),
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700380 item: TypedefKind::Type(ity.into()),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700381 old_tyname: None,
382 analysis: (),
383 });
384 Ok(())
385 }
386 _ => Err(ConvertErrorWithContext(
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700387 ConvertErrorFromCpp::UnexpectedItemInMod,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700388 None,
389 )),
390 }
391 }
392
393 fn spot_forward_declaration(s: &Fields) -> bool {
394 Self::spot_field(s, "_unused")
395 }
396
397 fn spot_zero_length_struct(s: &Fields) -> bool {
398 Self::spot_field(s, "_address")
399 }
400
401 fn spot_field(s: &Fields, desired_id: &str) -> bool {
402 s.iter()
403 .filter_map(|f| f.ident.as_ref())
404 .any(|id| id == desired_id)
405 }
406
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700407 fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertErrorFromCpp> {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700408 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) {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700415 return Err(ConvertErrorFromCpp::DidNotGenerateAnything(
416 generate_directive,
417 ));
Brian Silverman4e662aa2022-05-11 23:10:19 -0700418 }
419 }
420 Ok(())
421 }
422}