Skip to content

Commit b4f5a59

Browse files
committed
Regularize bindgen annotations.
Previously autocxx_bindgen added annotations which were not valid Rust code. It now generates annotations which correspond to a real autocxx macro, and thus can more plausibly be upstreamed to the real bindgen. This also extracts a better encapsulated framework for parsing these attributes.
1 parent a2fbee6 commit b4f5a59

File tree

11 files changed

+255
-179
lines changed

11 files changed

+255
-179
lines changed

engine/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ log = "0.4"
3636
proc-macro2 = "1.0.11"
3737
quote = "1.0"
3838
indoc = "1.0"
39-
autocxx-bindgen = "=0.59.9"
39+
autocxx-bindgen = "=0.59.10"
4040
#autocxx-bindgen = { git = "https://github.com./adetaylor/rust-bindgen", branch = "rvalue-references" }
4141
itertools = "0.10"
4242
cc = { version = "1.0", optional = true }

engine/src/conversion/analysis/mod.rs

-40
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use syn::{Attribute, Ident};
16-
17-
use crate::conversion::convert_error::ErrorContext;
18-
19-
use super::{convert_error::ConvertErrorWithContext, ConvertError};
20-
2115
pub(crate) mod abstract_types;
2216
pub(crate) mod casts;
2317
pub(crate) mod ctypes;
@@ -30,37 +24,3 @@ pub(crate) mod tdef;
3024
mod type_converter;
3125

3226
pub(crate) use name_check::check_names;
33-
34-
// Remove `bindgen_` attributes. They don't have a corresponding macro defined anywhere,
35-
// so they will cause compilation errors if we leave them in.
36-
// We may return an error if one of the bindgen attributes shows that the
37-
// item can't be processed.
38-
fn remove_bindgen_attrs(
39-
attrs: &mut Vec<Attribute>,
40-
id: Ident,
41-
) -> Result<(), ConvertErrorWithContext> {
42-
if has_attr(attrs, "bindgen_unused_template_param") {
43-
return Err(ConvertErrorWithContext(
44-
ConvertError::UnusedTemplateParam,
45-
Some(ErrorContext::Item(id)),
46-
));
47-
}
48-
49-
fn is_bindgen_attr(attr: &Attribute) -> bool {
50-
let segments = &attr.path.segments;
51-
segments.len() == 1
52-
&& segments
53-
.first()
54-
.unwrap()
55-
.ident
56-
.to_string()
57-
.starts_with("bindgen_")
58-
}
59-
60-
attrs.retain(|a| !is_bindgen_attr(a));
61-
Ok(())
62-
}
63-
64-
fn has_attr(attrs: &[Attribute], attr_name: &str) -> bool {
65-
attrs.iter().any(|at| at.path.is_ident(attr_name))
66-
}

engine/src/conversion/analysis/pod/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::{
2626
api::{AnalysisPhase, Api, ApiName, CppVisibility, StructDetails, TypeKind, UnanalyzedApi},
2727
convert_error::{ConvertErrorWithContext, ErrorContext},
2828
error_reporter::convert_apis,
29+
parse::BindgenSemanticAttributes,
2930
ConvertError,
3031
},
3132
types::{Namespace, QualifiedName},
@@ -115,7 +116,8 @@ fn analyze_enum(
115116
name: ApiName,
116117
mut item: ItemEnum,
117118
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
118-
super::remove_bindgen_attrs(&mut item.attrs, name.name.get_final_ident())?;
119+
let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs);
120+
metadata.check_for_fatal_attrs(&name.name.get_final_ident())?;
119121
Ok(Box::new(std::iter::once(Api::Enum { name, item })))
120122
}
121123

@@ -134,7 +136,8 @@ fn analyze_struct(
134136
Some(ErrorContext::Item(id)),
135137
));
136138
}
137-
super::remove_bindgen_attrs(&mut details.item.attrs, id.clone())?;
139+
let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs);
140+
metadata.check_for_fatal_attrs(&id)?;
138141
let bases = get_bases(&details.item);
139142
let mut field_deps = HashSet::new();
140143
let type_kind = if byvalue_checker.is_pod(&name.name) {

engine/src/conversion/analysis/tdef.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,12 @@ use crate::{
2323
api::{AnalysisPhase, Api, ApiName, TypedefKind, UnanalyzedApi},
2424
convert_error::{ConvertErrorWithContext, ErrorContext},
2525
error_reporter::convert_apis,
26+
parse::BindgenSemanticAttributes,
2627
ConvertError,
2728
},
2829
types::QualifiedName,
2930
};
3031

31-
use super::remove_bindgen_attrs;
32-
3332
pub(crate) struct TypedefAnalysis {
3433
pub(crate) kind: TypedefKind,
3534
pub(crate) deps: HashSet<QualifiedName>,
@@ -92,8 +91,8 @@ fn get_replacement_typedef(
9291
extra_apis: &mut Vec<UnanalyzedApi>,
9392
) -> Result<Api<TypedefPhase>, ConvertErrorWithContext> {
9493
let mut converted_type = ity.clone();
95-
let id = ity.ident.clone();
96-
remove_bindgen_attrs(&mut converted_type.attrs, id)?;
94+
let metadata = BindgenSemanticAttributes::new_retaining_others(&mut converted_type.attrs);
95+
metadata.check_for_fatal_attrs(&ity.ident)?;
9796
let type_conversion_results = type_converter.convert_type(
9897
(*ity.ty).clone(),
9998
name.name.get_namespace(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use proc_macro2::{Ident, TokenStream};
16+
use syn::{
17+
parenthesized,
18+
parse::{Parse, Parser},
19+
Attribute, LitStr,
20+
};
21+
22+
use crate::conversion::{
23+
api::{CppVisibility, Layout, References, Virtualness},
24+
convert_error::{ConvertErrorWithContext, ErrorContext},
25+
ConvertError,
26+
};
27+
28+
/// The set of all annotations that autocxx_bindgen has added
29+
/// for our benefit.
30+
#[derive(Debug)]
31+
pub(crate) struct BindgenSemanticAttributes(Vec<BindgenSemanticAttribute>);
32+
33+
impl BindgenSemanticAttributes {
34+
// Remove `bindgen_` attributes. They don't have a corresponding macro defined anywhere,
35+
// so they will cause compilation errors if we leave them in.
36+
// We may return an error if one of the bindgen attributes shows that the
37+
// item can't be processed.
38+
pub(crate) fn new_retaining_others(attrs: &mut Vec<Attribute>) -> Self {
39+
let metadata = Self::new(attrs);
40+
attrs.retain(|a| !(a.path.segments.last().unwrap().ident == "cpp_semantics"));
41+
metadata
42+
}
43+
44+
pub(crate) fn new(attrs: &[Attribute]) -> Self {
45+
Self(
46+
attrs
47+
.iter()
48+
.filter_map(|attr| {
49+
if attr.path.segments.last().unwrap().ident == "cpp_semantics" {
50+
let r: Result<BindgenSemanticAttribute, syn::Error> = attr.parse_args();
51+
r.ok()
52+
} else {
53+
None
54+
}
55+
})
56+
.collect(),
57+
)
58+
}
59+
60+
/// Some attributes indicate we can never handle a given item. Check for those.
61+
pub(crate) fn check_for_fatal_attrs(
62+
&self,
63+
id_for_context: &Ident,
64+
) -> Result<(), ConvertErrorWithContext> {
65+
if self.has_attr("unused_template_param") {
66+
Err(ConvertErrorWithContext(
67+
ConvertError::UnusedTemplateParam,
68+
Some(ErrorContext::Item(id_for_context.clone())),
69+
))
70+
} else {
71+
Ok(())
72+
}
73+
}
74+
75+
/// Whether the given attribute is present.
76+
pub(super) fn has_attr(&self, attr_name: &str) -> bool {
77+
self.0.iter().any(|a| a.is_ident(attr_name))
78+
}
79+
80+
/// The C++ visibility of the item.
81+
pub(super) fn get_cpp_visibility(&self) -> CppVisibility {
82+
if self.has_attr("visibility_private") {
83+
CppVisibility::Private
84+
} else if self.has_attr("visibility_protected") {
85+
CppVisibility::Protected
86+
} else {
87+
CppVisibility::Public
88+
}
89+
}
90+
91+
/// Whether the item is virtual.
92+
pub(super) fn get_virtualness(&self) -> Virtualness {
93+
if self.has_attr("pure_virtual") {
94+
Virtualness::PureVirtual
95+
} else if self.has_attr("bindgen_virtual") {
96+
Virtualness::Virtual
97+
} else {
98+
Virtualness::None
99+
}
100+
}
101+
102+
fn parse_if_present<T: Parse>(&self, annotation: &str) -> Option<T> {
103+
self.0
104+
.iter()
105+
.find(|a| a.is_ident(annotation))
106+
.map(|a| a.parse_args().unwrap())
107+
}
108+
109+
fn string_if_present(&self, annotation: &str) -> Option<String> {
110+
let ls: Option<LitStr> = self.parse_if_present(annotation);
111+
ls.map(|ls| ls.value())
112+
}
113+
114+
/// The in-memory layout of the item.
115+
pub(super) fn get_layout(&self) -> Option<Layout> {
116+
self.parse_if_present("layout")
117+
}
118+
119+
/// The original C++ name, which bindgen may have changed.
120+
pub(super) fn get_original_name(&self) -> Option<String> {
121+
self.string_if_present("original_name")
122+
}
123+
124+
fn get_bindgen_special_member_annotation(&self) -> Option<String> {
125+
self.string_if_present("special_member")
126+
}
127+
128+
/// Whether this is a move constructor.
129+
pub(super) fn is_move_constructor(&self) -> bool {
130+
self.get_bindgen_special_member_annotation()
131+
.map_or(false, |val| val == "move_ctor")
132+
}
133+
134+
/// Any reference parameters or return values.
135+
pub(super) fn get_reference_parameters_and_return(&self) -> References {
136+
let mut results = References::default();
137+
for a in &self.0 {
138+
if a.is_ident("ret_type_reference") {
139+
results.ref_return = true;
140+
} else if a.is_ident("ret_type_rvalue_reference") {
141+
results.rvalue_ref_return = true;
142+
} else if a.is_ident("arg_type_reference") {
143+
let r: Result<Ident, syn::Error> = a.parse_args();
144+
if let Ok(ls) = r {
145+
results.ref_params.insert(ls);
146+
}
147+
} else if a.is_ident("arg_type_rvalue_reference") {
148+
let r: Result<Ident, syn::Error> = a.parse_args();
149+
if let Ok(ls) = r {
150+
results.rvalue_ref_params.insert(ls);
151+
}
152+
}
153+
}
154+
results
155+
}
156+
}
157+
158+
#[derive(Debug)]
159+
struct BindgenSemanticAttribute {
160+
annotation_name: Ident,
161+
body: Option<TokenStream>,
162+
}
163+
164+
impl BindgenSemanticAttribute {
165+
fn is_ident(&self, name: &str) -> bool {
166+
self.annotation_name == name
167+
}
168+
169+
fn parse_args<T: Parse>(&self) -> Result<T, syn::Error> {
170+
T::parse.parse2(self.body.as_ref().unwrap().clone())
171+
}
172+
}
173+
174+
impl Parse for BindgenSemanticAttribute {
175+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
176+
let annotation_name: Ident = input.parse()?;
177+
if input.peek(syn::token::Paren) {
178+
let body_contents;
179+
parenthesized!(body_contents in input);
180+
Ok(Self {
181+
annotation_name,
182+
body: Some(body_contents.parse()?),
183+
})
184+
} else if !input.is_empty() {
185+
Err(input.error("expected nothing"))
186+
} else {
187+
Ok(Self {
188+
annotation_name,
189+
body: None,
190+
})
191+
}
192+
}
193+
}

engine/src/conversion/parse/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
mod bindgen_semantic_attributes;
1516
mod parse_bindgen;
1617
mod parse_foreign_mod;
1718

19+
pub(crate) use bindgen_semantic_attributes::BindgenSemanticAttributes;
1820
pub(crate) use parse_bindgen::ParseBindgen;

0 commit comments

Comments
 (0)