1
- use crate :: utils:: { numeric_literal:: NumericLiteral , snippet_opt, span_lint_and_help, span_lint_and_sugg} ;
1
+ use crate :: utils:: {
2
+ last_path_segment, numeric_literal:: NumericLiteral , qpath_res, snippet_opt, span_lint_and_help, span_lint_and_sugg,
3
+ } ;
2
4
use if_chain:: if_chain;
3
5
use rustc_ast:: { LitIntType , LitKind } ;
4
6
use rustc_errors:: Applicability ;
5
- use rustc_hir:: { BinOpKind , Expr , ExprKind , ItemKind , Lit , Node } ;
7
+ use rustc_hir:: {
8
+ def:: { DefKind , Res } ,
9
+ BinOpKind , BindingAnnotation , Expr , ExprKind , ItemKind , Lit , Node , PatKind , QPath ,
10
+ } ;
6
11
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
7
12
use rustc_middle:: lint:: in_external_macro;
8
13
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -42,7 +47,7 @@ impl LateLintPass<'_> for XorUsedAsPow {
42
47
// [ ] catch statements with literal or constant variables in rhs
43
48
// [x] check for overflows on suggested changes
44
49
// [x] ignore binary, octal, and hex literals in either side
45
- // [ ] avoid linting on enum discriminants
50
+ // [x ] avoid linting on enum discriminants
46
51
47
52
let parent_id = cx. tcx . hir ( ) . get_parent_item ( expr. hir_id ) ;
48
53
if let Some ( Node :: Item ( parent_item) ) = cx. tcx . hir ( ) . find ( parent_id) {
@@ -60,12 +65,42 @@ impl LateLintPass<'_> for XorUsedAsPow {
60
65
then {
61
66
match & right. kind {
62
67
ExprKind :: Lit ( rhs) => {
63
- if let Some ( ( rhs_val, rhs_type ) ) = unwrap_dec_int_literal( cx, rhs) {
64
- report_and_suggest ( cx, lhs_val, rhs_val, expr. span) ;
68
+ if let Some ( ( rhs_val, _ ) ) = unwrap_dec_int_literal( cx, rhs) {
69
+ report_with_lit ( cx, lhs_val, rhs_val, expr. span) ;
65
70
}
66
71
}
67
- ExprKind :: Path ( path) => {
68
- // TODO: fill this out
72
+ ExprKind :: Path ( qpath) => {
73
+ match qpath_res( cx, qpath, right. hir_id) {
74
+ Res :: Local ( hir_id) => {
75
+ if_chain! {
76
+ let node = cx. tcx. hir( ) . get( hir_id) ;
77
+ if let Node :: Binding ( pat) = node;
78
+ if let PatKind :: Binding ( bind_ann, ..) = pat. kind;
79
+ if !matches!( bind_ann, BindingAnnotation :: RefMut |
80
+ BindingAnnotation :: Mutable ) ;
81
+ let parent_node = cx. tcx. hir( ) . get_parent_node( hir_id) ;
82
+ if let Some ( Node :: Local ( parent_let_expr) ) = cx. tcx. hir( ) . find( parent_node) ;
83
+ if let Some ( init) = parent_let_expr. init;
84
+ then {
85
+ match init. kind {
86
+ // immutable bindings that are initialized with literal
87
+ ExprKind :: Lit ( ..) => report_with_ident( cx, lhs_val, qpath, expr. span) ,
88
+ // immutable bindings that are initialized with constant
89
+ ExprKind :: Path ( ref path) => {
90
+ let res = qpath_res( cx, path, init. hir_id) ;
91
+ if let Res :: Def ( DefKind :: Const , ..) = res {
92
+ report_with_ident( cx, lhs_val, qpath, expr. span) ;
93
+ }
94
+ }
95
+ _ => { } ,
96
+ }
97
+ }
98
+ }
99
+ } ,
100
+ // constant
101
+ Res :: Def ( DefKind :: Const , ..) => report_with_ident( cx, lhs_val, qpath, expr. span) ,
102
+ _ => { } ,
103
+ }
69
104
}
70
105
_ => { }
71
106
}
@@ -89,49 +124,57 @@ fn unwrap_dec_int_literal(cx: &LateContext<'_>, lit: &Lit) -> Option<(u128, LitI
89
124
}
90
125
}
91
126
92
- fn report_and_suggest ( cx : & LateContext < ' _ > , lhs : u128 , rhs : u128 , span : Span ) {
127
+ fn report_with_ident ( cx : & LateContext < ' _ > , lhs : u128 , rhs : & QPath < ' _ > , span : Span ) {
128
+ match lhs {
129
+ 2 => {
130
+ let ident = last_path_segment ( rhs) . ident . name . to_ident_string ( ) ;
131
+ report_pow_of_two ( cx, "1" , & ident, span) ;
132
+ } ,
133
+ 10 => report_pow_of_ten ( cx, span) ,
134
+ _ => { } ,
135
+ }
136
+ }
137
+
138
+ fn report_with_lit ( cx : & LateContext < ' _ > , lhs : u128 , rhs : u128 , span : Span ) {
93
139
if rhs > 127 {
94
140
return ;
95
141
}
96
-
97
- if lhs == 2 {
98
- if rhs > 0 {
99
- let int_type = if rhs <= 31 {
100
- "u32"
142
+ match lhs {
143
+ 2 => {
144
+ let lhs_str = if rhs <= 31 {
145
+ "1_u32"
101
146
} else if rhs <= 63 {
102
- "u64 "
147
+ "1_u64 "
103
148
} else {
104
- "u127 "
149
+ "1_u127 "
105
150
} ;
106
151
107
- span_lint_and_sugg (
108
- cx,
109
- XOR_USED_AS_POW ,
110
- span,
111
- "it appears you are trying to get a power of two, but `^` is not an exponentiation operator" ,
112
- "use a bitshift instead" ,
113
- format ! ( "1_{} << {}" , int_type, rhs) ,
114
- Applicability :: MaybeIncorrect ,
115
- )
116
- } else if rhs == 0 {
117
- span_lint_and_sugg (
118
- cx,
119
- XOR_USED_AS_POW ,
120
- span,
121
- "it appears you are trying to get a power of two, but `^` is not an exponentiation operator" ,
122
- "try" ,
123
- "1" . to_owned ( ) ,
124
- Applicability :: MaybeIncorrect ,
125
- )
126
- }
127
- } else if lhs == 10 {
128
- span_lint_and_help (
129
- cx,
130
- XOR_USED_AS_POW ,
131
- span,
132
- "`^` is not an exponentiation operator but appears to have been used as one" ,
133
- None ,
134
- "did you mean to use .pow()?" ,
135
- )
152
+ report_pow_of_two ( cx, lhs_str, & rhs. to_string ( ) , span) ;
153
+ } ,
154
+ 10 => report_pow_of_ten ( cx, span) ,
155
+ _ => { } ,
136
156
}
137
157
}
158
+
159
+ fn report_pow_of_two ( cx : & LateContext < ' _ > , lhs : & str , rhs : & str , span : Span ) {
160
+ span_lint_and_sugg (
161
+ cx,
162
+ XOR_USED_AS_POW ,
163
+ span,
164
+ "it appears you are trying to get a power of two, but `^` is not an exponentiation operator" ,
165
+ "use a bitshift instead" ,
166
+ format ! ( "{} << {}" , lhs, rhs) ,
167
+ Applicability :: MaybeIncorrect ,
168
+ )
169
+ }
170
+
171
+ fn report_pow_of_ten ( cx : & LateContext < ' _ > , span : Span ) {
172
+ span_lint_and_help (
173
+ cx,
174
+ XOR_USED_AS_POW ,
175
+ span,
176
+ "`^` is not an exponentiation operator but appears to have been used as one" ,
177
+ None ,
178
+ "did you mean to use .pow()?" ,
179
+ )
180
+ }
0 commit comments