Don't include qualifiers when converting C++ type to a string.

This would result in us storing type names like "const char" in the IR, which
confused the code generator. Besides, we already take care of const separately
anyway.

See the new regression test for more details.

PiperOrigin-RevId: 419748299
diff --git a/rs_bindings_from_cc/ast_visitor.cc b/rs_bindings_from_cc/ast_visitor.cc
index 2824f86..f8e5b06 100644
--- a/rs_bindings_from_cc/ast_visitor.cc
+++ b/rs_bindings_from_cc/ast_visitor.cc
@@ -421,7 +421,9 @@
     std::optional<devtools_rust::TypeLifetimes> lifetimes,
     bool nullable) const {
   std::optional<MappedType> type = std::nullopt;
-  std::string type_string = qual_type.getAsString();
+  // When converting the type to a string, don't include qualifiers -- we handle
+  // these separately.
+  std::string type_string = qual_type.getUnqualifiedType().getAsString();
 
   if (const auto* pointer_type = qual_type->getAs<clang::PointerType>()) {
     std::optional<LifetimeId> lifetime;
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index af60b47..c19d40f 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -1136,6 +1136,45 @@
     }
 
     #[test]
+    fn test_const_char_ptr_func() -> Result<()> {
+        // This is a regression test: We used to include the "const" in the name
+        // of the CcType, which caused a panic in the code generator
+        // ('"const char" is not a valid Ident').
+        // It's therefore important that f() is inline so that we need to
+        // generate a thunk for it (where we then process the CcType).
+        let ir = ir_from_cc(&tokens_to_string(quote! {
+            inline void f(const char *str);
+        })?)?;
+
+        let rs_api = generate_rs_api(&ir)?;
+        assert_rs_matches!(
+            rs_api,
+            quote! {
+                #[inline(always)]
+                pub fn f(str: *const i8) {
+                    unsafe { crate::detail::__rust_thunk___Z1fPKc(str) }
+                }
+            }
+        );
+        assert_rs_matches!(
+            rs_api,
+            quote! {
+                extern "C" {
+                    pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
+                }
+            }
+        );
+
+        assert_cc_matches!(
+            generate_rs_api_impl(&ir)?,
+            quote! {
+                extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(str) ; }
+            }
+        );
+        Ok(())
+    }
+
+    #[test]
     fn test_item_order() -> Result<()> {
         let ir = ir_from_cc(
             "int first_func();