Skip to content

Commit

Permalink
Add [extern_without_abi] lint
Browse files Browse the repository at this point in the history
  • Loading branch information
CBSpeir committed Oct 13, 2024
1 parent 6a281e9 commit 7df214f
Show file tree
Hide file tree
Showing 14 changed files with 350 additions and 36 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5456,6 +5456,7 @@ Released 2018-09-13
[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
[`extern_without_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#extern_without_abi
[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
[`extra_unused_type_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters
[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO,
crate::exit::EXIT_INFO,
crate::explicit_write::EXPLICIT_WRITE_INFO,
crate::extern_without_abi::EXTERN_WITHOUT_ABI_INFO,
crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO,
crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO,
crate::field_scoped_visibility_modifiers::FIELD_SCOPED_VISIBILITY_MODIFIERS_INFO,
Expand Down
113 changes: 113 additions & 0 deletions clippy_lints/src/extern_without_abi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_from_proc_macro;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, FnHeader, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::declare_lint_pass;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;

const LINT_MSG: &str = "`extern` missing explicit ABI";
const LINT_HELP_MSG: &str = "consider using";

const EXTERN: &str = "extern";
const FN: &str = "fn";
const OPEN_BRACE: &str = "{";
const ABI: &str = "\"C\"";

declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `extern` without an explicit ABI.
///
/// ### Why is this bad?
/// Explicitly declaring the ABI is the recommended convention. See:
/// [Rust Style Guide - `extern` items](https://doc.rust-lang.org/nightly/style-guide/items.html#extern-items)
///
/// It's also enforced by `rustfmt` when the `force_explicit_abi` option is enabled. See:
/// [Configuring Rustfmt](https://rust-lang.github.io/rustfmt/?version=master&search=#force_explicit_abi)
///
/// ### Example
/// ```no_run
/// extern fn foo() {}
///
/// extern {
/// fn bar();
/// }
/// ```
/// Use instead:
/// ```no_run
/// extern "C" fn foo() {}
///
/// extern "C" {
/// fn bar();
/// }
/// ```
#[clippy::version = "1.83.0"]
pub EXTERN_WITHOUT_ABI,
style,
"`extern` missing explicit ABI"
}

declare_lint_pass!(ExternWithoutAbi => [EXTERN_WITHOUT_ABI]);

impl<'tcx> LateLintPass<'tcx> for ExternWithoutAbi {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::ForeignMod { abi: Abi::C { .. }, .. } = item.kind
&& !in_external_macro(cx.sess(), item.span)
&& !is_from_proc_macro(cx, item)
&& let snippet = snippet(cx.sess(), item.span, "").as_ref()
&& is_extern_followed_by(OPEN_BRACE, snippet)
{
emit_lint(cx, item.span, snippet);
}
}

fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
kind: FnKind<'tcx>,
_: &'tcx FnDecl<'tcx>,
body: &'tcx Body<'tcx>,
span: Span,
_: LocalDefId,
) {
if let FnKind::ItemFn(_, _, header) = kind
&& let FnHeader { abi: Abi::C { .. }, .. } = header
&& !in_external_macro(cx.sess(), span)
&& let hir_id = cx.tcx.hir().body_owner(body.id())
&& !is_from_proc_macro(cx, &(&kind, body, hir_id, span))
&& let snippet = snippet(cx.sess(), span, "").as_ref()
&& is_extern_followed_by(FN, snippet)
{
emit_lint(cx, span, snippet);
}
}
}

fn is_extern_followed_by(item: &str, snippet: &str) -> bool {
let mut tokens = snippet.split_whitespace();

if let (_, Some(i)) = (tokens.next(), tokens.next())
&& i.starts_with(item)
{
return true;
}
false
}

fn emit_lint(cx: &LateContext<'_>, span: Span, snippet: &str) {
let sugg = snippet.replacen(EXTERN, format!("{EXTERN} {ABI}").as_str(), 1);
span_lint_and_sugg(
cx,
EXTERN_WITHOUT_ABI,
span,
LINT_MSG,
LINT_HELP_MSG,
sugg,
Applicability::MachineApplicable,
);
}
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ mod excessive_nesting;
mod exhaustive_items;
mod exit;
mod explicit_write;
mod extern_without_abi;
mod extra_unused_type_parameters;
mod fallible_impl_from;
mod field_scoped_visibility_modifiers;
Expand Down Expand Up @@ -949,5 +950,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
store.register_late_pass(|_| Box::new(extern_without_abi::ExternWithoutAbi));
// add lints here, do not remove this comment, it's used in `new_lint`
}
1 change: 1 addition & 0 deletions tests/ui/boxed_local.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(
clippy::borrowed_box,
clippy::extern_without_abi,
clippy::needless_pass_by_value,
clippy::unused_unit,
clippy::redundant_clone,
Expand Down
8 changes: 4 additions & 4 deletions tests/ui/boxed_local.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: local variable doesn't need to be boxed here
--> tests/ui/boxed_local.rs:40:13
--> tests/ui/boxed_local.rs:41:13
|
LL | fn warn_arg(x: Box<A>) {
| ^
Expand All @@ -8,19 +8,19 @@ LL | fn warn_arg(x: Box<A>) {
= help: to override `-D warnings` add `#[allow(clippy::boxed_local)]`

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

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

error: local variable doesn't need to be boxed here
--> tests/ui/boxed_local.rs:196:16
--> tests/ui/boxed_local.rs:197:16
|
LL | fn foo(x: Box<u32>) {}
| ^
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/doc/doc-fixable.fixed
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

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

#![allow(dead_code, incomplete_features)]
#![allow(dead_code, incomplete_features, clippy::extern_without_abi)]
#![warn(clippy::doc_markdown)]
#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
#![rustfmt::skip]
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/doc/doc-fixable.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

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

#![allow(dead_code, incomplete_features)]
#![allow(dead_code, incomplete_features, clippy::extern_without_abi)]
#![warn(clippy::doc_markdown)]
#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
#![rustfmt::skip]
Expand Down
60 changes: 60 additions & 0 deletions tests/ui/extern_without_abi.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//@aux-build:proc_macros.rs

#![warn(clippy::extern_without_abi)]

extern crate proc_macros;
use proc_macros::{external, with_span};

#[rustfmt::skip]
extern "C" fn foo() {}
//~^ ERROR: `extern` missing explicit ABI

#[rustfmt::skip]
extern "C"
fn foo_two() {}
//~^^ ERROR: `extern` missing explicit ABI

extern "C" fn bar() {}

#[rustfmt::skip]
extern
"C"
fn bar_two() {}

extern "system" fn baz() {}

#[rustfmt::skip]
extern "C" {
//~^ ERROR: `extern` missing explicit ABI
fn qux();
}

#[rustfmt::skip]
extern "C"
{
//~^^ ERROR: `extern` missing explicit ABI
fn qux_two();
}

#[rustfmt::skip]
extern "C" {fn qux_three();}
//~^ ERROR: `extern` missing explicit ABI

extern "C" {
fn grault();
}

extern "system" {
fn grault_two();
}

external! {
extern fn waldo() {}
}

with_span! {
span
extern fn waldo_two() {}
}

fn main() {}
60 changes: 60 additions & 0 deletions tests/ui/extern_without_abi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//@aux-build:proc_macros.rs

#![warn(clippy::extern_without_abi)]

extern crate proc_macros;
use proc_macros::{external, with_span};

#[rustfmt::skip]
extern fn foo() {}
//~^ ERROR: `extern` missing explicit ABI

#[rustfmt::skip]
extern
fn foo_two() {}
//~^^ ERROR: `extern` missing explicit ABI

extern "C" fn bar() {}

#[rustfmt::skip]
extern
"C"
fn bar_two() {}

extern "system" fn baz() {}

#[rustfmt::skip]
extern {
//~^ ERROR: `extern` missing explicit ABI
fn qux();
}

#[rustfmt::skip]
extern
{
//~^^ ERROR: `extern` missing explicit ABI
fn qux_two();
}

#[rustfmt::skip]
extern {fn qux_three();}
//~^ ERROR: `extern` missing explicit ABI

extern "C" {
fn grault();
}

extern "system" {
fn grault_two();
}

external! {
extern fn waldo() {}
}

with_span! {
span
extern fn waldo_two() {}
}

fn main() {}
66 changes: 66 additions & 0 deletions tests/ui/extern_without_abi.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
error: `extern` missing explicit ABI
--> tests/ui/extern_without_abi.rs:9:1
|
LL | extern fn foo() {}
| ^^^^^^^^^^^^^^^^^^ help: consider using: `extern "C" fn foo() {}`
|
= note: `-D clippy::extern-without-abi` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::extern_without_abi)]`

error: `extern` missing explicit ABI
--> tests/ui/extern_without_abi.rs:13:1
|
LL | / extern
LL | | fn foo_two() {}
| |_______________^
|
help: consider using
|
LL ~ extern "C"
LL + fn foo_two() {}
|

error: `extern` missing explicit ABI
--> tests/ui/extern_without_abi.rs:27:1
|
LL | / extern {
LL | |
LL | | fn qux();
LL | | }
| |_^
|
help: consider using
|
LL + extern "C" {
LL +
LL + fn qux();
LL + }
|

error: `extern` missing explicit ABI
--> tests/ui/extern_without_abi.rs:33:1
|
LL | / extern
LL | | {
LL | |
LL | | fn qux_two();
LL | | }
| |_^
|
help: consider using
|
LL ~ extern "C"
LL + {
LL +
LL + fn qux_two();
LL + }
|

error: `extern` missing explicit ABI
--> tests/ui/extern_without_abi.rs:40:1
|
LL | extern {fn qux_three();}
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `extern "C" {fn qux_three();}`

error: aborting due to 5 previous errors

Loading

0 comments on commit 7df214f

Please sign in to comment.