1- use clippy_utils:: diagnostics:: span_lint;
2- use rustc_ast:: visit:: FnKind ;
3- use rustc_ast:: { Extern , FnHeader , FnSig , ForeignMod , Item , ItemKind , NodeId } ;
4- use rustc_lint:: { EarlyContext , EarlyLintPass } ;
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;
510use rustc_session:: declare_lint_pass;
611use rustc_span:: Span ;
12+ use rustc_target:: spec:: abi:: Abi ;
713
814const 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\" " ;
921
1022declare_clippy_lint ! {
1123 /// ### What it does
1224 /// Checks for usage of `extern` without an explicit ABI.
1325 ///
1426 /// ### Why is this bad?
15- /// Explicitly declaring the ABI improves readability.
16- //
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+ ///
1733 /// ### Example
1834 /// ```no_run
1935 /// extern fn foo() {}
@@ -38,24 +54,60 @@ declare_clippy_lint! {
3854
3955declare_lint_pass ! ( ExternWithoutAbi => [ EXTERN_WITHOUT_ABI ] ) ;
4056
41- impl EarlyLintPass for ExternWithoutAbi {
42- fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & Item ) {
43- if let ItemKind :: ForeignMod ( ref foreign_mod) = item. kind
44- && let ForeignMod { abi : None , .. } = foreign_mod
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)
4564 {
46- span_lint ( cx, EXTERN_WITHOUT_ABI , item. span , LINT_MSG ) ;
65+ emit_lint ( cx, item. span , snippet ) ;
4766 }
4867 }
4968
50- fn check_fn ( & mut self , cx : & EarlyContext < ' _ > , kind : FnKind < ' _ > , _: Span , _: NodeId ) {
51- if let FnKind :: Fn ( _, _, sig, ..) = kind
52- && let FnSig { header, .. } = sig
53- && let FnHeader {
54- ext : Extern :: Implicit ( span) ,
55- ..
56- } = header
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)
5785 {
58- span_lint ( cx, EXTERN_WITHOUT_ABI , * span, LINT_MSG ) ;
86+ emit_lint ( cx, span, snippet ) ;
5987 }
6088 }
6189}
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+ }
0 commit comments