Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame^] | 1 | // Copyright 2021 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 | |
| 11 | use syn::Ident; |
| 12 | |
| 13 | use crate::{ |
| 14 | conversion::{ |
| 15 | api::{Api, SubclassName}, |
| 16 | apivec::ApiVec, |
| 17 | error_reporter::convert_item_apis, |
| 18 | ConvertError, |
| 19 | }, |
| 20 | types::{validate_ident_ok_for_cxx, QualifiedName}, |
| 21 | }; |
| 22 | |
| 23 | use super::fun::FnPhase; |
| 24 | |
| 25 | /// Do some final checks that the names we've come up with can be represented |
| 26 | /// within cxx. |
| 27 | pub(crate) fn check_names(apis: ApiVec<FnPhase>) -> ApiVec<FnPhase> { |
| 28 | // If any items have names which can't be represented by cxx, |
| 29 | // abort. This check should ideally be done at the times we fill in the |
| 30 | // `name` field of each `api` in the first place, at parse time, though |
| 31 | // as the `name` field of each API may change during various analysis phases, |
| 32 | // currently it seems better to do it here to ensure we respect |
| 33 | // the output of any such changes. |
| 34 | let mut intermediate = ApiVec::new(); |
| 35 | convert_item_apis(apis, &mut intermediate, |api| match api { |
| 36 | Api::Typedef { ref name, .. } |
| 37 | | Api::ForwardDeclaration { ref name, .. } |
| 38 | | Api::OpaqueTypedef { ref name, .. } |
| 39 | | Api::Const { ref name, .. } |
| 40 | | Api::Enum { ref name, .. } |
| 41 | | Api::Struct { ref name, .. } => { |
| 42 | validate_all_segments_ok_for_cxx(name.name.segment_iter())?; |
| 43 | if let Some(cpp_name) = name.cpp_name_if_present() { |
| 44 | // The C++ name might itself be outer_type::inner_type and thus may |
| 45 | // have multiple segments. |
| 46 | validate_all_segments_ok_for_cxx( |
| 47 | QualifiedName::new_from_cpp_name(cpp_name).segment_iter(), |
| 48 | )?; |
| 49 | } |
| 50 | Ok(Box::new(std::iter::once(api))) |
| 51 | } |
| 52 | Api::Subclass { |
| 53 | name: SubclassName(ref name), |
| 54 | ref superclass, |
| 55 | } => { |
| 56 | validate_all_segments_ok_for_cxx(name.name.segment_iter())?; |
| 57 | validate_all_segments_ok_for_cxx(superclass.segment_iter())?; |
| 58 | Ok(Box::new(std::iter::once(api))) |
| 59 | } |
| 60 | Api::Function { ref name, .. } => { |
| 61 | // we don't handle function names here because |
| 62 | // the function analysis does an equivalent check. Instead of just rejecting |
| 63 | // the function, it creates a wrapper function instead with a more |
| 64 | // palatable name. That's preferable to rejecting the API entirely. |
| 65 | validate_all_segments_ok_for_cxx(name.name.segment_iter())?; |
| 66 | Ok(Box::new(std::iter::once(api))) |
| 67 | } |
| 68 | Api::ConcreteType { .. } |
| 69 | | Api::CType { .. } |
| 70 | | Api::StringConstructor { .. } |
| 71 | | Api::RustType { .. } |
| 72 | | Api::RustSubclassFn { .. } |
| 73 | | Api::RustFn { .. } |
| 74 | | Api::SubclassTraitItem { .. } |
| 75 | | Api::ExternCppType { .. } |
| 76 | | Api::IgnoredItem { .. } => Ok(Box::new(std::iter::once(api))), |
| 77 | }); |
| 78 | |
| 79 | // Reject any names which are duplicates within the cxx bridge mod, |
| 80 | // that has a flat namespace. |
| 81 | let mut names_found: HashMap<Ident, Vec<String>> = HashMap::new(); |
| 82 | for api in intermediate.iter() { |
| 83 | let my_name = api.cxxbridge_name(); |
| 84 | if let Some(name) = my_name { |
| 85 | let e = names_found.entry(name).or_default(); |
| 86 | e.push(api.name_info().name.to_string()); |
| 87 | } |
| 88 | } |
| 89 | let mut results = ApiVec::new(); |
| 90 | convert_item_apis(intermediate, &mut results, |api| { |
| 91 | let my_name = api.cxxbridge_name(); |
| 92 | if let Some(name) = my_name { |
| 93 | let symbols_for_this_name = names_found.entry(name).or_default(); |
| 94 | if symbols_for_this_name.len() > 1usize { |
| 95 | Err(ConvertError::DuplicateCxxBridgeName( |
| 96 | symbols_for_this_name.clone(), |
| 97 | )) |
| 98 | } else { |
| 99 | Ok(Box::new(std::iter::once(api))) |
| 100 | } |
| 101 | } else { |
| 102 | Ok(Box::new(std::iter::once(api))) |
| 103 | } |
| 104 | }); |
| 105 | results |
| 106 | } |
| 107 | |
| 108 | fn validate_all_segments_ok_for_cxx( |
| 109 | items: impl Iterator<Item = String>, |
| 110 | ) -> Result<(), ConvertError> { |
| 111 | for seg in items { |
| 112 | validate_ident_ok_for_cxx(&seg)?; |
| 113 | } |
| 114 | Ok(()) |
| 115 | } |