Skip to content

Commit e61f6b6

Browse files
committed
Add first attempts at checks for paths
1 parent 18b2df5 commit e61f6b6

File tree

1 file changed

+87
-44
lines changed

1 file changed

+87
-44
lines changed

clippy_lints/src/xor_used_as_pow.rs

Lines changed: 87 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
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+
};
24
use if_chain::if_chain;
35
use rustc_ast::{LitIntType, LitKind};
46
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+
};
611
use rustc_lint::{LateContext, LateLintPass, LintContext};
712
use rustc_middle::lint::in_external_macro;
813
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -42,7 +47,7 @@ impl LateLintPass<'_> for XorUsedAsPow {
4247
// [ ] catch statements with literal or constant variables in rhs
4348
// [x] check for overflows on suggested changes
4449
// [x] ignore binary, octal, and hex literals in either side
45-
// [ ] avoid linting on enum discriminants
50+
// [x] avoid linting on enum discriminants
4651

4752
let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
4853
if let Some(Node::Item(parent_item)) = cx.tcx.hir().find(parent_id) {
@@ -60,12 +65,42 @@ impl LateLintPass<'_> for XorUsedAsPow {
6065
then {
6166
match &right.kind {
6267
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);
6570
}
6671
}
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+
}
69104
}
70105
_ => {}
71106
}
@@ -89,49 +124,57 @@ fn unwrap_dec_int_literal(cx: &LateContext<'_>, lit: &Lit) -> Option<(u128, LitI
89124
}
90125
}
91126

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) {
93139
if rhs > 127 {
94140
return;
95141
}
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"
101146
} else if rhs <= 63 {
102-
"u64"
147+
"1_u64"
103148
} else {
104-
"u127"
149+
"1_u127"
105150
};
106151

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+
_ => {},
136156
}
137157
}
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

Comments
 (0)