Skip to content

Commit

Permalink
Correctly handle TypeRefTypeHint of the references
Browse files Browse the repository at this point in the history
  • Loading branch information
twistedfall committed Sep 2, 2024
1 parent e6cd36a commit fbd2c3e
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 8 deletions.
3 changes: 3 additions & 0 deletions binding-generator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ percent-encoding = { version = "2", default-features = false }
regex = "1"
shlex = { version = "1.3", default-features = false }

[dev-dependencies]
tempfile = "3"

[features]
clang-runtime = ["clang/runtime", "clang-sys/runtime"]
2 changes: 1 addition & 1 deletion binding-generator/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub trait GeneratorVisitor<'tu>: Sized {
/// It takes [Entity]s supplied by the entity walker, extracts their export data (whether the entity should appear in bindings at
/// all or is internal) and calls the corresponding method in [GeneratorVisitor] based on their type. This is the 2nd pass of the
/// binding generation.
struct OpenCvWalker<'tu, 'r, V> {
pub struct OpenCvWalker<'tu, 'r, V> {
module: &'r str,
opencv_module_header_dir: &'r Path,
visitor: V,
Expand Down
2 changes: 1 addition & 1 deletion binding-generator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub use entity::EntityExt;
pub use enumeration::Enum;
use field::Field;
pub use func::{Func, FuncId, FuncTypeHint};
pub use generator::{GeneratedType, Generator, GeneratorVisitor};
pub use generator::{GeneratedType, Generator, GeneratorVisitor, OpenCvWalker};
pub use generator_env::{ClassKindOverride, ExportConfig, GeneratorEnv};
pub use iterator_ext::IteratorExt;
use memoize::{MemoizeMap, MemoizeMapExt};
Expand Down
14 changes: 14 additions & 0 deletions binding-generator/src/settings/argument_override.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,20 @@ pub static ARGUMENT_OVERRIDE: Lazy<HashMap<FuncId, HashMap<&str, TypeRefTypeHint
FuncId::new_mut("cv::MatSize::MatSize", ["_p"]),
HashMap::from([("_p", TypeRefTypeHint::PrimitivePtrAsRaw)]),
),
(
FuncId::new_mut(
"cv::findCirclesGrid",
["image", "patternSize", "centers", "flags", "blobDetector", "parameters"],
),
HashMap::from([("blobDetector", TypeRefTypeHint::Nullable)]),
),
(
FuncId::new_mut(
"cv::findCirclesGrid",
["image", "patternSize", "centers", "flags", "blobDetector"],
),
HashMap::from([("blobDetector", TypeRefTypeHint::Nullable)]),
),
])
});

Expand Down
4 changes: 2 additions & 2 deletions binding-generator/src/type_ref/desc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ impl<'tu> ClangTypeExt<'tu> for Type<'tu> {
match kind {
TypeKind::Pointer => {
let pointee = self.get_pointee_type().expect("No pointee type for pointer");
let pointee_typeref = TypeRef::new_ext(pointee, type_hint, parent_entity, gen_env);
let pointee_typeref = TypeRef::new_ext(pointee, type_hint.recurse(), parent_entity, gen_env);
let pointee_kind = pointee_typeref.kind();
if pointee_kind.is_function() {
pointee_kind.into_owned()
Expand All @@ -392,7 +392,7 @@ impl<'tu> ClangTypeExt<'tu> for Type<'tu> {

TypeKind::LValueReference => TypeRefKind::Reference(TypeRef::new_ext(
self.get_pointee_type().expect("No pointee type for reference"),
type_hint,
type_hint.recurse(),
parent_entity,
gen_env,
)),
Expand Down
4 changes: 2 additions & 2 deletions binding-generator/src/type_ref/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ impl<'tu, 'ge> TypeRefKind<'tu, 'ge> {
.map(|(_, str_type)| (Dir::from_out_dir(inner.inherent_constness().is_mut()), str_type)),
TypeRefKind::Array(inner, ..) => {
if inner.kind().is_char() {
Some((Dir::In, StrType::CharPtr(StrEnc::Text)))
Some((Dir::from_out_dir(inner.constness().is_mut()), StrType::CharPtr(StrEnc::Text)))
} else {
None
}
Expand Down Expand Up @@ -527,7 +527,7 @@ mod tests {

{
let char_array = TypeRef::new_array(TypeRefDesc::char(), None);
assert_eq!(Some((Dir::In, StrType::CharPtr(StrEnc::Text))), as_string(char_array));
assert_eq!(Some((Dir::Out, StrType::CharPtr(StrEnc::Text))), as_string(char_array));
}
}
}
11 changes: 11 additions & 0 deletions binding-generator/src/type_ref/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ impl TypeRefTypeHint {
}
}

/// Filters current TypeRef type hint to make it suitable for inner type e.g. for the pointee
///
/// Useful for example to strip the nullability from the inner type of a pointer
pub fn recurse(self) -> Self {
match self {
Self::Nullable => Self::None,
Self::NullableSlice => Self::Slice,
recursable => recursable,
}
}

pub fn nullability(&self) -> Nullability {
match self {
TypeRefTypeHint::Nullable | TypeRefTypeHint::NullableSlice => Nullability::Nullable,
Expand Down
10 changes: 8 additions & 2 deletions binding-generator/src/writer/rust_native/type_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,14 @@ impl TypeRefExt for TypeRef<'_, '_> {
}
kind => {
let (indirection, tref_kind, tref) = match kind {
TypeRefKind::Pointer(pointee) => (Indirection::Pointer, pointee.kind().into_owned(), Owned(pointee)),
TypeRefKind::Reference(pointee) => (Indirection::Reference, pointee.kind().into_owned(), Owned(pointee)),
TypeRefKind::Pointer(pointee) => {
let pointee = pointee.with_type_hint(self.type_hint().clone());
(Indirection::Pointer, pointee.kind().into_owned(), Owned(pointee))
}
TypeRefKind::Reference(pointee) => {
let pointee = pointee.with_type_hint(self.type_hint().clone());
(Indirection::Reference, pointee.kind().into_owned(), Owned(pointee))
}
kind => (Indirection::None, kind, Borrowed(self)),
};
match tref_kind.canonical().into_owned() {
Expand Down
7 changes: 7 additions & 0 deletions binding-generator/tests/code_template.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#define CV_EXPORTS

namespace cv {

{{code}}

}
82 changes: 82 additions & 0 deletions binding-generator/tests/generation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::fs::File;
use std::io::Write;
use std::path::Path;

use clang::diagnostic::Severity;
use clang::{Clang, Entity, Index};
use opencv_binding_generator::writer::rust_native::element::RustNativeGeneratedElement;
use opencv_binding_generator::{EntityWalkerExt, Func, GeneratorEnv, GeneratorVisitor, OpenCvWalker};
use tempfile::TempDir;

fn clang_parse(code: &str, op: impl FnOnce(Entity)) {
const CODE_TPL: &str = include_str!("code_template.cpp");
const CODE_PAT: &str = "{{code}}";

let temp_dir = TempDir::new().expect("Can't create temp dir");
let temp_file_path = temp_dir.path().join("temp.cpp");
if let Some(start) = CODE_TPL.find(CODE_PAT) {
let mut temp_cpp = File::create(&temp_file_path).expect("Can't create temp file");
temp_cpp
.write_all(CODE_TPL[..start].as_bytes())
.expect("Can't write to temp file");
temp_cpp.write_all(code.as_bytes()).expect("Can't write to temp file");
temp_cpp
.write_all(CODE_TPL[start + CODE_PAT.len()..].as_bytes())
.expect("Can't write to temp file");
}
let clang = Clang::new().expect("Can't init clang");
let index = Index::new(&clang, false, false);
let root_tu = index
.parser(&temp_file_path)
.skip_function_bodies(true)
.detailed_preprocessing_record(true)
.parse()
.expect("Can't parse");
let diags = root_tu.get_diagnostics();
if !diags.is_empty() {
let mut has_error = false;
eprintln!("WARNING: {} diagnostic messages", diags.len());
for diag in diags {
if !has_error && matches!(diag.get_severity(), Severity::Error | Severity::Fatal) {
has_error = true;
}
eprintln!(" {diag}");
}
if has_error {
panic!("Errors during header parsing");
}
}
op(root_tu.get_entity());
}

fn extract_functions(code: &str, cb: impl FnMut(Func)) {
struct FunctionExtractor<F> {
cb: F,
}

impl<F: FnMut(Func)> GeneratorVisitor<'_> for FunctionExtractor<F> {
fn visit_func(&mut self, func: Func) {
(self.cb)(func);
}
}

clang_parse(code, |root_tu| {
let gen_env = GeneratorEnv::empty();
let visitor = FunctionExtractor { cb };
let opencv_walker = OpenCvWalker::new("core", Path::new(""), visitor, gen_env);

root_tu.walk_opencv_entities(opencv_walker);
});
}

#[test]
fn char_ptr_slice() {
extract_functions("CV_EXPORTS int startLoop(int argc, char* argv[]);", |f| {
assert_eq!(f.gen_rust("0.0.0").trim(), "#[inline]\npub fn start_loop(argv: &mut [&str]) -> Result<i32> {\n\tstring_array_arg_mut!(argv);\n\treturn_send!(via ocvrs_return);\n\tunsafe { sys::cv_startLoop_int_charXX(argv.len().try_into()?, argv.as_mut_ptr(), ocvrs_return.as_mut_ptr()) };\n\treturn_receive!(unsafe ocvrs_return => ret);\n\tlet ret = ret.into_result()?;\n\tOk(ret)\n}");
assert_eq!(f.gen_cpp().trim(), "void cv_startLoop_int_charXX(int argc, char** argv, Result<int>* ocvrs_return) {\n\ttry {\n\t\tint ret = cv::startLoop(argc, argv);\n\t\tOk(ret, ocvrs_return);\n\t} OCVRS_CATCH(ocvrs_return);\n}");
assert_eq!(
f.gen_rust_externs().trim(),
r#"pub fn cv_startLoop_int_charXX(argc: i32, argv: *mut *mut c_char, ocvrs_return: *mut Result<i32>);"#,
);
});
}

0 comments on commit fbd2c3e

Please sign in to comment.