Skip to content

Commit 659112c

Browse files
committed
Auto merge of rust-lang#10369 - nindalf:no_mangle_lint, r=llogiq
Add new lint no_mangle_with_rust_abi Fixes issue rust-lang#10347 This PR adds a new lint `no_mangle_with_rust_abi` that suggests converting a function to the C ABI to if the function has the `#[no_mangle]` attribute and `Abi == Abi::Rust`. It will not run for any of the other variants defined in [rustc_target::spec::abi::Abi](https://doc.rust-lang.org/beta/nightly-rustc/rustc_target/spec/abi/enum.Abi.html), nor suggest any conversion other than conversion to the C ABI. Functions that explicitly opt into the Rust ABI with `extern "Rust"` are ignored by this lint. --- changelog: [`no_mangle_with_rust_abi`]: add lint that converts Rust ABI functions with the `#[no_mangle]` attribute to C ABI
2 parents f9adb83 + 00c294a commit 659112c

9 files changed

+212
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4622,6 +4622,7 @@ Released 2018-09-13
46224622
[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
46234623
[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace
46244624
[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
4625+
[`no_mangle_with_rust_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_mangle_with_rust_abi
46254626
[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
46264627
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
46274628
[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
450450
crate::no_effect::NO_EFFECT_INFO,
451451
crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO,
452452
crate::no_effect::UNNECESSARY_OPERATION_INFO,
453+
crate::no_mangle_with_rust_abi::NO_MANGLE_WITH_RUST_ABI_INFO,
453454
crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO,
454455
crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO,
455456
crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ mod neg_cmp_op_on_partial_ord;
219219
mod neg_multiply;
220220
mod new_without_default;
221221
mod no_effect;
222+
mod no_mangle_with_rust_abi;
222223
mod non_copy_const;
223224
mod non_expressive_names;
224225
mod non_octal_unix_permissions;
@@ -921,6 +922,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
921922
avoid_breaking_exported_api,
922923
))
923924
});
925+
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
924926
// add lints here, do not remove this comment, it's used in `new_lint`
925927
}
926928

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet_with_applicability;
3+
use rustc_errors::Applicability;
4+
use rustc_hir::{Item, ItemKind};
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use rustc_target::spec::abi::Abi;
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Checks for Rust ABI functions with the `#[no_mangle]` attribute.
12+
///
13+
/// ### Why is this bad?
14+
/// The Rust ABI is not stable, but in many simple cases matches
15+
/// enough with the C ABI that it is possible to forget to add
16+
/// `extern "C"` to a function called from C. Changes to the
17+
/// Rust ABI can break this at any point.
18+
///
19+
/// ### Example
20+
/// ```rust
21+
/// #[no_mangle]
22+
/// fn example(arg_one: u32, arg_two: usize) {}
23+
/// ```
24+
///
25+
/// Use instead:
26+
/// ```rust
27+
/// #[no_mangle]
28+
/// extern "C" fn example(arg_one: u32, arg_two: usize) {}
29+
/// ```
30+
#[clippy::version = "1.69.0"]
31+
pub NO_MANGLE_WITH_RUST_ABI,
32+
pedantic,
33+
"convert Rust ABI functions to C ABI"
34+
}
35+
declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]);
36+
37+
impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
38+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
39+
if let ItemKind::Fn(fn_sig, _, _) = &item.kind {
40+
let attrs = cx.tcx.hir().attrs(item.hir_id());
41+
let mut applicability = Applicability::MachineApplicable;
42+
let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut applicability);
43+
for attr in attrs {
44+
if let Some(ident) = attr.ident()
45+
&& ident.name == rustc_span::sym::no_mangle
46+
&& fn_sig.header.abi == Abi::Rust
47+
&& !snippet.contains("extern") {
48+
49+
let suggestion = snippet.split_once("fn")
50+
.map_or(String::new(), |(first, second)| format!(r#"{first}extern "C" fn{second}"#));
51+
52+
span_lint_and_sugg(
53+
cx,
54+
NO_MANGLE_WITH_RUST_ABI,
55+
fn_sig.span,
56+
"attribute #[no_mangle] set on a Rust ABI function",
57+
"try",
58+
suggestion,
59+
applicability
60+
);
61+
}
62+
}
63+
}
64+
}
65+
}

tests/ui/must_use_candidates.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub unsafe fn mutates_static() -> usize {
8484
}
8585

8686
#[no_mangle]
87-
pub fn unmangled(i: bool) -> bool {
87+
pub extern "C" fn unmangled(i: bool) -> bool {
8888
!i
8989
}
9090

tests/ui/must_use_candidates.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub unsafe fn mutates_static() -> usize {
8484
}
8585

8686
#[no_mangle]
87-
pub fn unmangled(i: bool) -> bool {
87+
pub extern "C" fn unmangled(i: bool) -> bool {
8888
!i
8989
}
9090

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::no_mangle_with_rust_abi)]
5+
6+
#[no_mangle]
7+
extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
8+
9+
#[no_mangle]
10+
pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
11+
12+
/// # Safety
13+
/// This function shouldn't be called unless the horsemen are ready
14+
#[no_mangle]
15+
pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
16+
17+
/// # Safety
18+
/// This function shouldn't be called unless the horsemen are ready
19+
#[no_mangle]
20+
unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
21+
22+
#[no_mangle]
23+
extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
24+
arg_one: u32,
25+
arg_two: usize,
26+
) -> u32 {
27+
0
28+
}
29+
30+
// Must not run on functions that explicitly opt in to Rust ABI with `extern "Rust"`
31+
#[no_mangle]
32+
#[rustfmt::skip]
33+
extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {}
34+
35+
fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {}
36+
37+
#[no_mangle]
38+
extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {}
39+
40+
extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {}
41+
42+
extern "C" {
43+
fn c_abi_in_block(arg_one: u32, arg_two: usize);
44+
}
45+
46+
fn main() {
47+
// test code goes here
48+
}

tests/ui/no_mangle_with_rust_abi.rs

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::no_mangle_with_rust_abi)]
5+
6+
#[no_mangle]
7+
fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
8+
9+
#[no_mangle]
10+
pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
11+
12+
/// # Safety
13+
/// This function shouldn't be called unless the horsemen are ready
14+
#[no_mangle]
15+
pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
16+
17+
/// # Safety
18+
/// This function shouldn't be called unless the horsemen are ready
19+
#[no_mangle]
20+
unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
21+
22+
#[no_mangle]
23+
fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
24+
arg_one: u32,
25+
arg_two: usize,
26+
) -> u32 {
27+
0
28+
}
29+
30+
// Must not run on functions that explicitly opt in to Rust ABI with `extern "Rust"`
31+
#[no_mangle]
32+
#[rustfmt::skip]
33+
extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {}
34+
35+
fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {}
36+
37+
#[no_mangle]
38+
extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {}
39+
40+
extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {}
41+
42+
extern "C" {
43+
fn c_abi_in_block(arg_one: u32, arg_two: usize);
44+
}
45+
46+
fn main() {
47+
// test code goes here
48+
}
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
error: attribute #[no_mangle] set on a Rust ABI function
2+
--> $DIR/no_mangle_with_rust_abi.rs:7:1
3+
|
4+
LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize)`
6+
|
7+
= note: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings`
8+
9+
error: attribute #[no_mangle] set on a Rust ABI function
10+
--> $DIR/no_mangle_with_rust_abi.rs:10:1
11+
|
12+
LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize)`
14+
15+
error: attribute #[no_mangle] set on a Rust ABI function
16+
--> $DIR/no_mangle_with_rust_abi.rs:15:1
17+
|
18+
LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize)`
20+
21+
error: attribute #[no_mangle] set on a Rust ABI function
22+
--> $DIR/no_mangle_with_rust_abi.rs:20:1
23+
|
24+
LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize)`
26+
27+
error: attribute #[no_mangle] set on a Rust ABI function
28+
--> $DIR/no_mangle_with_rust_abi.rs:23:1
29+
|
30+
LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
31+
LL | | arg_one: u32,
32+
LL | | arg_two: usize,
33+
LL | | ) -> u32 {
34+
| |________^
35+
|
36+
help: try
37+
|
38+
LL + extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
39+
LL + arg_one: u32,
40+
LL + arg_two: usize,
41+
LL ~ ) -> u32 {
42+
|
43+
44+
error: aborting due to 5 previous errors
45+

0 commit comments

Comments
 (0)