blob: 2d4e3de8777d51a08f2c65c38b128bf49a122494 [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,
16 ConvertError,
17 },
18 types::Namespace,
19 types::QualifiedName,
20};
21use crate::{
22 conversion::{
23 convert_error::{ConvertErrorWithContext, ErrorContext},
24 error_reporter::report_any_error,
25 },
26 types::validate_ident_ok_for_cxx,
27};
28use autocxx_parser::{IncludeCppConfig, RustPath};
29use syn::{parse_quote, Fields, Ident, Item, Type, TypePath, UseTree};
30
31use super::{
32 super::utilities::generate_utilities, bindgen_semantic_attributes::BindgenSemanticAttributes,
33};
34
35use super::parse_foreign_mod::ParseForeignMod;
36
37/// Parses a bindgen mod in order to understand the APIs within it.
38pub(crate) struct ParseBindgen<'a> {
39 config: &'a IncludeCppConfig,
40 apis: ApiVec<NullPhase>,
41}
42
43fn api_name(ns: &Namespace, id: Ident, attrs: &BindgenSemanticAttributes) -> ApiName {
44 ApiName::new_with_cpp_name(ns, id, attrs.get_original_name())
45}
46
47pub(crate) fn api_name_qualified(
48 ns: &Namespace,
49 id: Ident,
50 attrs: &BindgenSemanticAttributes,
51) -> Result<ApiName, ConvertErrorWithContext> {
52 match validate_ident_ok_for_cxx(&id.to_string()) {
53 Err(e) => {
54 let ctx = ErrorContext::new_for_item(id);
55 Err(ConvertErrorWithContext(e, Some(ctx)))
56 }
57 Ok(..) => Ok(api_name(ns, id, attrs)),
58 }
59}
60
61impl<'a> ParseBindgen<'a> {
62 pub(crate) fn new(config: &'a IncludeCppConfig) -> Self {
63 ParseBindgen {
64 config,
65 apis: ApiVec::new(),
66 }
67 }
68
69 /// Parses items found in the `bindgen` output and returns a set of
70 /// `Api`s together with some other data.
71 pub(crate) fn parse_items(
72 mut self,
73 items: Vec<Item>,
74 ) -> Result<ApiVec<NullPhase>, ConvertError> {
75 let items = Self::find_items_in_root(items)?;
76 if !self.config.exclude_utilities() {
77 generate_utilities(&mut self.apis, self.config);
78 }
79 self.add_apis_from_config();
80 let root_ns = Namespace::new();
81 self.parse_mod_items(items, root_ns);
82 self.confirm_all_generate_directives_obeyed()?;
83 self.replace_extern_cpp_types();
84 Ok(self.apis)
85 }
86
87 /// Some API items are not populated from bindgen output, but instead
88 /// directly from items in the config.
89 fn add_apis_from_config(&mut self) {
90 self.apis
91 .extend(self.config.subclasses.iter().map(|sc| Api::Subclass {
92 name: SubclassName::new(sc.subclass.clone()),
93 superclass: QualifiedName::new_from_cpp_name(&sc.superclass),
94 }));
95 self.apis
96 .extend(self.config.extern_rust_funs.iter().map(|fun| {
97 let id = fun.sig.ident.clone();
98 Api::RustFn {
99 name: ApiName::new_in_root_namespace(id),
100 details: fun.clone(),
101 receiver: fun.receiver.as_ref().map(|receiver_id| {
102 QualifiedName::new(&Namespace::new(), receiver_id.clone())
103 }),
104 }
105 }));
106 let unique_rust_types: HashSet<&RustPath> = self.config.rust_types.iter().collect();
107 self.apis.extend(unique_rust_types.into_iter().map(|path| {
108 let id = path.get_final_ident();
109 Api::RustType {
110 name: ApiName::new_in_root_namespace(id.clone()),
111 path: path.clone(),
112 }
113 }));
114 self.apis.extend(
115 self.config
116 .concretes
117 .0
118 .iter()
119 .map(|(cpp_definition, rust_id)| {
120 let name = ApiName::new_in_root_namespace(rust_id.clone());
121 Api::ConcreteType {
122 name,
123 cpp_definition: cpp_definition.clone(),
124 rs_definition: None,
125 }
126 }),
127 );
128 }
129
130 /// We do this last, _after_ we've parsed all the APIs, because we might want to actually
131 /// replace some of the existing APIs (structs/enums/etc.) with replacements.
132 fn replace_extern_cpp_types(&mut self) {
133 let pod_requests: HashSet<_> = self.config.get_pod_requests().iter().collect();
134 let replacements: HashMap<_, _> = self
135 .config
136 .externs
137 .0
138 .iter()
139 .map(|(cpp_definition, details)| {
140 let qn = QualifiedName::new_from_cpp_name(cpp_definition);
141 let pod = pod_requests.contains(&qn.to_cpp_name());
142 (
143 qn.clone(),
144 Api::ExternCppType {
145 name: ApiName::new_from_qualified_name(qn),
146 details: details.clone(),
147 pod,
148 },
149 )
150 })
151 .collect();
152 self.apis
153 .retain(|api| !replacements.contains_key(api.name()));
154 self.apis.extend(replacements.into_iter().map(|(_, v)| v));
155 }
156
157 fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertError> {
158 for item in items {
159 match item {
160 Item::Mod(root_mod) => {
161 // With namespaces enabled, bindgen always puts everything
162 // in a mod called 'root'. We don't want to pass that
163 // onto cxx, so jump right into it.
164 assert!(root_mod.ident == "root");
165 if let Some((_, items)) = root_mod.content {
166 return Ok(items);
167 }
168 }
169 _ => return Err(ConvertError::UnexpectedOuterItem),
170 }
171 }
172 Ok(Vec::new())
173 }
174
175 /// Interpret the bindgen-generated .rs for a particular
176 /// mod, which corresponds to a C++ namespace.
177 fn parse_mod_items(&mut self, items: Vec<Item>, ns: Namespace) {
178 // This object maintains some state specific to this namespace, i.e.
179 // this particular mod.
180 let mut mod_converter = ParseForeignMod::new(ns.clone());
181 let mut more_apis = ApiVec::new();
182 for item in items {
183 report_any_error(&ns, &mut more_apis, || {
184 self.parse_item(item, &mut mod_converter, &ns)
185 });
186 }
187 self.apis.append(&mut more_apis);
188 mod_converter.finished(&mut self.apis);
189 }
190
191 fn parse_item(
192 &mut self,
193 item: Item,
194 mod_converter: &mut ParseForeignMod,
195 ns: &Namespace,
196 ) -> Result<(), ConvertErrorWithContext> {
197 match item {
198 Item::ForeignMod(fm) => {
199 mod_converter.convert_foreign_mod_items(fm.items);
200 Ok(())
201 }
202 Item::Struct(s) => {
203 if s.ident.to_string().ends_with("__bindgen_vtable") {
204 return Ok(());
205 }
206 let annotations = BindgenSemanticAttributes::new(&s.attrs);
207 // cxx::bridge can't cope with type aliases to generic
208 // types at the moment.
209 let name = api_name_qualified(ns, s.ident.clone(), &annotations)?;
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700210 let mut err = annotations.check_for_fatal_attrs(&s.ident).err();
Brian Silverman4e662aa2022-05-11 23:10:19 -0700211 let api = if ns.is_empty() && self.config.is_rust_type(&s.ident) {
212 None
213 } else if Self::spot_forward_declaration(&s.fields)
214 || (Self::spot_zero_length_struct(&s.fields) && err.is_some())
215 {
216 // Forward declarations are recorded especially because we can't
217 // store them in UniquePtr or similar.
218 // Templated forward declarations don't appear with an _unused field (which is what
219 // we spot in the previous clause) but instead with an _address field.
220 // So, solely in the case where we're storing up an error about such
221 // a templated type, we'll also treat such cases as forward declarations.
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700222 //
223 // We'll also at this point check for one specific problem with
224 // forward declarations.
225 if err.is_none() && name.cpp_name().contains("::") {
226 err = Some(ConvertErrorWithContext(
227 ConvertError::ForwardDeclaredNestedType,
228 Some(ErrorContext::new_for_item(s.ident)),
229 ));
230 }
Brian Silverman4e662aa2022-05-11 23:10:19 -0700231 Some(UnanalyzedApi::ForwardDeclaration { name, err })
232 } else {
233 let has_rvalue_reference_fields = s.fields.iter().any(|f| {
234 BindgenSemanticAttributes::new(&f.attrs).has_attr("rvalue_reference")
235 });
236 Some(UnanalyzedApi::Struct {
237 name,
238 details: Box::new(StructDetails {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700239 layout: annotations.get_layout(),
240 item: s,
241 has_rvalue_reference_fields,
242 }),
243 analysis: (),
244 })
245 };
246 if let Some(api) = api {
247 if !self.config.is_on_blocklist(&api.name().to_cpp_name()) {
248 self.apis.push(api);
249 }
250 }
251 Ok(())
252 }
253 Item::Enum(e) => {
254 let annotations = BindgenSemanticAttributes::new(&e.attrs);
255 let api = UnanalyzedApi::Enum {
256 name: api_name_qualified(ns, e.ident.clone(), &annotations)?,
257 item: e,
258 };
259 if !self.config.is_on_blocklist(&api.name().to_cpp_name()) {
260 self.apis.push(api);
261 }
262 Ok(())
263 }
264 Item::Impl(imp) => {
265 // We *mostly* ignore all impl blocks generated by bindgen.
266 // Methods also appear in 'extern "C"' blocks which
267 // we will convert instead. At that time we'll also construct
268 // synthetic impl blocks.
269 // We do however record which methods were spotted, since
270 // we have no other way of working out which functions are
271 // static methods vs plain functions.
272 mod_converter.convert_impl_items(imp);
273 Ok(())
274 }
275 Item::Mod(itm) => {
276 if let Some((_, items)) = itm.content {
277 let new_ns = ns.push(itm.ident.to_string());
278 self.parse_mod_items(items, new_ns);
279 }
280 Ok(())
281 }
282 Item::Use(use_item) => {
283 let mut segs = Vec::new();
284 let mut tree = &use_item.tree;
285 loop {
286 match tree {
287 UseTree::Path(up) => {
288 segs.push(up.ident.clone());
289 tree = &up.tree;
290 }
291 UseTree::Name(un) if un.ident == "root" => break, // we do not add this to any API since we generate equivalent
292 // use statements in our codegen phase.
293 UseTree::Rename(urn) => {
294 let old_id = &urn.ident;
295 let new_id = &urn.rename;
296 let new_tyname = QualifiedName::new(ns, new_id.clone());
297 assert!(segs.remove(0) == "self", "Path didn't start with self");
298 assert!(
299 segs.remove(0) == "super",
300 "Path didn't start with self::super"
301 );
302 // This is similar to the path encountered within 'tree'
303 // but without the self::super prefix which is unhelpful
304 // in our output mod, because we prefer relative paths
305 // (we're nested in another mod)
306 let old_path: TypePath = parse_quote! {
307 #(#segs)::* :: #old_id
308 };
309 let old_tyname = QualifiedName::from_type_path(&old_path);
310 if new_tyname == old_tyname {
311 return Err(ConvertErrorWithContext(
312 ConvertError::InfinitelyRecursiveTypedef(new_tyname),
313 Some(ErrorContext::new_for_item(new_id.clone())),
314 ));
315 }
316 let annotations = BindgenSemanticAttributes::new(&use_item.attrs);
317 self.apis.push(UnanalyzedApi::Typedef {
318 name: api_name(ns, new_id.clone(), &annotations),
319 item: TypedefKind::Use(
320 parse_quote! {
321 pub use #old_path as #new_id;
322 },
323 Box::new(Type::Path(old_path)),
324 ),
325 old_tyname: Some(old_tyname),
326 analysis: (),
327 });
328 break;
329 }
330 _ => {
331 return Err(ConvertErrorWithContext(
332 ConvertError::UnexpectedUseStatement(
333 segs.into_iter().last().map(|i| i.to_string()),
334 ),
335 None,
336 ))
337 }
338 }
339 }
340 Ok(())
341 }
342 Item::Const(const_item) => {
343 let annotations = BindgenSemanticAttributes::new(&const_item.attrs);
344 self.apis.push(UnanalyzedApi::Const {
345 name: api_name(ns, const_item.ident.clone(), &annotations),
346 const_item,
347 });
348 Ok(())
349 }
350 Item::Type(ity) => {
351 let annotations = BindgenSemanticAttributes::new(&ity.attrs);
352 // It's known that sometimes bindgen will give us duplicate typedefs with the
353 // same name - see test_issue_264.
354 self.apis.push(UnanalyzedApi::Typedef {
355 name: api_name(ns, ity.ident.clone(), &annotations),
356 item: TypedefKind::Type(ity),
357 old_tyname: None,
358 analysis: (),
359 });
360 Ok(())
361 }
362 _ => Err(ConvertErrorWithContext(
363 ConvertError::UnexpectedItemInMod,
364 None,
365 )),
366 }
367 }
368
369 fn spot_forward_declaration(s: &Fields) -> bool {
370 Self::spot_field(s, "_unused")
371 }
372
373 fn spot_zero_length_struct(s: &Fields) -> bool {
374 Self::spot_field(s, "_address")
375 }
376
377 fn spot_field(s: &Fields, desired_id: &str) -> bool {
378 s.iter()
379 .filter_map(|f| f.ident.as_ref())
380 .any(|id| id == desired_id)
381 }
382
383 fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertError> {
384 let api_names: HashSet<_> = self
385 .apis
386 .iter()
387 .map(|api| api.name().to_cpp_name())
388 .collect();
389 for generate_directive in self.config.must_generate_list() {
390 if !api_names.contains(&generate_directive) {
391 return Err(ConvertError::DidNotGenerateAnything(generate_directive));
392 }
393 }
394 Ok(())
395 }
396}