]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
Merge 1.70 into proxmox/bookworm
[rustc.git] / src / tools / clippy / clippy_lints / src / no_mangle_with_rust_abi.rs
CommitLineData
353b0b11 1use clippy_utils::diagnostics::span_lint_and_then;
9ffffee4
FG
2use clippy_utils::source::snippet_with_applicability;
3use rustc_errors::Applicability;
4use rustc_hir::{Item, ItemKind};
5use rustc_lint::{LateContext, LateLintPass};
6use rustc_session::{declare_lint_pass, declare_tool_lint};
353b0b11 7use rustc_span::{BytePos, Pos};
9ffffee4
FG
8use rustc_target::spec::abi::Abi;
9
10declare_clippy_lint! {
11 /// ### What it does
12 /// Checks for Rust ABI functions with the `#[no_mangle]` attribute.
13 ///
14 /// ### Why is this bad?
15 /// The Rust ABI is not stable, but in many simple cases matches
16 /// enough with the C ABI that it is possible to forget to add
17 /// `extern "C"` to a function called from C. Changes to the
18 /// Rust ABI can break this at any point.
19 ///
20 /// ### Example
21 /// ```rust
22 /// #[no_mangle]
23 /// fn example(arg_one: u32, arg_two: usize) {}
24 /// ```
25 ///
26 /// Use instead:
27 /// ```rust
28 /// #[no_mangle]
29 /// extern "C" fn example(arg_one: u32, arg_two: usize) {}
30 /// ```
31 #[clippy::version = "1.69.0"]
32 pub NO_MANGLE_WITH_RUST_ABI,
33 pedantic,
34 "convert Rust ABI functions to C ABI"
35}
36declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]);
37
38impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
39 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
40 if let ItemKind::Fn(fn_sig, _, _) = &item.kind {
41 let attrs = cx.tcx.hir().attrs(item.hir_id());
353b0b11
FG
42 let mut app = Applicability::MaybeIncorrect;
43 let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut app);
9ffffee4
FG
44 for attr in attrs {
45 if let Some(ident) = attr.ident()
46 && ident.name == rustc_span::sym::no_mangle
47 && fn_sig.header.abi == Abi::Rust
353b0b11
FG
48 && let Some((fn_attrs, _)) = snippet.split_once("fn")
49 && !fn_attrs.contains("extern")
50 {
51 let sugg_span = fn_sig.span
52 .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
53 .shrink_to_lo();
9ffffee4 54
353b0b11 55 span_lint_and_then(
9ffffee4
FG
56 cx,
57 NO_MANGLE_WITH_RUST_ABI,
58 fn_sig.span,
353b0b11
FG
59 "`#[no_mangle]` set on a function with the default (`Rust`) ABI",
60 |diag| {
61 diag.span_suggestion(sugg_span, "set an ABI", "extern \"C\" ", app)
62 .span_suggestion(sugg_span, "or explicitly set the default", "extern \"Rust\" ", app);
63 },
9ffffee4
FG
64 );
65 }
66 }
67 }
68 }
69}