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

Issue:
Handle the new `clippy::manual_c_str_literals` lint
Clippy 1.83 introduced [manual_c_str_literals](https://rust-lang.github.io/rust-clippy/master/index.html#/manual_c_str_literals) on this type of generated code:

```rust
#[allow(unsafe_code)]
pub const VSL_CLASS: &::std::ffi::CStr =
    unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"Log\0") };
```

```
warning: calling `CStr::new` with a byte string literal
   --> .../out/bindings.rs:708:14
    |
708 |     unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"Log\0") };
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"Log"`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals
    = note: `#[warn(clippy::manual_c_str_literals)]` on by default
```

## Possible solutions
* introduce a new `generate_cstr_literal(bool)` flag
* change `generate_cstr(bool)` to take an enum instead, e.g. `Off, CStrObjects, CStrLiterals`
* add `#[allow(clippy::manual_c_str_literals)]` to each instance of calling `from_bytes_with_nul_unchecked`

Pull request description:
Generate C-String literals `c"example"` instead of unsafe code
When `generate_cstr` is enabled, and the target rust version is >= 1.77, generate `c"..."` literals instead of the unsafe `from_bytes_with_nul_unchecked` calls.  Note that this requires 2021 edition, but since this is not on by default, and CStr support is relatively recent, i think it is ok to tie them together

Note that this also resolves the Clippy 1.83's [manual_c_str_literals](https://rust-lang.github.io/rust-clippy/master/index.html#/manual_c_str_literals) issue


Fixes #2994

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 25124418..e5918dcc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -436,9 +436,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.60"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
 dependencies = [
  "unicode-ident",
 ]
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index 943d8c75..9f253694 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -717,26 +717,38 @@ impl CodeGenerator for Var {
                     let len = proc_macro2::Literal::usize_unsuffixed(
                         cstr_bytes.len(),
                     );
+                    let cstr =
+                        if options.generate_cstr && rust_features.const_cstr {
+                            CStr::from_bytes_with_nul(&cstr_bytes).ok()
+                        } else {
+                            None
+                        };
 
-                    // TODO: Here we ignore the type we just made up, probably
-                    // we should refactor how the variable type and ty ID work.
-                    let array_ty = quote! { [u8; #len] };
-                    let cstr_ty = quote! { ::#prefix::ffi::CStr };
-
-                    let bytes = proc_macro2::Literal::byte_string(&cstr_bytes);
-
-                    if options.generate_cstr &&
-                        rust_features.const_cstr &&
-                        CStr::from_bytes_with_nul(&cstr_bytes).is_ok()
-                    {
-                        result.push(quote! {
-                            #(#attrs)*
-                            #[allow(unsafe_code)]
-                            pub const #canonical_ident: &#cstr_ty = unsafe {
-                                #cstr_ty::from_bytes_with_nul_unchecked(#bytes)
-                            };
-                        });
+                    if let Some(cstr) = cstr {
+                        let cstr_ty = quote! { ::#prefix::ffi::CStr };
+                        if rust_features.literal_cstr {
+                            let cstr = proc_macro2::Literal::c_string(&cstr);
+                            result.push(quote! {
+                                #(#attrs)*
+                                pub const #canonical_ident: &#cstr_ty = #cstr;
+                            });
+                        } else {
+                            let bytes =
+                                proc_macro2::Literal::byte_string(&cstr_bytes);
+                            result.push(quote! {
+                                #(#attrs)*
+                                #[allow(unsafe_code)]
+                                pub const #canonical_ident: &#cstr_ty = unsafe {
+                                    #cstr_ty::from_bytes_with_nul_unchecked(#bytes)
+                                };
+                            });
+                        }
                     } else {
+                        // TODO: Here we ignore the type we just made up, probably
+                        // we should refactor how the variable type and ty ID work.
+                        let array_ty = quote! { [u8; #len] };
+                        let bytes =
+                            proc_macro2::Literal::byte_string(&cstr_bytes);
                         let lifetime =
                             if true { None } else { Some(quote! { 'static }) }
                                 .into_iter();
diff --git a/bindgen/features.rs b/bindgen/features.rs
index 99915224..174491fa 100644
--- a/bindgen/features.rs
+++ b/bindgen/features.rs
@@ -159,7 +159,10 @@ define_rust_targets! {
         ptr_metadata: #81513,
         layout_for_ptr: #69835,
     },
-    Stable_1_77(77) => { offset_of: #106655 },
+    Stable_1_77(77) => {
+        offset_of: #106655,
+        literal_cstr: #117472,
+    },
     Stable_1_73(73) => { thiscall_abi: #42202 },
     Stable_1_71(71) => { c_unwind_abi: #106075 },
     Stable_1_68(68) => { abi_efiapi: #105795 },


Test patch:
diff --git a/bindgen-tests/tests/expectations/tests/strings_cstr2.rs b/bindgen-tests/tests/expectations/tests/strings_cstr2.rs
new file mode 100644
index 00000000..2ce21f43
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/strings_cstr2.rs
@@ -0,0 +1,4 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+pub const MY_STRING_UTF8: &::std::ffi::CStr = c"Hello, world!";
+pub const MY_STRING_INTERIOR_NULL: &::std::ffi::CStr = c"Hello,";
+pub const MY_STRING_NON_UTF8: &::std::ffi::CStr = c"ABCDE\xFF";
diff --git a/bindgen-tests/tests/headers/strings_cstr2.h b/bindgen-tests/tests/headers/strings_cstr2.h
new file mode 100644
index 00000000..2cd7e622
--- /dev/null
+++ b/bindgen-tests/tests/headers/strings_cstr2.h
@@ -0,0 +1,5 @@
+// bindgen-flags: --rust-target=1.77 --generate-cstr
+
+const char* MY_STRING_UTF8 = "Hello, world!";
+const char* MY_STRING_INTERIOR_NULL = "Hello,\0World!";
+const char* MY_STRING_NON_UTF8 = "ABCDE\xFF";