Skip to content

Add [extern_without_abi] lint #13404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

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

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

Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
Expand Down
2 changes: 1 addition & 1 deletion book/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
A collection of lints to catch common mistakes and improve your
[Rust](https://github.com/rust-lang/rust) code.

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

Lints are divided into categories, each with a default [lint
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how
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 = r#""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)
&& let snippet = snippet(cx.sess(), item.span, "").as_ref()
&& is_extern_followed_by(OPEN_BRACE, snippet)
&& !is_from_proc_macro(cx, item)
{
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 snippet = snippet(cx.sess(), span, "").as_ref()
&& is_extern_followed_by(FN, snippet)
&& let hir_id = cx.tcx.hir().body_owner(body.id())
&& !is_from_proc_macro(cx, &(&kind, body, hir_id, span))
{
emit_lint(cx, span, snippet);
}
}
}

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

if let (Some(EXTERN), 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 @@ -951,5 +952,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
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(unnecessary_literal_bound::UnnecessaryLiteralBound));
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() {}
Loading