1
- use clippy_utils:: {
2
- diagnostics:: { span_lint_and_help, span_lint_and_then} ,
3
- is_lint_allowed, match_def_path, path_def_id,
4
- } ;
1
+ use crate :: Lint ;
2
+ use clippy_utils:: { diagnostics:: span_lint_and_then, is_lint_allowed} ;
5
3
use rustc_hir:: { Expr , ExprKind } ;
6
- use rustc_lint:: { LateContext , LateLintPass } ;
4
+ use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
5
+ use rustc_middle:: lint:: in_external_macro;
7
6
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
7
+ use rustc_span:: Symbol ;
8
+ use std:: borrow:: Cow ;
8
9
9
10
declare_clippy_lint ! {
10
11
/// ### What it does
12
+ /// Checks for the usage of the `to_ne_bytes` method.
11
13
///
12
14
/// ### Why is this bad?
15
+ /// It's not, but some may prefer to specify the target endianness explicitly.
13
16
///
14
17
/// ### Example
15
- /// ```rust
16
- /// // example code where clippy issues a warning
17
- /// ```
18
- /// Use instead:
19
- /// ```rust
20
- /// // example code which does not raise clippy warning
18
+ /// ```rust,ignore
19
+ /// let _x = 2i32.to_ne_bytes();
20
+ /// let _y = 2i64.to_ne_bytes();
21
21
/// ```
22
22
#[ clippy:: version = "1.71.0" ]
23
23
pub HOST_ENDIAN_BYTES ,
@@ -27,15 +27,16 @@ declare_clippy_lint! {
27
27
28
28
declare_clippy_lint ! {
29
29
/// ### What it does
30
- /// Checks for the usage of the `to_ne_bytes ` method.
30
+ /// Checks for the usage of the `to_le_bytes ` method.
31
31
///
32
32
/// ### Why is this bad?
33
- /// It's not, but some may prefer to specify the target endianness explicitly.
33
+ /// It's not, but some may wish to lint usages of this method, either to suggest using the host
34
+ /// endianness or big endian.
34
35
///
35
36
/// ### Example
36
37
/// ```rust,ignore
37
- /// let _x = 2i32.to_ne_bytes ();
38
- /// let _y = 2i64.to_ne_bytes ();
38
+ /// let _x = 2i32.to_le_bytes ();
39
+ /// let _y = 2i64.to_le_bytes ();
39
40
/// ```
40
41
#[ clippy:: version = "1.71.0" ]
41
42
pub LITTLE_ENDIAN_BYTES ,
@@ -45,13 +46,16 @@ declare_clippy_lint! {
45
46
46
47
declare_clippy_lint ! {
47
48
/// ### What it does
48
- /// Checks for the usage of the `to_le_bytes ` method.
49
+ /// Checks for the usage of the `to_be_bytes ` method.
49
50
///
50
51
/// ### Why is this bad?
52
+ /// It's not, but some may wish to lint usages of this method, either to suggest using the host
53
+ /// endianness or little endian.
51
54
///
52
55
/// ### Example
53
56
/// ```rust,ignore
54
- /// // example code where clippy issues a warning
57
+ /// let _x = 2i32.to_be_bytes();
58
+ /// let _y = 2i64.to_be_bytes();
55
59
/// ```
56
60
#[ clippy:: version = "1.71.0" ]
57
61
pub BIG_ENDIAN_BYTES ,
@@ -61,78 +65,133 @@ declare_clippy_lint! {
61
65
62
66
declare_lint_pass ! ( EndianBytes => [ HOST_ENDIAN_BYTES , LITTLE_ENDIAN_BYTES , BIG_ENDIAN_BYTES ] ) ;
63
67
68
+ #[ derive( Clone , Debug ) ]
69
+ enum LintKind {
70
+ Host ,
71
+ Little ,
72
+ Big ,
73
+ }
74
+
75
+ impl LintKind {
76
+ fn allowed ( & self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
77
+ is_lint_allowed ( cx, self . as_lint ( ) , expr. hir_id )
78
+ }
79
+
80
+ fn as_lint ( & self ) -> & ' static Lint {
81
+ match self {
82
+ LintKind :: Host => HOST_ENDIAN_BYTES ,
83
+ LintKind :: Little => LITTLE_ENDIAN_BYTES ,
84
+ LintKind :: Big => BIG_ENDIAN_BYTES ,
85
+ }
86
+ }
87
+
88
+ fn to_name ( & self , prefix : & str ) -> String {
89
+ match self {
90
+ LintKind :: Host => format ! ( "{prefix}_ne_bytes" ) ,
91
+ LintKind :: Little => format ! ( "{prefix}_le_bytes" ) ,
92
+ LintKind :: Big => format ! ( "{prefix}_be_bytes" ) ,
93
+ }
94
+ }
95
+ }
96
+
64
97
impl LateLintPass < ' _ > for EndianBytes {
65
98
fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
99
+ if in_external_macro ( cx. sess ( ) , expr. span ) {
100
+ return ;
101
+ }
102
+
66
103
if_chain ! {
67
104
if let ExprKind :: MethodCall ( method_name, receiver, args, ..) = expr. kind;
68
105
if let ExprKind :: Lit ( ..) = receiver. kind;
69
106
if args. is_empty( ) ;
107
+ if try_lint_endian_bytes( cx, expr, "to" , method_name. ident. name) ;
70
108
then {
71
- if method_name. ident. name == sym!( to_ne_bytes) {
72
- span_lint_and_help(
73
- cx,
74
- HOST_ENDIAN_BYTES ,
75
- expr. span,
76
- "use of the method `to_ne_bytes`" ,
77
- None ,
78
- "consider specifying the desired endianness" ,
79
- ) ;
80
- } else if method_name. ident. name == sym!( to_le_bytes) {
81
- span_lint_and_then( cx, LITTLE_ENDIAN_BYTES , expr. span, "use of the method `to_le_bytes`" , |diag| {
82
- if is_lint_allowed( cx, BIG_ENDIAN_BYTES , expr. hir_id) {
83
- diag. help( "use `to_be_bytes` instead" ) ;
84
- }
85
- } ) ;
86
- } else if method_name. ident. name == sym!( to_be_bytes) {
87
- span_lint_and_then( cx, BIG_ENDIAN_BYTES , expr. span, "use of the method `to_be_bytes`" , |diag| {
88
- if is_lint_allowed( cx, LITTLE_ENDIAN_BYTES , expr. hir_id) {
89
- diag. help( "use `to_le_bytes` instead" ) ;
90
- }
91
- } ) ;
92
- }
93
-
94
- // don't waste time also checking from_**_bytes
95
109
return ;
96
110
}
97
111
}
98
112
99
- span_lint_and_help (
100
- cx,
101
- HOST_ENDIAN_BYTES ,
102
- expr. span ,
103
- "use of the method `from_ne_bytes`" ,
104
- None ,
105
- & format ! ( "consider specifying the desired endianness: {expr:?}" ) ,
106
- ) ;
107
-
108
113
if_chain ! {
109
- if let ExprKind :: Call ( function, args) = expr. kind;
110
- if let Some ( function_def_id) = path_def_id( cx, function) ;
111
- if args. len( ) == 1 ;
114
+ if let ExprKind :: Call ( function, ..) = expr. kind;
115
+ if let ExprKind :: Path ( qpath) = function. kind;
116
+ if let Some ( def_id) = cx. qpath_res( & qpath, function. hir_id) . opt_def_id( ) ;
117
+ if let Some ( function_name) = cx. get_def_path( def_id) . last( ) ;
118
+ if cx. typeck_results( ) . expr_ty( expr) . is_primitive_ty( ) ;
112
119
then {
113
- if match_def_path( cx, function_def_id, & [ "from_ne_bytes" ] ) {
114
- span_lint_and_help(
115
- cx,
116
- HOST_ENDIAN_BYTES ,
117
- expr. span,
118
- "use of the method `from_ne_bytes`" ,
119
- None ,
120
- "consider specifying the desired endianness" ,
121
- ) ;
122
- } else if match_def_path( cx, function_def_id, & [ "from_le_bytes" ] ) {
123
- span_lint_and_then( cx, LITTLE_ENDIAN_BYTES , expr. span, "use of the method `from_le_bytes`" , |diag| {
124
- if is_lint_allowed( cx, BIG_ENDIAN_BYTES , expr. hir_id) {
125
- diag. help( "use `from_be_bytes` instead" ) ;
126
- }
127
- } ) ;
128
- } else if match_def_path( cx, function_def_id, & [ "from_be_bytes" ] ) {
129
- span_lint_and_then( cx, BIG_ENDIAN_BYTES , expr. span, "use of the method `from_be_bytes`" , |diag| {
130
- if is_lint_allowed( cx, LITTLE_ENDIAN_BYTES , expr. hir_id) {
131
- diag. help( "use `from_le_bytes` instead" ) ;
132
- }
133
- } ) ;
134
- }
120
+ try_lint_endian_bytes( cx, expr, "from" , * function_name) ;
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ fn try_lint_endian_bytes ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , prefix : & str , name : Symbol ) -> bool {
127
+ let ne = format ! ( "{prefix}_ne_bytes" ) ;
128
+ let le = format ! ( "{prefix}_le_bytes" ) ;
129
+ let be = format ! ( "{prefix}_be_bytes" ) ;
130
+
131
+ let ( lint, other_lints) = match name. as_str ( ) {
132
+ name if name == ne => ( ( & LintKind :: Host ) , [ ( & LintKind :: Little ) , ( & LintKind :: Big ) ] ) ,
133
+ name if name == le => ( ( & LintKind :: Little ) , [ ( & LintKind :: Host ) , ( & LintKind :: Big ) ] ) ,
134
+ name if name == be => ( ( & LintKind :: Big ) , [ ( & LintKind :: Host ) , ( & LintKind :: Little ) ] ) ,
135
+ _ => return false ,
136
+ } ;
137
+
138
+ let mut help = None ;
139
+
140
+ ' build_help: {
141
+ // all lints disallowed, don't give help here
142
+ if [ & [ lint] , other_lints. as_slice ( ) ]
143
+ . concat ( )
144
+ . iter ( )
145
+ . all ( |lint| !lint. allowed ( cx, expr) )
146
+ {
147
+ break ' build_help;
148
+ }
149
+
150
+ // ne_bytes and all other lints allowed
151
+ if lint. to_name ( prefix) == ne && other_lints. iter ( ) . all ( |lint| lint. allowed ( cx, expr) ) {
152
+ help = Some ( Cow :: Borrowed ( "specify the desired endianness explicitly" ) ) ;
153
+ break ' build_help;
154
+ }
155
+
156
+ // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but
157
+ // le_bytes is not
158
+ if ( lint. to_name ( prefix) == le || lint. to_name ( prefix) == be) && LintKind :: Host . allowed ( cx, expr) {
159
+ help = Some ( Cow :: Borrowed ( "use the native endianness instead" ) ) ;
160
+ break ' build_help;
161
+ }
162
+
163
+ let allowed_lints = other_lints. iter ( ) . filter ( |lint| lint. allowed ( cx, expr) ) ;
164
+ let len = allowed_lints. clone ( ) . count ( ) ;
165
+
166
+ let mut help_str = "use " . to_owned ( ) ;
167
+
168
+ for ( i, lint) in allowed_lints. enumerate ( ) {
169
+ let only_one = len == 1 ;
170
+ if !only_one {
171
+ help_str. push_str ( "either of " ) ;
172
+ }
173
+
174
+ help_str. push_str ( & format ! ( "`{}` " , lint. to_name( prefix) ) ) ;
175
+
176
+ if i != len && !only_one {
177
+ help_str. push_str ( "or " ) ;
135
178
}
136
179
}
180
+
181
+ help = Some ( Cow :: Owned ( help_str + "instead" ) ) ;
137
182
}
183
+
184
+ span_lint_and_then (
185
+ cx,
186
+ lint. as_lint ( ) ,
187
+ expr. span ,
188
+ & format ! ( "usage of the method `{}`" , lint. to_name( prefix) ) ,
189
+ move |diag| {
190
+ if let Some ( help) = help {
191
+ diag. help ( help) ;
192
+ }
193
+ } ,
194
+ ) ;
195
+
196
+ true
138
197
}
0 commit comments