Set `defining_target` for explicit template specializations.

There was an inconsistency here: while it's true that, in principle, since there's only one target involved here (the template specialization is defined in the target that owns it), the existence or nonexistence of `defining_target` is used elsewhere to determine whether a type is a template type. So we still need to populate the `defining_target` field, in order to e.g. filter it out correctly in `:supported` targets.

PiperOrigin-RevId: 684974881
Change-Id: I8bad2f6179d9cd36d7c4b0a57fa41e13512f8f84
diff --git a/rs_bindings_from_cc/generate_bindings/lib.rs b/rs_bindings_from_cc/generate_bindings/lib.rs
index ae77a19..46b63bd 100644
--- a/rs_bindings_from_cc/generate_bindings/lib.rs
+++ b/rs_bindings_from_cc/generate_bindings/lib.rs
@@ -3277,6 +3277,31 @@
     }
 
     #[gtest]
+    fn test_default_crubit_features_disabled_template_explicit_specialization() -> Result<()> {
+        let mut ir = ir_from_cc(
+            r#"
+            template <typename T>
+            struct X {
+                T t;
+            };
+
+            template <>
+            struct X<int> {
+                int val;
+                X<int>() : val(42) {}
+            };
+
+            inline X<int> NotPresent() { return X<int>(); }"#,
+        )?;
+        *ir.target_crubit_features_mut(&ir.current_target().clone()) =
+            crubit_feature::CrubitFeature::Supported.into();
+        let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
+        assert_rs_not_matches!(rs_api, quote! {NotPresent});
+        assert_cc_not_matches!(rs_api_impl, quote! {NotPresent});
+        Ok(())
+    }
+
+    #[gtest]
     fn test_type_map_override_assert() -> Result<()> {
         let rs_api = generate_bindings_tokens(ir_from_cc(
             r#" #pragma clang lifetime_elision
diff --git a/rs_bindings_from_cc/importers/cxx_record.cc b/rs_bindings_from_cc/importers/cxx_record.cc
index 0c382ee..436e74b 100644
--- a/rs_bindings_from_cc/importers/cxx_record.cc
+++ b/rs_bindings_from_cc/importers/cxx_record.cc
@@ -301,10 +301,9 @@
           ictx_.GetComment(specialization_decl->getSpecializedTemplate());
     }
     source_loc = specialization_decl->getBeginLoc();
-    /// Note: only specify defining_target if it's a template instantiation!
-    /// Explicit specializations are their own defining_target.
+    // Specify defining_target if it's a template instantiation.
     if (auto instantiation_source =
-            specialization_decl->getInstantiatedFrom()) {
+            specialization_decl->getSpecializedTemplateOrPartial()) {
       clang::NamedDecl* decl;
       if (auto* template_decl =
               instantiation_source.dyn_cast<clang::ClassTemplateDecl*>()) {
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index 56e109f..c9bb2a9 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -1284,7 +1284,8 @@
           Record {
             rs_name: "__CcTemplateInstN23test_namespace_bindings8MyStructIiEE", ...
             cc_name: "test_namespace_bindings::MyStruct<int>", ...
-            owning_target: BazelLabel("//test:testing_target"), ...
+            owning_target: BazelLabel("//test:testing_target"),
+            defining_target: Some(BazelLabel("//test:testing_target")), ...
             doc_comment: Some("Doc comment for template specialization for T=int."), ...
             fields: [Field {
                 identifier: Some("value"), ...
diff --git a/rs_bindings_from_cc/test/templates/explicit_specialization/BUILD b/rs_bindings_from_cc/test/templates/explicit_specialization/BUILD
new file mode 100644
index 0000000..4dc5f70
--- /dev/null
+++ b/rs_bindings_from_cc/test/templates/explicit_specialization/BUILD
@@ -0,0 +1,22 @@
+"""End-to-end example of explicit template specialization working."""
+
+load("//common:crubit_wrapper_macros_oss.bzl", "crubit_rust_test")
+load("//rs_bindings_from_cc/test:test_bindings.bzl", "crubit_test_cc_library")
+
+package(default_applicable_licenses = ["//:license"])
+
+crubit_test_cc_library(
+    name = "explicit_specialization",
+    hdrs = ["explicit_specialization.h"],
+)
+
+crubit_rust_test(
+    name = "main",
+    srcs = ["test.rs"],
+    cc_deps = [
+        ":explicit_specialization",
+    ],
+    deps = [
+        "@crate_index//:googletest",
+    ],
+)
diff --git a/rs_bindings_from_cc/test/templates/explicit_specialization/explicit_specialization.h b/rs_bindings_from_cc/test/templates/explicit_specialization/explicit_specialization.h
new file mode 100644
index 0000000..d9d9aed
--- /dev/null
+++ b/rs_bindings_from_cc/test/templates/explicit_specialization/explicit_specialization.h
@@ -0,0 +1,21 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_TEMPLATES_EXPLICIT_SPECIALIZATION_EXPLICIT_SPECIALIZATION_H_
+#define THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_TEMPLATES_EXPLICIT_SPECIALIZATION_EXPLICIT_SPECIALIZATION_H_
+
+template <typename T>
+struct X {
+  T t;
+};
+
+template <>
+struct X<int> {
+  int val;
+  X<int>() : val(42) {}
+};
+
+inline X<int> ReturnX() { return X<int>(); }
+
+#endif  // THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_TEMPLATES_EXPLICIT_SPECIALIZATION_EXPLICIT_SPECIALIZATION_H_
diff --git a/rs_bindings_from_cc/test/templates/explicit_specialization/test.rs b/rs_bindings_from_cc/test/templates/explicit_specialization/test.rs
new file mode 100644
index 0000000..da09c57
--- /dev/null
+++ b/rs_bindings_from_cc/test/templates/explicit_specialization/test.rs
@@ -0,0 +1,15 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#[cfg(test)]
+mod tests {
+    use explicit_specialization::*;
+    use googletest::prelude::*;
+
+    #[gtest]
+    fn test_explicit_specialization_works_correctly() {
+        let x = ReturnX();
+        assert_eq!(42, x.val);
+    }
+}