Skip to content

Commit

Permalink
Merge pull request #734 from google/bindgen-annotation-macro
Browse files Browse the repository at this point in the history
Regularize bindgen annotations.
  • Loading branch information
adetaylor authored Jan 9, 2022
2 parents a2fbee6 + b4f5a59 commit 62955ce
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 179 deletions.
2 changes: 1 addition & 1 deletion engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ log = "0.4"
proc-macro2 = "1.0.11"
quote = "1.0"
indoc = "1.0"
autocxx-bindgen = "=0.59.9"
autocxx-bindgen = "=0.59.10"
#autocxx-bindgen = { git = "https://github.com/adetaylor/rust-bindgen", branch = "rvalue-references" }
itertools = "0.10"
cc = { version = "1.0", optional = true }
Expand Down
40 changes: 0 additions & 40 deletions engine/src/conversion/analysis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use syn::{Attribute, Ident};

use crate::conversion::convert_error::ErrorContext;

use super::{convert_error::ConvertErrorWithContext, ConvertError};

pub(crate) mod abstract_types;
pub(crate) mod casts;
pub(crate) mod ctypes;
Expand All @@ -30,37 +24,3 @@ pub(crate) mod tdef;
mod type_converter;

pub(crate) use name_check::check_names;

// Remove `bindgen_` attributes. They don't have a corresponding macro defined anywhere,
// so they will cause compilation errors if we leave them in.
// We may return an error if one of the bindgen attributes shows that the
// item can't be processed.
fn remove_bindgen_attrs(
attrs: &mut Vec<Attribute>,
id: Ident,
) -> Result<(), ConvertErrorWithContext> {
if has_attr(attrs, "bindgen_unused_template_param") {
return Err(ConvertErrorWithContext(
ConvertError::UnusedTemplateParam,
Some(ErrorContext::Item(id)),
));
}

fn is_bindgen_attr(attr: &Attribute) -> bool {
let segments = &attr.path.segments;
segments.len() == 1
&& segments
.first()
.unwrap()
.ident
.to_string()
.starts_with("bindgen_")
}

attrs.retain(|a| !is_bindgen_attr(a));
Ok(())
}

fn has_attr(attrs: &[Attribute], attr_name: &str) -> bool {
attrs.iter().any(|at| at.path.is_ident(attr_name))
}
7 changes: 5 additions & 2 deletions engine/src/conversion/analysis/pod/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use crate::{
api::{AnalysisPhase, Api, ApiName, CppVisibility, StructDetails, TypeKind, UnanalyzedApi},
convert_error::{ConvertErrorWithContext, ErrorContext},
error_reporter::convert_apis,
parse::BindgenSemanticAttributes,
ConvertError,
},
types::{Namespace, QualifiedName},
Expand Down Expand Up @@ -115,7 +116,8 @@ fn analyze_enum(
name: ApiName,
mut item: ItemEnum,
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
super::remove_bindgen_attrs(&mut item.attrs, name.name.get_final_ident())?;
let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs);
metadata.check_for_fatal_attrs(&name.name.get_final_ident())?;
Ok(Box::new(std::iter::once(Api::Enum { name, item })))
}

Expand All @@ -134,7 +136,8 @@ fn analyze_struct(
Some(ErrorContext::Item(id)),
));
}
super::remove_bindgen_attrs(&mut details.item.attrs, id.clone())?;
let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs);
metadata.check_for_fatal_attrs(&id)?;
let bases = get_bases(&details.item);
let mut field_deps = HashSet::new();
let type_kind = if byvalue_checker.is_pod(&name.name) {
Expand Down
7 changes: 3 additions & 4 deletions engine/src/conversion/analysis/tdef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ use crate::{
api::{AnalysisPhase, Api, ApiName, TypedefKind, UnanalyzedApi},
convert_error::{ConvertErrorWithContext, ErrorContext},
error_reporter::convert_apis,
parse::BindgenSemanticAttributes,
ConvertError,
},
types::QualifiedName,
};

use super::remove_bindgen_attrs;

pub(crate) struct TypedefAnalysis {
pub(crate) kind: TypedefKind,
pub(crate) deps: HashSet<QualifiedName>,
Expand Down Expand Up @@ -92,8 +91,8 @@ fn get_replacement_typedef(
extra_apis: &mut Vec<UnanalyzedApi>,
) -> Result<Api<TypedefPhase>, ConvertErrorWithContext> {
let mut converted_type = ity.clone();
let id = ity.ident.clone();
remove_bindgen_attrs(&mut converted_type.attrs, id)?;
let metadata = BindgenSemanticAttributes::new_retaining_others(&mut converted_type.attrs);
metadata.check_for_fatal_attrs(&ity.ident)?;
let type_conversion_results = type_converter.convert_type(
(*ity.ty).clone(),
name.name.get_namespace(),
Expand Down
193 changes: 193 additions & 0 deletions engine/src/conversion/parse/bindgen_semantic_attributes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use proc_macro2::{Ident, TokenStream};
use syn::{
parenthesized,
parse::{Parse, Parser},
Attribute, LitStr,
};

use crate::conversion::{
api::{CppVisibility, Layout, References, Virtualness},
convert_error::{ConvertErrorWithContext, ErrorContext},
ConvertError,
};

/// The set of all annotations that autocxx_bindgen has added
/// for our benefit.
#[derive(Debug)]
pub(crate) struct BindgenSemanticAttributes(Vec<BindgenSemanticAttribute>);

impl BindgenSemanticAttributes {
// Remove `bindgen_` attributes. They don't have a corresponding macro defined anywhere,
// so they will cause compilation errors if we leave them in.
// We may return an error if one of the bindgen attributes shows that the
// item can't be processed.
pub(crate) fn new_retaining_others(attrs: &mut Vec<Attribute>) -> Self {
let metadata = Self::new(attrs);
attrs.retain(|a| !(a.path.segments.last().unwrap().ident == "cpp_semantics"));
metadata
}

pub(crate) fn new(attrs: &[Attribute]) -> Self {
Self(
attrs
.iter()
.filter_map(|attr| {
if attr.path.segments.last().unwrap().ident == "cpp_semantics" {
let r: Result<BindgenSemanticAttribute, syn::Error> = attr.parse_args();
r.ok()
} else {
None
}
})
.collect(),
)
}

/// Some attributes indicate we can never handle a given item. Check for those.
pub(crate) fn check_for_fatal_attrs(
&self,
id_for_context: &Ident,
) -> Result<(), ConvertErrorWithContext> {
if self.has_attr("unused_template_param") {
Err(ConvertErrorWithContext(
ConvertError::UnusedTemplateParam,
Some(ErrorContext::Item(id_for_context.clone())),
))
} else {
Ok(())
}
}

/// Whether the given attribute is present.
pub(super) fn has_attr(&self, attr_name: &str) -> bool {
self.0.iter().any(|a| a.is_ident(attr_name))
}

/// The C++ visibility of the item.
pub(super) fn get_cpp_visibility(&self) -> CppVisibility {
if self.has_attr("visibility_private") {
CppVisibility::Private
} else if self.has_attr("visibility_protected") {
CppVisibility::Protected
} else {
CppVisibility::Public
}
}

/// Whether the item is virtual.
pub(super) fn get_virtualness(&self) -> Virtualness {
if self.has_attr("pure_virtual") {
Virtualness::PureVirtual
} else if self.has_attr("bindgen_virtual") {
Virtualness::Virtual
} else {
Virtualness::None
}
}

fn parse_if_present<T: Parse>(&self, annotation: &str) -> Option<T> {
self.0
.iter()
.find(|a| a.is_ident(annotation))
.map(|a| a.parse_args().unwrap())
}

fn string_if_present(&self, annotation: &str) -> Option<String> {
let ls: Option<LitStr> = self.parse_if_present(annotation);
ls.map(|ls| ls.value())
}

/// The in-memory layout of the item.
pub(super) fn get_layout(&self) -> Option<Layout> {
self.parse_if_present("layout")
}

/// The original C++ name, which bindgen may have changed.
pub(super) fn get_original_name(&self) -> Option<String> {
self.string_if_present("original_name")
}

fn get_bindgen_special_member_annotation(&self) -> Option<String> {
self.string_if_present("special_member")
}

/// Whether this is a move constructor.
pub(super) fn is_move_constructor(&self) -> bool {
self.get_bindgen_special_member_annotation()
.map_or(false, |val| val == "move_ctor")
}

/// Any reference parameters or return values.
pub(super) fn get_reference_parameters_and_return(&self) -> References {
let mut results = References::default();
for a in &self.0 {
if a.is_ident("ret_type_reference") {
results.ref_return = true;
} else if a.is_ident("ret_type_rvalue_reference") {
results.rvalue_ref_return = true;
} else if a.is_ident("arg_type_reference") {
let r: Result<Ident, syn::Error> = a.parse_args();
if let Ok(ls) = r {
results.ref_params.insert(ls);
}
} else if a.is_ident("arg_type_rvalue_reference") {
let r: Result<Ident, syn::Error> = a.parse_args();
if let Ok(ls) = r {
results.rvalue_ref_params.insert(ls);
}
}
}
results
}
}

#[derive(Debug)]
struct BindgenSemanticAttribute {
annotation_name: Ident,
body: Option<TokenStream>,
}

impl BindgenSemanticAttribute {
fn is_ident(&self, name: &str) -> bool {
self.annotation_name == name
}

fn parse_args<T: Parse>(&self) -> Result<T, syn::Error> {
T::parse.parse2(self.body.as_ref().unwrap().clone())
}
}

impl Parse for BindgenSemanticAttribute {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let annotation_name: Ident = input.parse()?;
if input.peek(syn::token::Paren) {
let body_contents;
parenthesized!(body_contents in input);
Ok(Self {
annotation_name,
body: Some(body_contents.parse()?),
})
} else if !input.is_empty() {
Err(input.error("expected nothing"))
} else {
Ok(Self {
annotation_name,
body: None,
})
}
}
}
2 changes: 2 additions & 0 deletions engine/src/conversion/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

mod bindgen_semantic_attributes;
mod parse_bindgen;
mod parse_foreign_mod;

pub(crate) use bindgen_semantic_attributes::BindgenSemanticAttributes;
pub(crate) use parse_bindgen::ParseBindgen;
Loading

0 comments on commit 62955ce

Please sign in to comment.