Skip to content

Commit 515cc2b

Browse files
committed
Add [extern_without_abi] lint
1 parent 43e3384 commit 515cc2b

14 files changed

+350
-36
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5456,6 +5456,7 @@ Released 2018-09-13
54565456
[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
54575457
[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
54585458
[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
5459+
[`extern_without_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#extern_without_abi
54595460
[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
54605461
[`extra_unused_type_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters
54615462
[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
173173
crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO,
174174
crate::exit::EXIT_INFO,
175175
crate::explicit_write::EXPLICIT_WRITE_INFO,
176+
crate::extern_without_abi::EXTERN_WITHOUT_ABI_INFO,
176177
crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO,
177178
crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO,
178179
crate::field_scoped_visibility_modifiers::FIELD_SCOPED_VISIBILITY_MODIFIERS_INFO,
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::is_from_proc_macro;
3+
use clippy_utils::source::snippet;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::def_id::LocalDefId;
6+
use rustc_hir::intravisit::FnKind;
7+
use rustc_hir::{Body, FnDecl, FnHeader, Item, ItemKind};
8+
use rustc_lint::{LateContext, LateLintPass, LintContext};
9+
use rustc_middle::lint::in_external_macro;
10+
use rustc_session::declare_lint_pass;
11+
use rustc_span::Span;
12+
use rustc_target::spec::abi::Abi;
13+
14+
const LINT_MSG: &str = "`extern` missing explicit ABI";
15+
const LINT_HELP_MSG: &str = "consider using";
16+
17+
const EXTERN: &str = "extern";
18+
const FN: &str = "fn";
19+
const OPEN_BRACE: &str = "{";
20+
const ABI: &str = "\"C\"";
21+
22+
declare_clippy_lint! {
23+
/// ### What it does
24+
/// Checks for usage of `extern` without an explicit ABI.
25+
///
26+
/// ### Why is this bad?
27+
/// Explicitly declaring the ABI is the recommended convention. See:
28+
/// [Rust Style Guide - `extern` items](https://doc.rust-lang.org/nightly/style-guide/items.html#extern-items)
29+
///
30+
/// It's also enforced by `rustfmt` when the `force_explicit_abi` option is enabled. See:
31+
/// [Configuring Rustfmt](https://rust-lang.github.io/rustfmt/?version=master&search=#force_explicit_abi)
32+
///
33+
/// ### Example
34+
/// ```no_run
35+
/// extern fn foo() {}
36+
///
37+
/// extern {
38+
/// fn bar();
39+
/// }
40+
/// ```
41+
/// Use instead:
42+
/// ```no_run
43+
/// extern "C" fn foo() {}
44+
///
45+
/// extern "C" {
46+
/// fn bar();
47+
/// }
48+
/// ```
49+
#[clippy::version = "1.83.0"]
50+
pub EXTERN_WITHOUT_ABI,
51+
style,
52+
"`extern` missing explicit ABI"
53+
}
54+
55+
declare_lint_pass!(ExternWithoutAbi => [EXTERN_WITHOUT_ABI]);
56+
57+
impl<'tcx> LateLintPass<'tcx> for ExternWithoutAbi {
58+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
59+
if let ItemKind::ForeignMod { abi: Abi::C { .. }, .. } = item.kind
60+
&& !in_external_macro(cx.sess(), item.span)
61+
&& !is_from_proc_macro(cx, item)
62+
&& let snippet = snippet(cx.sess(), item.span, "").as_ref()
63+
&& is_extern_followed_by(OPEN_BRACE, snippet)
64+
{
65+
emit_lint(cx, item.span, snippet);
66+
}
67+
}
68+
69+
fn check_fn(
70+
&mut self,
71+
cx: &LateContext<'tcx>,
72+
kind: FnKind<'tcx>,
73+
_: &'tcx FnDecl<'tcx>,
74+
body: &'tcx Body<'tcx>,
75+
span: Span,
76+
_: LocalDefId,
77+
) {
78+
if let FnKind::ItemFn(_, _, header) = kind
79+
&& let FnHeader { abi: Abi::C { .. }, .. } = header
80+
&& !in_external_macro(cx.sess(), span)
81+
&& let hir_id = cx.tcx.hir().body_owner(body.id())
82+
&& !is_from_proc_macro(cx, &(&kind, body, hir_id, span))
83+
&& let snippet = snippet(cx.sess(), span, "").as_ref()
84+
&& is_extern_followed_by(FN, snippet)
85+
{
86+
emit_lint(cx, span, snippet);
87+
}
88+
}
89+
}
90+
91+
fn is_extern_followed_by(item: &str, snippet: &str) -> bool {
92+
let mut tokens = snippet.split_whitespace();
93+
94+
if let (_, Some(i)) = (tokens.next(), tokens.next())
95+
&& i.starts_with(item)
96+
{
97+
return true;
98+
}
99+
false
100+
}
101+
102+
fn emit_lint(cx: &LateContext<'_>, span: Span, snippet: &str) {
103+
let sugg = snippet.replacen(EXTERN, format!("{EXTERN} {ABI}").as_str(), 1);
104+
span_lint_and_sugg(
105+
cx,
106+
EXTERN_WITHOUT_ABI,
107+
span,
108+
LINT_MSG,
109+
LINT_HELP_MSG,
110+
sugg,
111+
Applicability::MachineApplicable,
112+
);
113+
}

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ mod excessive_nesting;
135135
mod exhaustive_items;
136136
mod exit;
137137
mod explicit_write;
138+
mod extern_without_abi;
138139
mod extra_unused_type_parameters;
139140
mod fallible_impl_from;
140141
mod field_scoped_visibility_modifiers;
@@ -944,5 +945,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
944945
store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo));
945946
store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
946947
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
948+
store.register_late_pass(|_| Box::new(extern_without_abi::ExternWithoutAbi));
947949
// add lints here, do not remove this comment, it's used in `new_lint`
948950
}

tests/ui/boxed_local.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(
22
clippy::borrowed_box,
3+
clippy::extern_without_abi,
34
clippy::needless_pass_by_value,
45
clippy::unused_unit,
56
clippy::redundant_clone,

tests/ui/boxed_local.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: local variable doesn't need to be boxed here
2-
--> tests/ui/boxed_local.rs:39:13
2+
--> tests/ui/boxed_local.rs:40:13
33
|
44
LL | fn warn_arg(x: Box<A>) {
55
| ^
@@ -8,19 +8,19 @@ LL | fn warn_arg(x: Box<A>) {
88
= help: to override `-D warnings` add `#[allow(clippy::boxed_local)]`
99

1010
error: local variable doesn't need to be boxed here
11-
--> tests/ui/boxed_local.rs:122:12
11+
--> tests/ui/boxed_local.rs:123:12
1212
|
1313
LL | pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
1414
| ^^^^^^^^^^^
1515

1616
error: local variable doesn't need to be boxed here
17-
--> tests/ui/boxed_local.rs:187:44
17+
--> tests/ui/boxed_local.rs:188:44
1818
|
1919
LL | fn default_impl_x(self: Box<Self>, x: Box<u32>) -> u32 {
2020
| ^
2121

2222
error: local variable doesn't need to be boxed here
23-
--> tests/ui/boxed_local.rs:195:16
23+
--> tests/ui/boxed_local.rs:196:16
2424
|
2525
LL | fn foo(x: Box<u32>) {}
2626
| ^

tests/ui/doc/doc-fixable.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
//! This file tests for the `DOC_MARKDOWN` lint.
33

4-
#![allow(dead_code, incomplete_features)]
4+
#![allow(dead_code, incomplete_features, clippy::extern_without_abi)]
55
#![warn(clippy::doc_markdown)]
66
#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
77
#![rustfmt::skip]

tests/ui/doc/doc-fixable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
//! This file tests for the `DOC_MARKDOWN` lint.
33
4-
#![allow(dead_code, incomplete_features)]
4+
#![allow(dead_code, incomplete_features, clippy::extern_without_abi)]
55
#![warn(clippy::doc_markdown)]
66
#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
77
#![rustfmt::skip]

tests/ui/extern_without_abi.fixed

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//@aux-build:proc_macros.rs
2+
3+
#![warn(clippy::extern_without_abi)]
4+
5+
extern crate proc_macros;
6+
use proc_macros::{external, with_span};
7+
8+
#[rustfmt::skip]
9+
extern "C" fn foo() {}
10+
//~^ ERROR: `extern` missing explicit ABI
11+
12+
#[rustfmt::skip]
13+
extern "C"
14+
fn foo_two() {}
15+
//~^^ ERROR: `extern` missing explicit ABI
16+
17+
extern "C" fn bar() {}
18+
19+
#[rustfmt::skip]
20+
extern
21+
"C"
22+
fn bar_two() {}
23+
24+
extern "system" fn baz() {}
25+
26+
#[rustfmt::skip]
27+
extern "C" {
28+
//~^ ERROR: `extern` missing explicit ABI
29+
fn qux();
30+
}
31+
32+
#[rustfmt::skip]
33+
extern "C"
34+
{
35+
//~^^ ERROR: `extern` missing explicit ABI
36+
fn qux_two();
37+
}
38+
39+
#[rustfmt::skip]
40+
extern "C" {fn qux_three();}
41+
//~^ ERROR: `extern` missing explicit ABI
42+
43+
extern "C" {
44+
fn grault();
45+
}
46+
47+
extern "system" {
48+
fn grault_two();
49+
}
50+
51+
external! {
52+
extern fn waldo() {}
53+
}
54+
55+
with_span! {
56+
span
57+
extern fn waldo_two() {}
58+
}
59+
60+
fn main() {}

tests/ui/extern_without_abi.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//@aux-build:proc_macros.rs
2+
3+
#![warn(clippy::extern_without_abi)]
4+
5+
extern crate proc_macros;
6+
use proc_macros::{external, with_span};
7+
8+
#[rustfmt::skip]
9+
extern fn foo() {}
10+
//~^ ERROR: `extern` missing explicit ABI
11+
12+
#[rustfmt::skip]
13+
extern
14+
fn foo_two() {}
15+
//~^^ ERROR: `extern` missing explicit ABI
16+
17+
extern "C" fn bar() {}
18+
19+
#[rustfmt::skip]
20+
extern
21+
"C"
22+
fn bar_two() {}
23+
24+
extern "system" fn baz() {}
25+
26+
#[rustfmt::skip]
27+
extern {
28+
//~^ ERROR: `extern` missing explicit ABI
29+
fn qux();
30+
}
31+
32+
#[rustfmt::skip]
33+
extern
34+
{
35+
//~^^ ERROR: `extern` missing explicit ABI
36+
fn qux_two();
37+
}
38+
39+
#[rustfmt::skip]
40+
extern {fn qux_three();}
41+
//~^ ERROR: `extern` missing explicit ABI
42+
43+
extern "C" {
44+
fn grault();
45+
}
46+
47+
extern "system" {
48+
fn grault_two();
49+
}
50+
51+
external! {
52+
extern fn waldo() {}
53+
}
54+
55+
with_span! {
56+
span
57+
extern fn waldo_two() {}
58+
}
59+
60+
fn main() {}

tests/ui/extern_without_abi.stderr

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
error: `extern` missing explicit ABI
2+
--> tests/ui/extern_without_abi.rs:9:1
3+
|
4+
LL | extern fn foo() {}
5+
| ^^^^^^^^^^^^^^^^^^ help: consider using: `extern "C" fn foo() {}`
6+
|
7+
= note: `-D clippy::extern-without-abi` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::extern_without_abi)]`
9+
10+
error: `extern` missing explicit ABI
11+
--> tests/ui/extern_without_abi.rs:13:1
12+
|
13+
LL | / extern
14+
LL | | fn foo_two() {}
15+
| |_______________^
16+
|
17+
help: consider using
18+
|
19+
LL ~ extern "C"
20+
LL + fn foo_two() {}
21+
|
22+
23+
error: `extern` missing explicit ABI
24+
--> tests/ui/extern_without_abi.rs:27:1
25+
|
26+
LL | / extern {
27+
LL | |
28+
LL | | fn qux();
29+
LL | | }
30+
| |_^
31+
|
32+
help: consider using
33+
|
34+
LL + extern "C" {
35+
LL +
36+
LL + fn qux();
37+
LL + }
38+
|
39+
40+
error: `extern` missing explicit ABI
41+
--> tests/ui/extern_without_abi.rs:33:1
42+
|
43+
LL | / extern
44+
LL | | {
45+
LL | |
46+
LL | | fn qux_two();
47+
LL | | }
48+
| |_^
49+
|
50+
help: consider using
51+
|
52+
LL ~ extern "C"
53+
LL + {
54+
LL +
55+
LL + fn qux_two();
56+
LL + }
57+
|
58+
59+
error: `extern` missing explicit ABI
60+
--> tests/ui/extern_without_abi.rs:40:1
61+
|
62+
LL | extern {fn qux_three();}
63+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `extern "C" {fn qux_three();}`
64+
65+
error: aborting due to 5 previous errors
66+

0 commit comments

Comments
 (0)