Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

no_mangle attribute requires unsafe in Rust 2024 #13631

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions clippy_lints/src/no_mangle_with_rust_abi.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::{snippet, snippet_with_applicability};
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
Expand All @@ -18,13 +18,13 @@ declare_clippy_lint! {
/// Rust ABI can break this at any point.
///
/// ### Example
/// ```no_run
/// ```rust,ignore
/// #[no_mangle]
/// fn example(arg_one: u32, arg_two: usize) {}
/// ```
///
/// Use instead:
/// ```no_run
/// ```rust,ignore
/// #[no_mangle]
/// extern "C" fn example(arg_one: u32, arg_two: usize) {}
/// ```
Expand All @@ -40,24 +40,25 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
if let ItemKind::Fn(fn_sig, _, _) = &item.kind {
let attrs = cx.tcx.hir().attrs(item.hir_id());
let mut app = Applicability::MaybeIncorrect;
let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut app);
let fn_snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut app);
for attr in attrs {
if let Some(ident) = attr.ident()
&& ident.name == rustc_span::sym::no_mangle
&& fn_sig.header.abi == Abi::Rust
&& let Some((fn_attrs, _)) = snippet.split_once("fn")
&& let Some((fn_attrs, _)) = fn_snippet.split_once("fn")
&& !fn_attrs.contains("extern")
{
let sugg_span = fn_sig
.span
.with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
.shrink_to_lo();
let attr_snippet = snippet(cx, attr.span, "..");

span_lint_and_then(
cx,
NO_MANGLE_WITH_RUST_ABI,
fn_sig.span,
"`#[no_mangle]` set on a function with the default (`Rust`) ABI",
format!("`{attr_snippet}` set on a function with the default (`Rust`) ABI"),
|diag| {
diag.span_suggestion(sugg_span, "set an ABI", "extern \"C\" ", app)
.span_suggestion(sugg_span, "or explicitly set the default", "extern \"Rust\" ", app);
Expand Down
24 changes: 12 additions & 12 deletions tests/ui/no_mangle_with_rust_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,44 @@
#![allow(unused)]
#![warn(clippy::no_mangle_with_rust_abi)]

#[no_mangle]
#[unsafe(no_mangle)]
fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
//~| NOTE: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings`

#[no_mangle]
#[unsafe(no_mangle)]
pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI

/// # Safety
/// This function shouldn't be called unless the horsemen are ready
#[no_mangle]
#[unsafe(no_mangle)]
pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI

/// # Safety
/// This function shouldn't be called unless the horsemen are ready
#[no_mangle]
#[unsafe(no_mangle)]
unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI

#[no_mangle]
#[unsafe(no_mangle)]
fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
arg_one: u32,
arg_two: usize,
) -> u32 {
0
}

// Must not run on functions that explicitly opt in to using the Rust ABI with `extern "Rust"`
#[no_mangle]
#[unsafe(no_mangle)]
#[rustfmt::skip]
extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {}

fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {}

#[no_mangle]
#[unsafe(no_mangle)]
extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {}

extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {}
Expand Down
10 changes: 5 additions & 5 deletions tests/ui/no_mangle_with_rust_abi.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
--> tests/ui/no_mangle_with_rust_abi.rs:6:1
|
LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
Expand All @@ -15,7 +15,7 @@ help: or explicitly set the default
LL | extern "Rust" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
| +++++++++++++

error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
--> tests/ui/no_mangle_with_rust_abi.rs:11:1
|
LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
Expand All @@ -30,7 +30,7 @@ help: or explicitly set the default
LL | pub extern "Rust" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
| +++++++++++++

error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
--> tests/ui/no_mangle_with_rust_abi.rs:17:1
|
LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
Expand All @@ -45,7 +45,7 @@ help: or explicitly set the default
LL | pub unsafe extern "Rust" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
| +++++++++++++

error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
--> tests/ui/no_mangle_with_rust_abi.rs:23:1
|
LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
Expand All @@ -60,7 +60,7 @@ help: or explicitly set the default
LL | unsafe extern "Rust" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
| +++++++++++++

error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
--> tests/ui/no_mangle_with_rust_abi.rs:27:1
|
LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
Expand Down
57 changes: 57 additions & 0 deletions tests/ui/no_mangle_with_rust_abi_2021.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//@edition:2021
//
// Edition 2024 requires the use of #[unsafe(no_mangle)]

//@no-rustfix: overlapping suggestions
#![allow(unused)]
#![warn(clippy::no_mangle_with_rust_abi)]

#[no_mangle]
fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
//~| NOTE: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings`

#[no_mangle]
pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI

/// # Safety
/// This function shouldn't be called unless the horsemen are ready
#[no_mangle]
pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI

/// # Safety
/// This function shouldn't be called unless the horsemen are ready
#[no_mangle]
unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI

#[no_mangle]
fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
arg_one: u32,
arg_two: usize,
) -> u32 {
0
}

// Must not run on functions that explicitly opt in to using the Rust ABI with `extern "Rust"`
#[no_mangle]
#[rustfmt::skip]
extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {}

fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {}

#[no_mangle]
extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {}

extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {}

extern "C" {
fn c_abi_in_block(arg_one: u32, arg_two: usize);
}

fn main() {
// test code goes here
}
83 changes: 83 additions & 0 deletions tests/ui/no_mangle_with_rust_abi_2021.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
--> tests/ui/no_mangle_with_rust_abi_2021.rs:10:1
|
LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::no_mangle_with_rust_abi)]`
help: set an ABI
|
LL | extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
| ++++++++++
help: or explicitly set the default
|
LL | extern "Rust" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
| +++++++++++++

error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
--> tests/ui/no_mangle_with_rust_abi_2021.rs:15:1
|
LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: set an ABI
|
LL | pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
| ++++++++++
help: or explicitly set the default
|
LL | pub extern "Rust" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
| +++++++++++++

error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
--> tests/ui/no_mangle_with_rust_abi_2021.rs:21:1
|
LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: set an ABI
|
LL | pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
| ++++++++++
help: or explicitly set the default
|
LL | pub unsafe extern "Rust" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
| +++++++++++++

error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
--> tests/ui/no_mangle_with_rust_abi_2021.rs:27:1
|
LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: set an ABI
|
LL | unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
| ++++++++++
help: or explicitly set the default
|
LL | unsafe extern "Rust" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
| +++++++++++++

error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
--> tests/ui/no_mangle_with_rust_abi_2021.rs:31:1
|
LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
LL | |
LL | | arg_one: u32,
LL | | arg_two: usize,
LL | | ) -> u32 {
| |________^
|
help: set an ABI
|
LL | extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
| ++++++++++
help: or explicitly set the default
|
LL | extern "Rust" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
| +++++++++++++

error: aborting due to 5 previous errors