diff --git a/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs b/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs index 327c05638..62c74f8fc 100644 --- a/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs +++ b/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs @@ -371,6 +371,11 @@ pub(crate) fn generate_associated_item<'tcx>( if !is_supported_associated_item(tcx, def_id) { return None; } + // Associated items resolve names via `symbol_unqualified_name`, which - unlike + // `symbol_canonical_name` - does not consult `should_receive_bindings`. + if crate::should_receive_bindings(db, def_id).is_err() { + return None; + } crate::error_scope!(db, def_id); let result = match assoc_item.kind { ty::AssocKind::Fn { .. } => { diff --git a/cc_bindings_from_rs/test/do_not_bind/BUILD b/cc_bindings_from_rs/test/do_not_bind/BUILD new file mode 100644 index 000000000..22889843c --- /dev/null +++ b/cc_bindings_from_rs/test/do_not_bind/BUILD @@ -0,0 +1,50 @@ +load( + "@rules_rust//rust:defs.bzl", + "rust_library", +) +load( + "//cc_bindings_from_rs/bazel_support:cc_bindings_from_rust_rule.bzl", + "cc_bindings_from_rust", +) +load( + "//cc_bindings_from_rs/test/golden:golden_test.bzl", + "golden_test", +) +load("//common:crubit_wrapper_macros_oss.bzl", "crubit_cc_test") + +package(default_applicable_licenses = ["//:license"]) + +rust_library( + name = "do_not_bind", + testonly = 1, + srcs = ["do_not_bind.rs"], + aspect_hints = [ + "//features:experimental", + ], + proc_macro_deps = [ + "//support:crubit_annotate", + ], +) + +cc_bindings_from_rust( + name = "do_not_bind_cc_api", + testonly = 1, + crate = ":do_not_bind", +) + +golden_test( + name = "do_not_bind_golden_test", + basename = "do_not_bind", + golden_h = "do_not_bind_cc_api.h", + golden_rs = "do_not_bind_cc_api_impl.rs", + rust_library = "do_not_bind", +) + +crubit_cc_test( + name = "do_not_bind_test", + srcs = ["do_not_bind_test.cc"], + deps = [ + ":do_not_bind_cc_api", + "//testing/base/public:gunit_main", + ], +) diff --git a/cc_bindings_from_rs/test/do_not_bind/do_not_bind.rs b/cc_bindings_from_rs/test/do_not_bind/do_not_bind.rs new file mode 100644 index 000000000..92dd276df --- /dev/null +++ b/cc_bindings_from_rs/test/do_not_bind/do_not_bind.rs @@ -0,0 +1,47 @@ +// 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 + +//! Coverage for `#[crubit_annotate::do_not_bind]` across the item kinds it +//! claims to support. Each suppressed item is paired with an unsuppressed +//! control so the golden distinguishes "suppressed" from "never bound". + +pub struct Struct { + pub value: i32, +} + +pub trait Trait { + fn suppressed_trait_method(&self) -> i32; + fn bound_trait_method(&self) -> i32; +} + +#[crubit_annotate::do_not_bind] +pub fn suppressed_free_fn() -> i32 { + 1 +} + +pub fn bound_free_fn() -> i32 { + 2 +} + +impl Struct { + #[crubit_annotate::do_not_bind] + pub fn suppressed_inherent_method(&self) -> i32 { + 3 + } + + pub fn bound_inherent_method(&self) -> i32 { + 4 + } +} + +impl Trait for Struct { + #[crubit_annotate::do_not_bind] + fn suppressed_trait_method(&self) -> i32 { + 5 + } + + fn bound_trait_method(&self) -> i32 { + 6 + } +} diff --git a/cc_bindings_from_rs/test/do_not_bind/do_not_bind_cc_api.h b/cc_bindings_from_rs/test/do_not_bind/do_not_bind_cc_api.h new file mode 100644 index 000000000..ee78765fa --- /dev/null +++ b/cc_bindings_from_rs/test/do_not_bind/do_not_bind_cc_api.h @@ -0,0 +1,100 @@ +// 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 + +// Automatically @generated C++ bindings for the following Rust crate: +// do_not_bind_golden + +// clang-format off +#ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_DO_NOT_BIND_DO_NOT_BIND_GOLDEN +#define THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_DO_NOT_BIND_DO_NOT_BIND_GOLDEN + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#pragma clang diagnostic ignored "-Wunused-private-field" +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wignored-attributes" +#include "support/annotations_internal.h" +#include "support/rs_std/traits.h" + +#include +#include +#include + +namespace do_not_bind { + +struct CRUBIT_INTERNAL_RUST_TYPE(":: do_not_bind_golden :: Struct") alignas(4) + [[clang::trivial_abi]] Struct final { + public: + ::std::int32_t bound_inherent_method() const; + + ::std::int32_t value{}; + + private: + static void __crubit_field_offset_assertions(); +}; + +struct CRUBIT_INTERNAL_RUST_TYPE(":: do_not_bind_golden :: Trait") Trait { + template + using impl = rs_std::impl; +}; + +::std::int32_t bound_free_fn(); + +} // namespace do_not_bind + +template <> +struct rs_std::impl<::do_not_bind::Struct, ::do_not_bind::Trait> { + static constexpr bool kIsImplemented = true; + + static ::std::int32_t bound_trait_method(::do_not_bind::Struct const& self); +}; + +namespace do_not_bind { + +static_assert( + sizeof(Struct) == 4, + "Verify that ADT layout didn't change since this header got generated"); +static_assert( + alignof(Struct) == 4, + "Verify that ADT layout didn't change since this header got generated"); +static_assert(::std::is_trivially_destructible_v); +static_assert(::std::is_trivially_move_constructible_v<::do_not_bind::Struct>); +static_assert(::std::is_trivially_move_assignable_v<::do_not_bind::Struct>); +namespace __crubit_internal { +extern "C" ::std::int32_t __crubit_thunk_bound_uinherent_umethod( + ::do_not_bind::Struct const&); +} +inline ::std::int32_t Struct::bound_inherent_method() const { + auto&& self = *this; + return __crubit_internal::__crubit_thunk_bound_uinherent_umethod(self); +} +inline void Struct::__crubit_field_offset_assertions() { + static_assert(0 == offsetof(Struct, value)); +} +namespace __crubit_internal { +extern "C" ::std::int32_t __crubit_thunk_bound_ufree_ufn(); +} +inline ::std::int32_t bound_free_fn() { + return __crubit_internal::__crubit_thunk_bound_ufree_ufn(); +} + +} // namespace do_not_bind + +namespace do_not_bind { +namespace __crubit_internal { +extern "C" ::std::int32_t +__crubit_thunk_Trait_ubound_utrait_umethod_udo_unot_ubind_ugolden_x0000003a_x0000003aStruct( + ::do_not_bind::Struct const&); +} +} // namespace do_not_bind +inline ::std::int32_t +rs_std::impl<::do_not_bind::Struct, ::do_not_bind::Trait>::bound_trait_method( + ::do_not_bind::Struct const& self) { + return do_not_bind::__crubit_internal:: + __crubit_thunk_Trait_ubound_utrait_umethod_udo_unot_ubind_ugolden_x0000003a_x0000003aStruct( + self); +} + +#pragma clang diagnostic pop +#endif // THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_DO_NOT_BIND_DO_NOT_BIND_GOLDEN diff --git a/cc_bindings_from_rs/test/do_not_bind/do_not_bind_cc_api_impl.rs b/cc_bindings_from_rs/test/do_not_bind/do_not_bind_cc_api_impl.rs new file mode 100644 index 000000000..21ce4051a --- /dev/null +++ b/cc_bindings_from_rs/test/do_not_bind/do_not_bind_cc_api_impl.rs @@ -0,0 +1,34 @@ +// 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 + +// Automatically @generated C++ bindings for the following Rust crate: +// do_not_bind_golden + +#![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)] +#![allow(improper_ctypes_definitions)] +#![deny(warnings)] + +extern crate alloc; +extern crate core; +const _: () = assert!(::std::mem::size_of::<::do_not_bind_golden::Struct>() == 4); +const _: () = assert!(::std::mem::align_of::<::do_not_bind_golden::Struct>() == 4); +#[unsafe(no_mangle)] +unsafe extern "C" fn __crubit_thunk_bound_uinherent_umethod( + __self: &'static ::do_not_bind_golden::Struct, +) -> i32 { + unsafe { ::do_not_bind_golden::Struct::bound_inherent_method(__self) } +} +const _: () = assert!(::core::mem::offset_of!(::do_not_bind_golden::Struct, value) == 0); +#[unsafe(no_mangle)] +unsafe extern "C" fn __crubit_thunk_bound_ufree_ufn() -> i32 { + unsafe { ::do_not_bind_golden::bound_free_fn() } +} +#[unsafe(no_mangle)] +unsafe extern "C" fn __crubit_thunk_Trait_ubound_utrait_umethod_udo_unot_ubind_ugolden_x0000003a_x0000003aStruct( + __self: &'static ::do_not_bind_golden::Struct, +) -> i32 { + unsafe { + <::do_not_bind_golden::Struct as ::do_not_bind_golden::Trait>::bound_trait_method(__self) + } +} diff --git a/cc_bindings_from_rs/test/do_not_bind/do_not_bind_test.cc b/cc_bindings_from_rs/test/do_not_bind/do_not_bind_test.cc new file mode 100644 index 000000000..9d8a9be57 --- /dev/null +++ b/cc_bindings_from_rs/test/do_not_bind/do_not_bind_test.cc @@ -0,0 +1,41 @@ +// 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 + +#include "cc_bindings_from_rs/test/do_not_bind/do_not_bind.h" + +#include "gtest/gtest.h" + +namespace do_not_bind { +namespace { + +using TraitImpl = rs_std::impl; + +// `do_not_bind` used to parse and then be silently dropped on associated items, +// so assert absence directly rather than relying only on the golden. These must +// be dependent on `T` for the requires-expression to soft-fail. +template +concept HasSuppressedInherentMethod = + requires(const T& s) { s.suppressed_inherent_method(); }; + +template +concept HasSuppressedTraitMethod = requires(const T& s) { + rs_std::impl::suppressed_trait_method(s); +}; + +static_assert(!HasSuppressedInherentMethod); +static_assert(!HasSuppressedTraitMethod); + +TEST(DoNotBindTest, UnannotatedItemsStillReceiveBindings) { + Struct s = {.value = 0}; + EXPECT_EQ(bound_free_fn(), 2); + EXPECT_EQ(s.bound_inherent_method(), 4); + EXPECT_EQ(TraitImpl::bound_trait_method(s), 6); +} + +TEST(DoNotBindTest, SuppressingOneMethodKeepsTheTraitImplemented) { + static_assert(TraitImpl::kIsImplemented); +} + +} // namespace +} // namespace do_not_bind