Skip to content

Commit b7c4490

Browse files
committed
Add [extern_without_abi] lint
1 parent f328623 commit b7c4490

16 files changed

+352
-38
lines changed

CHANGELOG.md

+1
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

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
77

8-
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
8+
[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
99

1010
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
1111
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.

book/src/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
A collection of lints to catch common mistakes and improve your
77
[Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
Lints are divided into categories, each with a default [lint
1212
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how

clippy_lints/src/declared_lints.rs

+1
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,
+113
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 = r#""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+
&& let snippet = snippet(cx.sess(), item.span, "").as_ref()
62+
&& is_extern_followed_by(OPEN_BRACE, snippet)
63+
&& !is_from_proc_macro(cx, item)
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 snippet = snippet(cx.sess(), span, "").as_ref()
82+
&& is_extern_followed_by(FN, snippet)
83+
&& let hir_id = cx.tcx.hir().body_owner(body.id())
84+
&& !is_from_proc_macro(cx, &(&kind, body, hir_id, span))
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(EXTERN), 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

+2
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ mod excessive_nesting;
137137
mod exhaustive_items;
138138
mod exit;
139139
mod explicit_write;
140+
mod extern_without_abi;
140141
mod extra_unused_type_parameters;
141142
mod fallible_impl_from;
142143
mod field_scoped_visibility_modifiers;
@@ -951,5 +952,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
951952
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
952953
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
953954
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
955+
store.register_late_pass(|_| Box::new(extern_without_abi::ExternWithoutAbi));
954956
// add lints here, do not remove this comment, it's used in `new_lint`
955957
}

tests/ui/boxed_local.rs

+1
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

+4-4
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:40:13
2+
--> tests/ui/boxed_local.rs:41: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:123:12
11+
--> tests/ui/boxed_local.rs:124: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:188:44
17+
--> tests/ui/boxed_local.rs:189: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:196:16
23+
--> tests/ui/boxed_local.rs:197:16
2424
|
2525
LL | fn foo(x: Box<u32>) {}
2626
| ^

tests/ui/doc/doc-fixable.fixed

+1-1
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

+1-1
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

+60
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

+60
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() {}

0 commit comments

Comments
 (0)