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;
5
10
use rustc_session:: declare_lint_pass;
6
11
use rustc_span:: Span ;
12
+ use rustc_target:: spec:: abi:: Abi ;
7
13
8
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\" " ;
9
21
10
22
declare_clippy_lint ! {
11
23
/// ### What it does
12
24
/// Checks for usage of `extern` without an explicit ABI.
13
25
///
14
26
/// ### 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
+ ///
17
33
/// ### Example
18
34
/// ```no_run
19
35
/// extern fn foo() {}
@@ -38,24 +54,60 @@ declare_clippy_lint! {
38
54
39
55
declare_lint_pass ! ( ExternWithoutAbi => [ EXTERN_WITHOUT_ABI ] ) ;
40
56
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)
45
64
{
46
- span_lint ( cx, EXTERN_WITHOUT_ABI , item. span , LINT_MSG ) ;
65
+ emit_lint ( cx, item. span , snippet ) ;
47
66
}
48
67
}
49
68
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)
57
85
{
58
- span_lint ( cx, EXTERN_WITHOUT_ABI , * span, LINT_MSG ) ;
86
+ emit_lint ( cx, span, snippet ) ;
59
87
}
60
88
}
61
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
+ }
0 commit comments