Repository: rust-lang/rust-bindgen (rust)

Issue:
`ParseCallbacks::field_visibility` is not called for new type aliases
Given a header file

```c
typedef const char *  VCL_STRING;
```

I would like to create a new alias type:

```rust
let bindings = bindgen::Builder::default()
    .header("src/wrapper.h")
    .parse_callbacks(Box::new(my_callbacks))
    .new_type_alias("VCL_STRING")
    .generate();
```

The above code generates this Rust code:

```rust
pub struct VCL_STRING(pub *const ::std::ffi::c_char);
```

My `my_callbacks` instance implements `field_visibility` callback, but it never gets called for the `VCL_STRING` type - thus I have no way for the field to be created as private or crate-public.

Pull request:
Fix `field_visibility` not called for new-type aliases
The `field_visibility` callback is now called in case of alias new-type and new-type-deref with the type name and field_name set to `"0"`

This PR allows generation of different accessibility:

```c
typedef const char *  pub_var1;
typedef const char *  pubcrate_var2;
typedef const char *  private_var3;
```

```rust
pub struct pub_var1(pub *const ::std::os::raw::c_char);
pub struct pubcrate_var2(pub(crate) *const ::std::os::raw::c_char);
pub struct private_var3(*const ::std::os::raw::c_char);
```

Fixes #2966

Patch:
diff --git a/bindgen/codegen/dyngen.rs b/bindgen/codegen/dyngen.rs
index 4b2749ec..e75e11a2 100644
--- a/bindgen/codegen/dyngen.rs
+++ b/bindgen/codegen/dyngen.rs
@@ -83,6 +83,12 @@ impl DynamicItems {
         let init_fields = &self.init_fields;
         let struct_implementation = &self.struct_implementation;
 
+        let library_new = if ctx.options().wrap_unsafe_ops {
+            quote!(unsafe { ::libloading::Library::new(path) })
+        } else {
+            quote!(::libloading::Library::new(path))
+        };
+
         let from_library = if ctx.options().wrap_unsafe_ops {
             quote!(unsafe { Self::from_library(library) })
         } else {
@@ -100,7 +106,7 @@ impl DynamicItems {
                     path: P
                 ) -> Result<Self, ::libloading::Error>
                 where P: AsRef<::std::ffi::OsStr> {
-                    let library = ::libloading::Library::new(path)?;
+                    let library = #library_new?;
                     #from_library
                 }
 
@@ -202,6 +208,7 @@ impl DynamicItems {
         ident: Ident,
         ty: TokenStream,
         is_required: bool,
+        wrap_unsafe_ops: bool,
     ) {
         let member = if is_required {
             quote! { *mut #ty }
@@ -225,15 +232,20 @@ impl DynamicItems {
         });
 
         let ident_str = codegen::helpers::ast_ty::cstr_expr(ident.to_string());
-        self.constructor_inits.push(if is_required {
-            quote! {
-                let #ident = __library.get::<*mut #ty>(#ident_str).map(|sym| *sym)?;
-            }
+
+        let library_get = if wrap_unsafe_ops {
+            quote!(unsafe { __library.get::<*mut #ty>(#ident_str) })
         } else {
-            quote! {
-                let #ident = __library.get::<*mut #ty>(#ident_str).map(|sym| *sym);
-            }
-        });
+            quote!(__library.get::<*mut #ty>(#ident_str))
+        };
+
+        let qmark = if is_required { quote!(?) } else { quote!() };
+
+        let var_get = quote! {
+            let #ident = #library_get.map(|sym| *sym)#qmark;
+        };
+
+        self.constructor_inits.push(var_get);
 
         self.init_fields.push(quote! {
             #ident
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index 2de56c19..4cab8773 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -808,6 +808,7 @@ impl CodeGenerator for Var {
                         .to_rust_ty_or_opaque(ctx, &())
                         .into_token_stream(),
                     ctx.options().dynamic_link_require_all,
+                    ctx.options().wrap_unsafe_ops,
                 );
             } else {
                 result.push(tokens);
@@ -1099,13 +1100,21 @@ impl CodeGenerator for Type {
                     });
                 }
 
-                let access_spec =
-                    access_specifier(ctx.options().default_visibility);
                 tokens.append_all(match alias_style {
                     AliasVariation::TypeAlias => quote! {
                         = #inner_rust_type ;
                     },
                     AliasVariation::NewType | AliasVariation::NewTypeDeref => {
+                        let visibility = ctx
+                            .options()
+                            .last_callback(|cb| {
+                                cb.field_visibility(FieldInfo {
+                                    type_name: &item.canonical_name(ctx),
+                                    field_name: "0",
+                                })
+                            })
+                            .unwrap_or(ctx.options().default_visibility);
+                        let access_spec = access_specifier(visibility);
                         quote! {
                             (#access_spec #inner_rust_type) ;
                         }


Test patch:
diff --git a/bindgen-tests/tests/expectations/tests/issue-2966.rs b/bindgen-tests/tests/expectations/tests/issue-2966.rs
new file mode 100644
index 00000000..bfdcbd9e
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/issue-2966.rs
@@ -0,0 +1,10 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[repr(transparent)]
+#[derive(Debug, Copy, Clone)]
+pub struct pub_var1(pub *const ::std::os::raw::c_char);
+#[repr(transparent)]
+#[derive(Debug, Copy, Clone)]
+pub struct pubcrate_var2(pub(crate) *const ::std::os::raw::c_char);
+#[repr(transparent)]
+#[derive(Debug, Copy, Clone)]
+pub struct private_var3(*const ::std::os::raw::c_char);
diff --git a/bindgen-tests/tests/expectations/tests/wrap_unsafe_ops_dynamic_loading_simple.rs b/bindgen-tests/tests/expectations/tests/wrap_unsafe_ops_dynamic_loading_simple.rs
index 2f5b4cc5..05be5d99 100644
--- a/bindgen-tests/tests/expectations/tests/wrap_unsafe_ops_dynamic_loading_simple.rs
+++ b/bindgen-tests/tests/expectations/tests/wrap_unsafe_ops_dynamic_loading_simple.rs
@@ -16,13 +16,14 @@ pub struct TestLib {
         unsafe extern "C" fn() -> ::std::os::raw::c_int,
         ::libloading::Error,
     >,
+    pub FLUX: Result<*mut ::std::os::raw::c_int, ::libloading::Error>,
 }
 impl TestLib {
     pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
     where
         P: AsRef<::std::ffi::OsStr>,
     {
-        let library = ::libloading::Library::new(path)?;
+        let library = unsafe { ::libloading::Library::new(path) }?;
         unsafe { Self::from_library(library) }
     }
     pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
@@ -33,11 +34,14 @@ impl TestLib {
         let foo = unsafe { __library.get(b"foo\0") }.map(|sym| *sym);
         let bar = unsafe { __library.get(b"bar\0") }.map(|sym| *sym);
         let baz = unsafe { __library.get(b"baz\0") }.map(|sym| *sym);
+        let FLUX = unsafe { __library.get::<*mut ::std::os::raw::c_int>(b"FLUX\0") }
+            .map(|sym| *sym);
         Ok(TestLib {
             __library,
             foo,
             bar,
             baz,
+            FLUX,
         })
     }
     pub unsafe fn foo(
@@ -53,4 +57,7 @@ impl TestLib {
     pub unsafe fn baz(&self) -> ::std::os::raw::c_int {
         unsafe { (self.baz.as_ref().expect("Expected function, got error."))() }
     }
+    pub unsafe fn FLUX(&self) -> *mut ::std::os::raw::c_int {
+        *self.FLUX.as_ref().expect("Expected variable, got error.")
+    }
 }
diff --git a/bindgen-tests/tests/headers/issue-2966.h b/bindgen-tests/tests/headers/issue-2966.h
new file mode 100644
index 00000000..3f00dec6
--- /dev/null
+++ b/bindgen-tests/tests/headers/issue-2966.h
@@ -0,0 +1,6 @@
+// bindgen-flags: --default-alias-style=new_type
+// bindgen-parse-callbacks: type-visibility
+
+typedef const char *  pub_var1;
+typedef const char *  pubcrate_var2;
+typedef const char *  private_var3;
diff --git a/bindgen-tests/tests/headers/wrap_unsafe_ops_dynamic_loading_simple.h b/bindgen-tests/tests/headers/wrap_unsafe_ops_dynamic_loading_simple.h
index 2b8c1071..36a638ae 100644
--- a/bindgen-tests/tests/headers/wrap_unsafe_ops_dynamic_loading_simple.h
+++ b/bindgen-tests/tests/headers/wrap_unsafe_ops_dynamic_loading_simple.h
@@ -3,3 +3,5 @@
 int foo(int x, int y);
 int bar(void *x);
 int baz();
+
+const int FLUX;
diff --git a/bindgen-tests/tests/parse_callbacks/mod.rs b/bindgen-tests/tests/parse_callbacks/mod.rs
index ee31d56e..9f4b04a2 100644
--- a/bindgen-tests/tests/parse_callbacks/mod.rs
+++ b/bindgen-tests/tests/parse_callbacks/mod.rs
@@ -93,8 +93,8 @@ struct FieldVisibility {
 }
 
 /// Implements the `field_visibility` function of the trait by checking if the
-/// field name starts with `private_`. If it does it makes it private, if it
-/// doesn't it makes it public, taking into account the default visibility.
+/// field name starts with `private_`. If it does, it makes it private, if it
+/// doesn't, it makes it public, taking into account the default visibility.
 impl ParseCallbacks for FieldVisibility {
     fn field_visibility(
         &self,
@@ -113,6 +113,28 @@ impl ParseCallbacks for FieldVisibility {
     }
 }
 
+#[derive(Debug)]
+struct TypeVisibility;
+
+/// Implements the `field_visibility` function of the trait by checking the
+/// type name. Depending on name prefix, it will return a different visibility.
+impl ParseCallbacks for TypeVisibility {
+    fn field_visibility(
+        &self,
+        FieldInfo { type_name, .. }: FieldInfo,
+    ) -> Option<FieldVisibilityKind> {
+        if type_name.starts_with("private_") {
+            Some(FieldVisibilityKind::Private)
+        } else if type_name.starts_with("pubcrate_") {
+            Some(FieldVisibilityKind::PublicCrate)
+        } else if type_name.starts_with("pub_") {
+            Some(FieldVisibilityKind::Public)
+        } else {
+            None
+        }
+    }
+}
+
 #[derive(Debug)]
 pub(super) struct WrapAsVariadicFn;
 
@@ -129,6 +151,7 @@ pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
             Box::new(BlocklistedTypeImplementsTrait)
         }
         "wrap-as-variadic-fn" => Box::new(WrapAsVariadicFn),
+        "type-visibility" => Box::new(TypeVisibility),
         call_back => {
             if let Some(prefix) =
                 call_back.strip_prefix("remove-function-prefix-")