Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 1 | // 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 | |
| 9 | use indexmap::map::IndexMap as HashMap; |
| 10 | use indexmap::set::IndexSet as HashSet; |
| 11 | |
| 12 | use crate::{ |
| 13 | conversion::{ |
| 14 | api::{Api, ApiName, NullPhase, StructDetails, SubclassName, TypedefKind, UnanalyzedApi}, |
| 15 | apivec::ApiVec, |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 16 | convert_error::LocatedConvertErrorFromRust, |
| 17 | ConvertError, ConvertErrorFromCpp, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 18 | }, |
| 19 | types::Namespace, |
| 20 | types::QualifiedName, |
| 21 | }; |
| 22 | use crate::{ |
| 23 | conversion::{ |
| 24 | convert_error::{ConvertErrorWithContext, ErrorContext}, |
| 25 | error_reporter::report_any_error, |
| 26 | }, |
| 27 | types::validate_ident_ok_for_cxx, |
| 28 | }; |
| 29 | use autocxx_parser::{IncludeCppConfig, RustPath}; |
| 30 | use syn::{parse_quote, Fields, Ident, Item, Type, TypePath, UseTree}; |
| 31 | |
| 32 | use super::{ |
| 33 | super::utilities::generate_utilities, bindgen_semantic_attributes::BindgenSemanticAttributes, |
| 34 | }; |
| 35 | |
| 36 | use super::parse_foreign_mod::ParseForeignMod; |
| 37 | |
| 38 | /// Parses a bindgen mod in order to understand the APIs within it. |
| 39 | pub(crate) struct ParseBindgen<'a> { |
| 40 | config: &'a IncludeCppConfig, |
| 41 | apis: ApiVec<NullPhase>, |
| 42 | } |
| 43 | |
| 44 | fn api_name(ns: &Namespace, id: Ident, attrs: &BindgenSemanticAttributes) -> ApiName { |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 45 | ApiName::new_with_cpp_name(ns, id.into(), attrs.get_original_name()) |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 46 | } |
| 47 | |
| 48 | pub(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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 55 | let ctx = ErrorContext::new_for_item(id.into()); |
| 56 | Err(ConvertErrorWithContext( |
| 57 | ConvertErrorFromCpp::InvalidIdent(e), |
| 58 | Some(ctx), |
| 59 | )) |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 60 | } |
| 61 | Ok(..) => Ok(api_name(ns, id, attrs)), |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | impl<'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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 78 | source_file_contents: &str, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 79 | ) -> Result<ApiVec<NullPhase>, ConvertError> { |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 80 | let items = Self::find_items_in_root(items).map_err(ConvertError::Cpp)?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 81 | if !self.config.exclude_utilities() { |
| 82 | generate_utilities(&mut self.apis, self.config); |
| 83 | } |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 84 | self.add_apis_from_config(source_file_contents) |
| 85 | .map_err(ConvertError::Rust)?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 86 | let root_ns = Namespace::new(); |
| 87 | self.parse_mod_items(items, root_ns); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 88 | self.confirm_all_generate_directives_obeyed() |
| 89 | .map_err(ConvertError::Cpp)?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 90 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 96 | fn add_apis_from_config( |
| 97 | &mut self, |
| 98 | source_file_contents: &str, |
| 99 | ) -> Result<(), LocatedConvertErrorFromRust> { |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 100 | self.apis |
| 101 | .extend(self.config.subclasses.iter().map(|sc| Api::Subclass { |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 102 | name: SubclassName::new(sc.subclass.clone().into()), |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 103 | superclass: QualifiedName::new_from_cpp_name(&sc.superclass), |
| 104 | })); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 105 | 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 Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 116 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 120 | name: ApiName::new_in_root_namespace(id.clone().into()), |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 121 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 130 | let name = ApiName::new_in_root_namespace(rust_id.clone().into()); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 131 | Api::ConcreteType { |
| 132 | name, |
| 133 | cpp_definition: cpp_definition.clone(), |
| 134 | rs_definition: None, |
| 135 | } |
| 136 | }), |
| 137 | ); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 138 | Ok(()) |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 139 | } |
| 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 168 | fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertErrorFromCpp> { |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 169 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 180 | _ => return Err(ConvertErrorFromCpp::UnexpectedOuterItem), |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 181 | } |
| 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 Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 221 | let mut err = annotations.check_for_fatal_attrs(&s.ident).err(); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 222 | 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 Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 233 | // |
| 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 238 | ConvertErrorFromCpp::ForwardDeclaredNestedType, |
| 239 | Some(ErrorContext::new_for_item(s.ident.into())), |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 240 | )); |
| 241 | } |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 242 | 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 Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 250 | layout: annotations.get_layout(), |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 251 | item: s.into(), |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 252 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 268 | item: e.into(), |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 269 | }; |
| 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 307 | let new_tyname = QualifiedName::new(ns, new_id.clone().into()); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 308 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 323 | ConvertErrorFromCpp::InfinitelyRecursiveTypedef(new_tyname), |
| 324 | Some(ErrorContext::new_for_item(new_id.clone().into())), |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 325 | )); |
| 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 334 | Box::new(Type::Path(old_path).into()), |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 335 | ), |
| 336 | old_tyname: Some(old_tyname), |
| 337 | analysis: (), |
| 338 | }); |
| 339 | break; |
| 340 | } |
| 341 | _ => { |
| 342 | return Err(ConvertErrorWithContext( |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 343 | ConvertErrorFromCpp::UnexpectedUseStatement( |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 344 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 355 | // 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 Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 372 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 380 | item: TypedefKind::Type(ity.into()), |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 381 | old_tyname: None, |
| 382 | analysis: (), |
| 383 | }); |
| 384 | Ok(()) |
| 385 | } |
| 386 | _ => Err(ConvertErrorWithContext( |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 387 | ConvertErrorFromCpp::UnexpectedItemInMod, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 388 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 407 | fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertErrorFromCpp> { |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 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) { |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 415 | return Err(ConvertErrorFromCpp::DidNotGenerateAnything( |
| 416 | generate_directive, |
| 417 | )); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 418 | } |
| 419 | } |
| 420 | Ok(()) |
| 421 | } |
| 422 | } |