Skip to content

Commit 03ca52e

Browse files
cgm616ebroto
andcommitted
Apply suggestions from code review
Minor docs and description changes Co-authored-by: Eduardo Broto <[email protected]>
1 parent eb6c24d commit 03ca52e

File tree

6 files changed

+262
-69
lines changed

6 files changed

+262
-69
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11391139
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
11401140
store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax);
11411141
store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax);
1142-
store.register_early_pass(|| box xor_used_as_pow::XorUsedAsPow);
1142+
store.register_late_pass(|| box xor_used_as_pow::XorUsedAsPow);
11431143

11441144

11451145
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![

clippy_lints/src/xor_used_as_pow.rs

Lines changed: 147 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,179 @@
1-
use crate::utils::{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;
3-
use rustc_ast::{BinOpKind, Expr, ExprKind, LitKind};
5+
use rustc_ast::{LitIntType, LitKind};
46
use rustc_errors::Applicability;
5-
use rustc_lint::{EarlyContext, EarlyLintPass};
7+
use rustc_hir::{
8+
def::{DefKind, Res},
9+
BinOpKind, BindingAnnotation, Expr, ExprKind, ItemKind, Lit, Node, PatKind, QPath,
10+
};
11+
use rustc_lint::{LateContext, LateLintPass, LintContext};
612
use rustc_middle::lint::in_external_macro;
713
use rustc_session::{declare_lint_pass, declare_tool_lint};
14+
use rustc_span::Span;
815

916
declare_clippy_lint! {
10-
/// **What it does:** Checks for use of `^` operator when exponentiation was intended.
17+
/// **What it does:** Checks for use of `^` operator when exponentiation was probably intended.
18+
/// A caret is commonly an ASCII-compatible/keyboard-accessible way to write down exponentiation in docs,
19+
/// readmes, and comments, and copying and pasting a formula can inadvertedly introduce this error.
20+
/// Moreover, `^` means exponentiation in other programming languages.
1121
///
12-
/// **Why is this bad?** This is most probably a typo.
22+
/// **Why is this bad?** This is most probably a mistake.
1323
///
1424
/// **Known problems:** None.
1525
///
1626
/// **Example:**
1727
///
18-
/// ```rust,ignore
28+
/// ```rust
1929
/// // Bad
20-
/// 2 ^ 16;
30+
/// let a = 2 ^ 16;
31+
/// let b = 10 ^ 4;
2132
///
2233
/// // Good
23-
/// 1 << 16;
24-
/// 2i32.pow(16);
34+
/// let a = 1 << 16;
35+
/// let b = 10i32.pow(4);
2536
/// ```
2637
pub XOR_USED_AS_POW,
2738
correctness,
28-
"use of `^` operator when exponentiation was intended"
39+
"use of `^` operator when exponentiation was probably intended"
2940
}
3041

3142
declare_lint_pass!(XorUsedAsPow => [XOR_USED_AS_POW]);
3243

33-
impl EarlyLintPass for XorUsedAsPow {
34-
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
44+
impl LateLintPass<'_> for XorUsedAsPow {
45+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
46+
let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
47+
if let Some(Node::Item(parent_item)) = cx.tcx.hir().find(parent_id) {
48+
if let ItemKind::Enum(_, _) = parent_item.kind {
49+
return;
50+
}
51+
}
52+
3553
if_chain! {
36-
if !in_external_macro(cx.sess, expr.span);
54+
if !in_external_macro(cx.sess(), expr.span);
3755
if let ExprKind::Binary(op, left, right) = &expr.kind;
3856
if BinOpKind::BitXor == op.node;
39-
if let ExprKind::Lit(lit) = &left.kind;
40-
if let LitKind::Int(lhs, _) = lit.kind;
41-
if let ExprKind::Lit(lit) = &right.kind;
42-
if let LitKind::Int(rhs, _) = lit.kind;
57+
if let ExprKind::Lit(lhs) = &left.kind;
58+
if let Some((lhs_val, lhs_type)) = unwrap_dec_int_literal(cx, lhs);
4359
then {
44-
if lhs == 2 {
45-
if rhs == 8 || rhs == 16 || rhs == 32 || rhs == 64 || rhs == 128 {
46-
span_lint_and_sugg(
47-
cx,
48-
XOR_USED_AS_POW,
49-
expr.span,
50-
"it appears you are trying to get the maximum value of an integer, but `^` is not an exponentiation operator",
51-
"try",
52-
format!("std::u{}::MAX", rhs),
53-
Applicability::MaybeIncorrect,
54-
)
55-
} else {
56-
span_lint_and_sugg(
57-
cx,
58-
XOR_USED_AS_POW,
59-
expr.span,
60-
"it appears you are trying to get a power of two, but `^` is not an exponentiation operator",
61-
"use a bitshift instead",
62-
format!("1 << {}", rhs),
63-
Applicability::MaybeIncorrect,
64-
)
60+
match &right.kind {
61+
ExprKind::Lit(rhs) => {
62+
if let Some((rhs_val, _)) = unwrap_dec_int_literal(cx, rhs) {
63+
report_with_lit(cx, lhs_val, rhs_val, expr.span);
64+
}
6565
}
66-
} else {
67-
span_lint_and_help(
68-
cx,
69-
XOR_USED_AS_POW,
70-
expr.span,
71-
"`^` is not an exponentiation operator but appears to have been used as one",
72-
None,
73-
"did you mean to use .pow()?"
74-
)
66+
ExprKind::Path(qpath) => {
67+
match qpath_res(cx, qpath, right.hir_id) {
68+
Res::Local(hir_id) => {
69+
if_chain! {
70+
let node = cx.tcx.hir().get(hir_id);
71+
if let Node::Binding(pat) = node;
72+
if let PatKind::Binding(bind_ann, ..) = pat.kind;
73+
if !matches!(bind_ann, BindingAnnotation::RefMut |
74+
BindingAnnotation::Mutable);
75+
let parent_node = cx.tcx.hir().get_parent_node(hir_id);
76+
if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node);
77+
if let Some(init) = parent_let_expr.init;
78+
then {
79+
match init.kind {
80+
// immutable bindings that are initialized with literal
81+
ExprKind::Lit(..) => report_with_ident(cx, lhs_val, qpath, expr.span),
82+
// immutable bindings that are initialized with constant
83+
ExprKind::Path(ref path) => {
84+
let res = qpath_res(cx, path, init.hir_id);
85+
if let Res::Def(DefKind::Const, ..) = res {
86+
report_with_ident(cx, lhs_val, qpath, expr.span);
87+
}
88+
}
89+
_ => {},
90+
}
91+
}
92+
}
93+
},
94+
// constant
95+
Res::Def(DefKind::Const, ..) => report_with_ident(cx, lhs_val, qpath, expr.span),
96+
_ => {},
97+
}
98+
}
99+
_ => {}
75100
}
76101
}
77102
}
78103
}
79104
}
105+
106+
fn unwrap_dec_int_literal(cx: &LateContext<'_>, lit: &Lit) -> Option<(u128, LitIntType)> {
107+
if_chain! {
108+
if let LitKind::Int(val, val_type) = lit.node;
109+
if let Some(snippet) = snippet_opt(cx, lit.span);
110+
if let Some(decoded) = NumericLiteral::from_lit_kind(&snippet, &lit.node);
111+
if decoded.is_decimal();
112+
then {
113+
return Some((val, val_type));
114+
}
115+
else {
116+
return None;
117+
}
118+
}
119+
}
120+
121+
fn report_with_ident(cx: &LateContext<'_>, lhs: u128, rhs: &QPath<'_>, span: Span) {
122+
match lhs {
123+
2 => {
124+
let ident = last_path_segment(rhs).ident.name.to_ident_string();
125+
report_pow_of_two(cx, format!("1 << {}", ident), span);
126+
},
127+
10 => report_pow_of_ten(cx, span),
128+
_ => {},
129+
}
130+
}
131+
132+
fn report_with_lit(cx: &LateContext<'_>, lhs: u128, rhs: u128, span: Span) {
133+
if rhs > 127 {
134+
return;
135+
}
136+
match lhs {
137+
2 => {
138+
if rhs == 0 {
139+
report_pow_of_two(cx, format!("1"), span);
140+
return;
141+
}
142+
143+
let lhs_str = if rhs <= 31 {
144+
"1_u32"
145+
} else if rhs <= 63 {
146+
"1_u64"
147+
} else {
148+
"1_u127"
149+
};
150+
151+
report_pow_of_two(cx, format!("{} << {}", lhs_str, rhs), span);
152+
},
153+
10 => report_pow_of_ten(cx, span),
154+
_ => {},
155+
}
156+
}
157+
158+
fn report_pow_of_two(cx: &LateContext<'_>, sugg: String, span: Span) {
159+
span_lint_and_sugg(
160+
cx,
161+
XOR_USED_AS_POW,
162+
span,
163+
"it appears you are trying to get a power of two, but `^` is not an exponentiation operator",
164+
"use a bitshift or constant instead",
165+
sugg,
166+
Applicability::MaybeIncorrect,
167+
)
168+
}
169+
170+
fn report_pow_of_ten(cx: &LateContext<'_>, span: Span) {
171+
span_lint_and_help(
172+
cx,
173+
XOR_USED_AS_POW,
174+
span,
175+
"`^` is not an exponentiation operator but appears to have been used as one",
176+
None,
177+
"did you mean to use .pow()?",
178+
)
179+
}

src/lintlist/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2814,7 +2814,7 @@ vec![
28142814
Lint {
28152815
name: "xor_used_as_pow",
28162816
group: "correctness",
2817-
desc: "use of `^` operator when exponentiation was intended",
2817+
desc: "use of `^` operator when exponentiation was probably intended",
28182818
deprecation: None,
28192819
module: "xor_used_as_pow",
28202820
},

tests/ui/xor_used_as_pow.fixed

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// run-rustfix
2+
#![warn(clippy::xor_used_as_pow)]
3+
4+
// Should not be linted
5+
#[allow(dead_code)]
6+
enum E {
7+
First = 1 ^ 8,
8+
Second = 2 ^ 8,
9+
Third = 3 ^ 8,
10+
Tenth = 10 ^ 8,
11+
}
12+
13+
fn main() {
14+
// These should succeed:
15+
let _ = 9 ^ 3; // lhs other than 2 or 10
16+
let _ = 0x02 ^ 6; // lhs not decimal
17+
let _ = 2 ^ 0x10; // rhs hexadecimal
18+
let _ = 10 ^ 0b0101; // rhs binary
19+
let _ = 2 ^ 0o1; // rhs octal
20+
let _ = 10 ^ -18; // negative rhs
21+
22+
// These should fail
23+
let _ = 1_u32 << 3;
24+
let _ = 10 ^ 4;
25+
let _ = 1_u64 << 32;
26+
let _ = 1;
27+
let _ = 10 ^ 0;
28+
{
29+
let x = 15;
30+
let _ = 1 << x;
31+
let _ = 10 ^ x;
32+
}
33+
}

tests/ui/xor_used_as_pow.rs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,33 @@
1+
// run-rustfix
12
#![warn(clippy::xor_used_as_pow)]
23

4+
// Should not be linted
5+
#[allow(dead_code)]
6+
enum E {
7+
First = 1 ^ 8,
8+
Second = 2 ^ 8,
9+
Third = 3 ^ 8,
10+
Tenth = 10 ^ 8,
11+
}
12+
313
fn main() {
4-
// These should succeed
5-
// With variables, it's not as clear whether the intention was exponentiation or not
6-
let x = 15;
7-
println!("{}", 2 ^ x);
8-
let y = 2;
9-
println!("{}", y ^ 16);
14+
// These should succeed:
15+
let _ = 9 ^ 3; // lhs other than 2 or 10
16+
let _ = 0x02 ^ 6; // lhs not decimal
17+
let _ = 2 ^ 0x10; // rhs hexadecimal
18+
let _ = 10 ^ 0b0101; // rhs binary
19+
let _ = 2 ^ 0o1; // rhs octal
20+
let _ = 10 ^ -18; // negative rhs
1021

1122
// These should fail
12-
println!("{}", 2 ^ 16);
13-
println!("{}", 2 ^ 7);
14-
println!("{}", 9 ^ 3);
23+
let _ = 2 ^ 3;
24+
let _ = 10 ^ 4;
25+
let _ = 2 ^ 32;
26+
let _ = 2 ^ 0;
27+
let _ = 10 ^ 0;
28+
{
29+
let x = 15;
30+
let _ = 2 ^ x;
31+
let _ = 10 ^ x;
32+
}
1533
}

tests/ui/xor_used_as_pow.stderr

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,66 @@
1-
error: it appears you are trying to get the maximum value of an integer, but `^` is not an exponentiation operator
2-
--> $DIR/xor_used_as_pow.rs:12:20
1+
error: it appears you are trying to get a power of two, but `^` is not an exponentiation operator
2+
--> $DIR/xor_used_as_pow.rs:23:13
33
|
4-
LL | println!("{}", 2 ^ 16);
5-
| ^^^^^^ help: try: `std::u16::MAX`
4+
LL | let _ = 2 ^ 3;
5+
| ^^^^^ help: use a bitshift or constant instead: `1_u32 << 3`
66
|
77
= note: `-D clippy::xor-used-as-pow` implied by `-D warnings`
88

9+
error: `^` is not an exponentiation operator but appears to have been used as one
10+
--> $DIR/xor_used_as_pow.rs:24:13
11+
|
12+
LL | let _ = 10 ^ 4;
13+
| ^^^^^^
14+
|
15+
= help: did you mean to use .pow()?
16+
17+
error: it appears you are trying to get a power of two, but `^` is not an exponentiation operator
18+
--> $DIR/xor_used_as_pow.rs:25:13
19+
|
20+
LL | let _ = 2 ^ 32;
21+
| ^^^^^^ help: use a bitshift or constant instead: `1_u64 << 32`
22+
23+
error: the operation is ineffective. Consider reducing it to `2`
24+
--> $DIR/xor_used_as_pow.rs:26:13
25+
|
26+
LL | let _ = 2 ^ 0;
27+
| ^^^^^
28+
|
29+
= note: `-D clippy::identity-op` implied by `-D warnings`
30+
31+
error: it appears you are trying to get a power of two, but `^` is not an exponentiation operator
32+
--> $DIR/xor_used_as_pow.rs:26:13
33+
|
34+
LL | let _ = 2 ^ 0;
35+
| ^^^^^ help: use a bitshift or constant instead: `1`
36+
37+
error: the operation is ineffective. Consider reducing it to `10`
38+
--> $DIR/xor_used_as_pow.rs:27:13
39+
|
40+
LL | let _ = 10 ^ 0;
41+
| ^^^^^^
42+
43+
error: `^` is not an exponentiation operator but appears to have been used as one
44+
--> $DIR/xor_used_as_pow.rs:27:13
45+
|
46+
LL | let _ = 10 ^ 0;
47+
| ^^^^^^
48+
|
49+
= help: did you mean to use .pow()?
50+
951
error: it appears you are trying to get a power of two, but `^` is not an exponentiation operator
10-
--> $DIR/xor_used_as_pow.rs:13:20
52+
--> $DIR/xor_used_as_pow.rs:30:17
1153
|
12-
LL | println!("{}", 2 ^ 7);
13-
| ^^^^^ help: use a bitshift instead: `1 << 7`
54+
LL | let _ = 2 ^ x;
55+
| ^^^^^ help: use a bitshift or constant instead: `1 << x`
1456

1557
error: `^` is not an exponentiation operator but appears to have been used as one
16-
--> $DIR/xor_used_as_pow.rs:14:20
58+
--> $DIR/xor_used_as_pow.rs:31:17
1759
|
18-
LL | println!("{}", 9 ^ 3);
19-
| ^^^^^
60+
LL | let _ = 10 ^ x;
61+
| ^^^^^^
2062
|
2163
= help: did you mean to use .pow()?
2264

23-
error: aborting due to 3 previous errors
65+
error: aborting due to 9 previous errors
2466

0 commit comments

Comments
 (0)