From 0de994a368e9225fab812a7295f9350285cd55d3 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Sat, 10 May 2025 09:55:50 +0800 Subject: [PATCH] Warn when #[export_name] is used with generic functions --- compiler/rustc_lint/src/builtin.rs | 27 +-- .../ui/generics/export-name-on-generics.fixed | 157 +++++++++++++++++ tests/ui/generics/export-name-on-generics.rs | 159 ++++++++++++++++++ .../generics/export-name-on-generics.stderr | 144 ++++++++++++++++ 4 files changed, 477 insertions(+), 10 deletions(-) create mode 100644 tests/ui/generics/export-name-on-generics.fixed create mode 100644 tests/ui/generics/export-name-on-generics.rs create mode 100644 tests/ui/generics/export-name-on-generics.stderr diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 41b43f6479867..95e31e4af1e39 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -976,6 +976,9 @@ declare_lint! { /// ```rust /// #[unsafe(no_mangle)] /// fn foo(t: T) {} + /// + /// #[unsafe(export_name = "bar")] + /// fn bar(t: T) {} /// ``` /// /// {{produces}} @@ -983,10 +986,11 @@ declare_lint! { /// ### Explanation /// /// A function with generics must have its symbol mangled to accommodate - /// the generic parameter. The [`no_mangle` attribute] has no effect in - /// this situation, and should be removed. + /// the generic parameter. The [`no_mangle`] and [`export_name`] attributes + /// have no effect in this situation, and should be removed. /// - /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute + /// [`no_mangle`]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute + /// [`export_name`]: https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute NO_MANGLE_GENERIC_ITEMS, Warn, "generic items must be mangled" @@ -997,7 +1001,7 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { let attrs = cx.tcx.hir_attrs(it.hir_id()); - let check_no_mangle_on_generic_fn = |no_mangle_attr: &hir::Attribute, + let check_no_mangle_on_generic_fn = |attr: &hir::Attribute, impl_generics: Option<&hir::Generics<'_>>, generics: &hir::Generics<'_>, span| { @@ -1010,7 +1014,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { cx.emit_span_lint( NO_MANGLE_GENERIC_ITEMS, span, - BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span() }, + BuiltinNoMangleGeneric { suggestion: attr.span() }, ); break; } @@ -1019,8 +1023,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { }; match it.kind { hir::ItemKind::Fn { generics, .. } => { - if let Some(no_mangle_attr) = attr::find_by_name(attrs, sym::no_mangle) { - check_no_mangle_on_generic_fn(no_mangle_attr, None, generics, it.span); + if let Some(attr) = attr::find_by_name(attrs, sym::export_name) + .or_else(|| attr::find_by_name(attrs, sym::no_mangle)) + { + check_no_mangle_on_generic_fn(attr, None, generics, it.span); } } hir::ItemKind::Const(..) => { @@ -1048,11 +1054,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => { for it in *items { if let hir::AssocItemKind::Fn { .. } = it.kind { - if let Some(no_mangle_attr) = - attr::find_by_name(cx.tcx.hir_attrs(it.id.hir_id()), sym::no_mangle) + let attrs = cx.tcx.hir_attrs(it.id.hir_id()); + if let Some(attr) = attr::find_by_name(attrs, sym::export_name) + .or_else(|| attr::find_by_name(attrs, sym::no_mangle)) { check_no_mangle_on_generic_fn( - no_mangle_attr, + attr, Some(generics), cx.tcx.hir_get_generics(it.id.owner_id.def_id).unwrap(), it.span, diff --git a/tests/ui/generics/export-name-on-generics.fixed b/tests/ui/generics/export-name-on-generics.fixed new file mode 100644 index 0000000000000..4430cd9a2998a --- /dev/null +++ b/tests/ui/generics/export-name-on-generics.fixed @@ -0,0 +1,157 @@ +//@ run-rustfix +#![allow(dead_code, elided_named_lifetimes)] +#![deny(no_mangle_generic_items)] + +pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + +pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + +#[export_name = "baz"] +pub fn baz(x: &i32) -> &i32 { x } + +#[export_name = "qux"] +pub fn qux<'a>(x: &'a i32) -> &i32 { x } + +pub struct Foo; + +impl Foo { + + pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + + pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + pub fn baz(x: &i32) -> &i32 { x } + + #[export_name = "qux"] + pub fn qux<'a>(x: &'a i32) -> &i32 { x } +} + +trait Trait1 { + fn foo(); + extern "C" fn bar(); + fn baz(x: &i32) -> &i32; + fn qux<'a>(x: &'a i32) -> &i32; +} + +impl Trait1 for Foo { + + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + fn baz(x: &i32) -> &i32 { x } + + #[export_name = "qux"] + fn qux<'a>(x: &'a i32) -> &i32 { x } +} + +trait Trait2 { + fn foo(); + fn foo2(); + extern "C" fn bar(); + fn baz(x: &i32) -> &i32; + fn qux<'a>(x: &'a i32) -> &i32; +} + +impl Trait2 for Foo { + + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + + fn foo2() {} //~ ERROR functions generic over types or consts must be mangled + + + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + + fn baz(x: &i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled + + + fn qux<'a>(x: &'a i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled +} + +pub struct Bar(#[allow(dead_code)] T); + +impl Bar { + + pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + + pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + + pub fn baz() {} //~ ERROR functions generic over types or consts must be mangled +} + +impl Bar { + #[export_name = "qux"] + pub fn qux() {} +} + +trait Trait3 { + fn foo(); + extern "C" fn bar(); + fn baz(); +} + +impl Trait3 for Bar { + + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + + fn baz() {} //~ ERROR functions generic over types or consts must be mangled +} + +pub struct Baz<'a>(#[allow(dead_code)] &'a i32); + +impl<'a> Baz<'a> { + #[export_name = "foo"] + pub fn foo() {} + + #[export_name = "bar"] + pub fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +trait Trait4 { + fn foo(); + fn bar<'a>(x: &'a i32) -> &i32; +} + +impl Trait4 for Bar { + #[export_name = "foo"] + fn foo() {} + + #[export_name = "bar"] + fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +impl<'a> Trait4 for Baz<'a> { + #[export_name = "foo"] + fn foo() {} + + #[export_name = "bar"] + fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +trait Trait5 { + fn foo(); +} + +impl Trait5 for Foo { + #[export_name = "foo"] + fn foo() {} +} + +impl Trait5 for Bar { + #[export_name = "foo"] + fn foo() {} +} + +fn main() {} diff --git a/tests/ui/generics/export-name-on-generics.rs b/tests/ui/generics/export-name-on-generics.rs new file mode 100644 index 0000000000000..cbf1102196052 --- /dev/null +++ b/tests/ui/generics/export-name-on-generics.rs @@ -0,0 +1,159 @@ +//@ run-rustfix +#![allow(dead_code, elided_named_lifetimes)] +#![deny(no_mangle_generic_items)] + +#[export_name = "foo"] +pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + +#[export_name = "bar"] +pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + +#[export_name = "baz"] +pub fn baz(x: &i32) -> &i32 { x } + +#[export_name = "qux"] +pub fn qux<'a>(x: &'a i32) -> &i32 { x } + +pub struct Foo; + +impl Foo { + #[export_name = "foo"] + pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "bar"] + pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + pub fn baz(x: &i32) -> &i32 { x } + + #[export_name = "qux"] + pub fn qux<'a>(x: &'a i32) -> &i32 { x } +} + +trait Trait1 { + fn foo(); + extern "C" fn bar(); + fn baz(x: &i32) -> &i32; + fn qux<'a>(x: &'a i32) -> &i32; +} + +impl Trait1 for Foo { + #[export_name = "foo"] + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "bar"] + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + fn baz(x: &i32) -> &i32 { x } + + #[export_name = "qux"] + fn qux<'a>(x: &'a i32) -> &i32 { x } +} + +trait Trait2 { + fn foo(); + fn foo2(); + extern "C" fn bar(); + fn baz(x: &i32) -> &i32; + fn qux<'a>(x: &'a i32) -> &i32; +} + +impl Trait2 for Foo { + #[export_name = "foo"] + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "foo2"] + fn foo2() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + fn baz(x: &i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "qux"] + fn qux<'a>(x: &'a i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled +} + +pub struct Bar(#[allow(dead_code)] T); + +impl Bar { + #[export_name = "foo"] + pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "bar"] + pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + pub fn baz() {} //~ ERROR functions generic over types or consts must be mangled +} + +impl Bar { + #[export_name = "qux"] + pub fn qux() {} +} + +trait Trait3 { + fn foo(); + extern "C" fn bar(); + fn baz(); +} + +impl Trait3 for Bar { + #[export_name = "foo"] + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "bar"] + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + fn baz() {} //~ ERROR functions generic over types or consts must be mangled +} + +pub struct Baz<'a>(#[allow(dead_code)] &'a i32); + +impl<'a> Baz<'a> { + #[export_name = "foo"] + pub fn foo() {} + + #[export_name = "bar"] + pub fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +trait Trait4 { + fn foo(); + fn bar<'a>(x: &'a i32) -> &i32; +} + +impl Trait4 for Bar { + #[export_name = "foo"] + fn foo() {} + + #[export_name = "bar"] + fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +impl<'a> Trait4 for Baz<'a> { + #[export_name = "foo"] + fn foo() {} + + #[export_name = "bar"] + fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +trait Trait5 { + fn foo(); +} + +impl Trait5 for Foo { + #[export_name = "foo"] + fn foo() {} +} + +impl Trait5 for Bar { + #[export_name = "foo"] + fn foo() {} +} + +fn main() {} diff --git a/tests/ui/generics/export-name-on-generics.stderr b/tests/ui/generics/export-name-on-generics.stderr new file mode 100644 index 0000000000000..7bc7b8ca55931 --- /dev/null +++ b/tests/ui/generics/export-name-on-generics.stderr @@ -0,0 +1,144 @@ +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:6:1 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/export-name-on-generics.rs:3:9 + | +LL | #![deny(no_mangle_generic_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:9:1 + | +LL | #[export_name = "bar"] + | ---------------------- help: remove this attribute +LL | pub extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:21:5 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:24:5 + | +LL | #[export_name = "bar"] + | ---------------------- help: remove this attribute +LL | pub extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:42:5 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | fn foo() {} + | ^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:45:5 + | +LL | #[export_name = "bar"] + | ---------------------- help: remove this attribute +LL | extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:64:5 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | fn foo() {} + | ^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:67:5 + | +LL | #[export_name = "foo2"] + | ----------------------- help: remove this attribute +LL | fn foo2() {} + | ^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:70:5 + | +LL | #[export_name = "baz"] + | ---------------------- help: remove this attribute +LL | extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:73:5 + | +LL | #[export_name = "baz"] + | ---------------------- help: remove this attribute +LL | fn baz(x: &i32) -> &i32 { x } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:76:5 + | +LL | #[export_name = "qux"] + | ---------------------- help: remove this attribute +LL | fn qux<'a>(x: &'a i32) -> &i32 { x } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:83:5 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:86:5 + | +LL | #[export_name = "bar"] + | ---------------------- help: remove this attribute +LL | pub extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:89:5 + | +LL | #[export_name = "baz"] + | ---------------------- help: remove this attribute +LL | pub fn baz() {} + | ^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:105:5 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | fn foo() {} + | ^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:108:5 + | +LL | #[export_name = "bar"] + | ---------------------- help: remove this attribute +LL | extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:111:5 + | +LL | #[export_name = "baz"] + | ---------------------- help: remove this attribute +LL | fn baz() {} + | ^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors +