]>
Commit | Line | Data |
---|---|---|
353b0b11 | 1 | use clippy_utils::diagnostics::span_lint_and_then; |
9ffffee4 FG |
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}; | |
353b0b11 | 7 | use rustc_span::{BytePos, Pos}; |
9ffffee4 FG |
8 | use rustc_target::spec::abi::Abi; |
9 | ||
10 | declare_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 | } | |
36 | declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]); | |
37 | ||
38 | impl<'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 | } |