From 2dd9898d17872f26f75b0e3ea7120602e352f3c1 Mon Sep 17 00:00:00 2001 From: Krishna Sundarram Date: Sat, 18 Feb 2023 16:47:27 +0000 Subject: [PATCH] Add new lint no_mangle_with_rust_abi --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/no_mangle_with_rust_abi.rs | 60 +++++++++++++++++++++ tests/ui/must_use_candidates.fixed | 2 +- tests/ui/must_use_candidates.rs | 2 +- tests/ui/must_use_candidates.stderr | 10 +++- tests/ui/no_mangle_with_rust_abi.rs | 24 +++++++++ tests/ui/no_mangle_with_rust_abi.stderr | 27 ++++++++++ 9 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 clippy_lints/src/no_mangle_with_rust_abi.rs create mode 100644 tests/ui/no_mangle_with_rust_abi.rs create mode 100644 tests/ui/no_mangle_with_rust_abi.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9138a4fb9c58..6e01e19379e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4621,6 +4621,7 @@ Released 2018-09-13 [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect [`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding +[`no_mangle_with_rust_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_mangle_with_rust_abi [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2577cdc8c664..361ad822f882 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -449,6 +449,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::no_effect::NO_EFFECT_INFO, crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO, crate::no_effect::UNNECESSARY_OPERATION_INFO, + crate::no_mangle_with_rust_abi::NO_MANGLE_WITH_RUST_ABI_INFO, crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO, crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO, crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ce5df8c7a7be..bca28d81db3b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -219,6 +219,7 @@ mod neg_cmp_op_on_partial_ord; mod neg_multiply; mod new_without_default; mod no_effect; +mod no_mangle_with_rust_abi; mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; @@ -916,6 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)); store.register_late_pass(|_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters)); + store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/no_mangle_with_rust_abi.rs b/clippy_lints/src/no_mangle_with_rust_abi.rs new file mode 100644 index 000000000000..c47e1796313b --- /dev/null +++ b/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -0,0 +1,60 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_target::spec::abi::Abi; + +declare_clippy_lint! { + /// ### What it does + /// Checks for Rust ABI functions with the `#[no_mangle]` attribute. + /// + /// ### Why is this bad? + /// The Rust ABI is not stable, but in many simple cases matches enough + /// with the C ABI that it is possible to forget to add `extern "C"` to a + /// function called from C. Changes to the Rust ABI can break this at any + /// point. + /// + /// ### Example + /// ```rust + /// #[no_mangle] + /// fn example(arg_one: u32, arg_two: usize) {} + /// ``` + /// + /// Use instead: + /// ```rust + /// #[no_mangle] + /// extern "C" fn example(arg_one: u32, arg_two: usize) {} + /// ``` + #[clippy::version = "1.69.0"] + pub NO_MANGLE_WITH_RUST_ABI, + suspicious, + "convert Rust ABI functions to C ABI" +} +declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]); + +impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn(fn_sig, _, _) = &item.kind { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + for attr in attrs { + if let Some(ident) = attr.ident() + && ident.name.as_str() == "no_mangle" + && fn_sig.header.abi == Abi::Rust { + let mut applicability = Applicability::MachineApplicable; + let span = fn_sig.span; + span_lint_and_sugg( + cx, + NO_MANGLE_WITH_RUST_ABI, + span, + &format!("attribute #[no_mangle] set on a Rust ABI function"), + "try", + format!(r#"extern "C" {}"#, snippet_with_applicability(cx, span, "..", &mut applicability)), + applicability + ); + } + } + } + } +} diff --git a/tests/ui/must_use_candidates.fixed b/tests/ui/must_use_candidates.fixed index 04a74a009e09..9c8a39096509 100644 --- a/tests/ui/must_use_candidates.fixed +++ b/tests/ui/must_use_candidates.fixed @@ -84,7 +84,7 @@ pub unsafe fn mutates_static() -> usize { } #[no_mangle] -pub fn unmangled(i: bool) -> bool { +extern "C" pub fn unmangled(i: bool) -> bool { !i } diff --git a/tests/ui/must_use_candidates.rs b/tests/ui/must_use_candidates.rs index f04122f4eeab..94d3c83bdb93 100644 --- a/tests/ui/must_use_candidates.rs +++ b/tests/ui/must_use_candidates.rs @@ -84,7 +84,7 @@ pub unsafe fn mutates_static() -> usize { } #[no_mangle] -pub fn unmangled(i: bool) -> bool { +pub extern "C" fn unmangled(i: bool) -> bool { !i } diff --git a/tests/ui/must_use_candidates.stderr b/tests/ui/must_use_candidates.stderr index 0fa3849d03bf..a6e6c9f8b4c3 100644 --- a/tests/ui/must_use_candidates.stderr +++ b/tests/ui/must_use_candidates.stderr @@ -30,5 +30,13 @@ error: this function could have a `#[must_use]` attribute LL | pub fn arcd(_x: Arc) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc) -> bool` -error: aborting due to 5 previous errors +error: attribute #[no_mangle] set on a Rust ABI function + --> $DIR/must_use_candidates.rs:87:1 + | +LL | pub fn unmangled(i: bool) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `extern "C" pub fn unmangled(i: bool) -> bool` + | + = note: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings` + +error: aborting due to 6 previous errors diff --git a/tests/ui/no_mangle_with_rust_abi.rs b/tests/ui/no_mangle_with_rust_abi.rs new file mode 100644 index 000000000000..e440b3b13991 --- /dev/null +++ b/tests/ui/no_mangle_with_rust_abi.rs @@ -0,0 +1,24 @@ +#![allow(unused)] +#![warn(clippy::no_mangle_with_rust_abi)] + +#[no_mangle] +fn rust_abi_fn(arg_one: u32, arg_two: usize) {} + +#[no_mangle] +fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + arg_one: u32, + arg_two: usize, +) -> u32 { + 0 +} + +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) {} + +fn main() { + // test code goes here +} diff --git a/tests/ui/no_mangle_with_rust_abi.stderr b/tests/ui/no_mangle_with_rust_abi.stderr new file mode 100644 index 000000000000..be26309fa46d --- /dev/null +++ b/tests/ui/no_mangle_with_rust_abi.stderr @@ -0,0 +1,27 @@ +error: attribute #[no_mangle] set on a Rust ABI function + --> $DIR/no_mangle_with_rust_abi.rs:5:1 + | +LL | fn rust_abi_fn(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `extern "C" fn rust_abi_fn(arg_one: u32, arg_two: usize)` + | + = note: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings` + +error: attribute #[no_mangle] set on a Rust ABI function + --> $DIR/no_mangle_with_rust_abi.rs:8:1 + | +LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( +LL | | arg_one: u32, +LL | | arg_two: usize, +LL | | ) -> u32 { + | |________^ + | +help: try + | +LL + extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( +LL + arg_one: u32, +LL + arg_two: usize, +LL ~ ) -> u32 { + | + +error: aborting due to 2 previous errors +