summaryrefslogtreecommitdiffstats
path: root/src/test/ui/lint/clashing-extern-fn.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/ui/lint/clashing-extern-fn.rs')
-rw-r--r--src/test/ui/lint/clashing-extern-fn.rs417
1 files changed, 417 insertions, 0 deletions
diff --git a/src/test/ui/lint/clashing-extern-fn.rs b/src/test/ui/lint/clashing-extern-fn.rs
new file mode 100644
index 000000000..809e06026
--- /dev/null
+++ b/src/test/ui/lint/clashing-extern-fn.rs
@@ -0,0 +1,417 @@
+// check-pass
+// aux-build:external_extern_fn.rs
+#![crate_type = "lib"]
+#![warn(clashing_extern_declarations)]
+
+mod redeclared_different_signature {
+ mod a {
+ extern "C" {
+ fn clash(x: u8);
+ }
+ }
+ mod b {
+ extern "C" {
+ fn clash(x: u64); //~ WARN `clash` redeclared with a different signature
+ }
+ }
+}
+
+mod redeclared_same_signature {
+ mod a {
+ extern "C" {
+ fn no_clash(x: u8);
+ }
+ }
+ mod b {
+ extern "C" {
+ fn no_clash(x: u8);
+ }
+ }
+}
+
+extern crate external_extern_fn;
+mod extern_no_clash {
+ // Should not clash with external_extern_fn::extern_fn.
+ extern "C" {
+ fn extern_fn(x: u8);
+ }
+}
+
+extern "C" {
+ fn some_other_new_name(x: i16);
+
+ #[link_name = "extern_link_name"]
+ fn some_new_name(x: i16);
+
+ #[link_name = "link_name_same"]
+ fn both_names_different(x: i16);
+}
+
+fn link_name_clash() {
+ extern "C" {
+ fn extern_link_name(x: u32);
+ //~^ WARN `extern_link_name` redeclared with a different signature
+
+ #[link_name = "some_other_new_name"]
+ //~^ WARN `some_other_extern_link_name` redeclares `some_other_new_name` with a different
+ fn some_other_extern_link_name(x: u32);
+
+ #[link_name = "link_name_same"]
+ //~^ WARN `other_both_names_different` redeclares `link_name_same` with a different
+ fn other_both_names_different(x: u32);
+ }
+}
+
+mod a {
+ extern "C" {
+ fn different_mod(x: u8);
+ }
+}
+mod b {
+ extern "C" {
+ fn different_mod(x: u64); //~ WARN `different_mod` redeclared with a different signature
+ }
+}
+
+extern "C" {
+ fn variadic_decl(x: u8, ...);
+}
+
+fn variadic_clash() {
+ extern "C" {
+ fn variadic_decl(x: u8); //~ WARN `variadic_decl` redeclared with a different signature
+ }
+}
+
+#[no_mangle]
+fn no_mangle_name(x: u8) {}
+
+extern "C" {
+ #[link_name = "unique_link_name"]
+ fn link_name_specified(x: u8);
+}
+
+fn tricky_no_clash() {
+ extern "C" {
+ // Shouldn't warn, because the declaration above actually declares a different symbol (and
+ // Rust's name resolution rules around shadowing will handle this gracefully).
+ fn link_name_specified() -> u32;
+
+ // The case of a no_mangle name colliding with an extern decl (see #28179) is related but
+ // shouldn't be reported by ClashingExternDeclarations, because this is an example of
+ // unmangled name clash causing bad behaviour in functions with a defined body.
+ fn no_mangle_name() -> u32;
+ }
+}
+
+mod banana {
+ mod one {
+ #[repr(C)]
+ struct Banana {
+ weight: u32,
+ length: u16,
+ }
+ extern "C" {
+ fn weigh_banana(count: *const Banana) -> u64;
+ }
+ }
+
+ mod two {
+ #[repr(C)]
+ struct Banana {
+ weight: u32,
+ length: u16,
+ } // note: distinct type
+ // This should not trigger the lint because two::Banana is structurally equivalent to
+ // one::Banana.
+ extern "C" {
+ fn weigh_banana(count: *const Banana) -> u64;
+ }
+ }
+
+ mod three {
+ // This _should_ trigger the lint, because repr(packed) should generate a struct that has a
+ // different layout.
+ #[repr(packed)]
+ struct Banana {
+ weight: u32,
+ length: u16,
+ }
+ #[allow(improper_ctypes)]
+ extern "C" {
+ fn weigh_banana(count: *const Banana) -> u64;
+ //~^ WARN `weigh_banana` redeclared with a different signature
+ }
+ }
+}
+
+mod sameish_members {
+ mod a {
+ #[repr(C)]
+ struct Point {
+ x: i16,
+ y: i16,
+ }
+
+ extern "C" {
+ fn draw_point(p: Point);
+ }
+ }
+ mod b {
+ #[repr(C)]
+ struct Point {
+ coordinates: [i16; 2],
+ }
+
+ // It's possible we are overconservative for this case, as accessing the elements of the
+ // coordinates array might end up correctly accessing `.x` and `.y`. However, this may not
+ // always be the case, for every architecture and situation. This is also a really odd
+ // thing to do anyway.
+ extern "C" {
+ fn draw_point(p: Point);
+ //~^ WARN `draw_point` redeclared with a different signature
+ }
+ }
+}
+
+mod same_sized_members_clash {
+ mod a {
+ #[repr(C)]
+ struct Point3 {
+ x: f32,
+ y: f32,
+ z: f32,
+ }
+ extern "C" {
+ fn origin() -> Point3;
+ }
+ }
+ mod b {
+ #[repr(C)]
+ struct Point3 {
+ x: i32,
+ y: i32,
+ z: i32, // NOTE: Incorrectly redeclared as i32
+ }
+ extern "C" {
+ fn origin() -> Point3; //~ WARN `origin` redeclared with a different signature
+ }
+ }
+}
+
+mod transparent {
+ #[repr(transparent)]
+ struct T(usize);
+ mod a {
+ use super::T;
+ extern "C" {
+ fn transparent() -> T;
+ fn transparent_incorrect() -> T;
+ }
+ }
+
+ mod b {
+ extern "C" {
+ // Shouldn't warn here, because repr(transparent) guarantees that T's layout is the
+ // same as just the usize.
+ fn transparent() -> usize;
+
+ // Should warn, because there's a signedness conversion here:
+ fn transparent_incorrect() -> isize;
+ //~^ WARN `transparent_incorrect` redeclared with a different signature
+ }
+ }
+}
+
+mod missing_return_type {
+ mod a {
+ extern "C" {
+ fn missing_return_type() -> usize;
+ }
+ }
+
+ mod b {
+ extern "C" {
+ // This should output a warning because we can't assume that the first declaration is
+ // the correct one -- if this one is the correct one, then calling the usize-returning
+ // version would allow reads into uninitialised memory.
+ fn missing_return_type();
+ //~^ WARN `missing_return_type` redeclared with a different signature
+ }
+ }
+}
+
+mod non_zero_and_non_null {
+ mod a {
+ extern "C" {
+ fn non_zero_usize() -> core::num::NonZeroUsize;
+ fn non_null_ptr() -> core::ptr::NonNull<usize>;
+ }
+ }
+ mod b {
+ extern "C" {
+ // If there's a clash in either of these cases you're either gaining an incorrect
+ // invariant that the value is non-zero, or you're missing out on that invariant. Both
+ // cases are warning for, from both a caller-convenience and optimisation perspective.
+ fn non_zero_usize() -> usize;
+ //~^ WARN `non_zero_usize` redeclared with a different signature
+ fn non_null_ptr() -> *const usize;
+ //~^ WARN `non_null_ptr` redeclared with a different signature
+ }
+ }
+}
+
+// See #75739
+mod non_zero_transparent {
+ mod a1 {
+ use std::num::NonZeroUsize;
+ extern "C" {
+ fn f1() -> NonZeroUsize;
+ }
+ }
+
+ mod b1 {
+ #[repr(transparent)]
+ struct X(NonZeroUsize);
+ use std::num::NonZeroUsize;
+ extern "C" {
+ fn f1() -> X;
+ }
+ }
+
+ mod a2 {
+ use std::num::NonZeroUsize;
+ extern "C" {
+ fn f2() -> NonZeroUsize;
+ }
+ }
+
+ mod b2 {
+ #[repr(transparent)]
+ struct X1(NonZeroUsize);
+
+ #[repr(transparent)]
+ struct X(X1);
+
+ use std::num::NonZeroUsize;
+ extern "C" {
+ // Same case as above, but with two layers of newtyping.
+ fn f2() -> X;
+ }
+ }
+
+ mod a3 {
+ #[repr(transparent)]
+ struct X(core::ptr::NonNull<i32>);
+
+ use std::num::NonZeroUsize;
+ extern "C" {
+ fn f3() -> X;
+ }
+ }
+
+ mod b3 {
+ extern "C" {
+ fn f3() -> core::ptr::NonNull<i32>;
+ }
+ }
+
+ mod a4 {
+ #[repr(transparent)]
+ enum E {
+ X(std::num::NonZeroUsize),
+ }
+ extern "C" {
+ fn f4() -> E;
+ }
+ }
+
+ mod b4 {
+ extern "C" {
+ fn f4() -> std::num::NonZeroUsize;
+ }
+ }
+}
+
+mod null_optimised_enums {
+ mod a {
+ extern "C" {
+ fn option_non_zero_usize() -> usize;
+ fn option_non_zero_isize() -> isize;
+ fn option_non_null_ptr() -> *const usize;
+
+ fn option_non_zero_usize_incorrect() -> usize;
+ fn option_non_null_ptr_incorrect() -> *const usize;
+ }
+ }
+ mod b {
+ extern "C" {
+ // This should be allowed, because these conversions are guaranteed to be FFI-safe (see
+ // #60300)
+ fn option_non_zero_usize() -> Option<core::num::NonZeroUsize>;
+ fn option_non_zero_isize() -> Option<core::num::NonZeroIsize>;
+ fn option_non_null_ptr() -> Option<core::ptr::NonNull<usize>>;
+
+ // However, these should be incorrect (note isize instead of usize)
+ fn option_non_zero_usize_incorrect() -> isize;
+ //~^ WARN `option_non_zero_usize_incorrect` redeclared with a different signature
+ fn option_non_null_ptr_incorrect() -> *const isize;
+ //~^ WARN `option_non_null_ptr_incorrect` redeclared with a different signature
+ }
+ }
+}
+
+#[allow(improper_ctypes)]
+mod unknown_layout {
+ mod a {
+ extern "C" {
+ pub fn generic(l: Link<u32>);
+ }
+ pub struct Link<T> {
+ pub item: T,
+ pub next: *const Link<T>,
+ }
+ }
+
+ mod b {
+ extern "C" {
+ pub fn generic(l: Link<u32>);
+ }
+ pub struct Link<T> {
+ pub item: T,
+ pub next: *const Link<T>,
+ }
+ }
+}
+
+mod hidden_niche {
+ mod a {
+ extern "C" {
+ fn hidden_niche_transparent() -> usize;
+ fn hidden_niche_transparent_no_niche() -> usize;
+ fn hidden_niche_unsafe_cell() -> usize;
+ }
+ }
+ mod b {
+ use std::cell::UnsafeCell;
+ use std::num::NonZeroUsize;
+
+ #[repr(transparent)]
+ struct Transparent { x: NonZeroUsize }
+
+ #[repr(transparent)]
+ struct TransparentNoNiche { y: UnsafeCell<NonZeroUsize> }
+
+ extern "C" {
+ fn hidden_niche_transparent() -> Option<Transparent>;
+
+ fn hidden_niche_transparent_no_niche() -> Option<TransparentNoNiche>;
+ //~^ WARN redeclared with a different signature
+ //~| WARN block uses type `Option<TransparentNoNiche>`, which is not FFI-safe
+
+ fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZeroUsize>>;
+ //~^ WARN redeclared with a different signature
+ //~| WARN block uses type `Option<UnsafeCell<NonZeroUsize>>`, which is not FFI-safe
+ }
+ }
+}