Skip to content

Commit 4259377

Browse files
committed
Auto merge of #3725 - mikerite:fix-2728, r=phansch
Fix `cast_sign_loss` false positive This checks if the value is a non-negative constant before linting about losing the sign. Because the `constant` function doesn't handle const functions, we check if the value is from a call to a `max_value` function directly. A utility method called `get_def_path` was added to make checking for the function paths easier. Fixes #2728
2 parents 7bea436 + f3ee53d commit 4259377

File tree

6 files changed

+87
-46
lines changed

6 files changed

+87
-46
lines changed

clippy_lints/src/consts.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![allow(clippy::float_cmp)]
22

3-
use crate::utils::{clip, sext, unsext};
3+
use crate::utils::{clip, get_def_path, sext, unsext};
4+
use if_chain::if_chain;
45
use rustc::hir::def::Def;
56
use rustc::hir::*;
67
use rustc::lint::LateContext;
@@ -234,6 +235,31 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
234235
UnDeref => Some(o),
235236
}),
236237
ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
238+
ExprKind::Call(ref callee, ref args) => {
239+
// We only handle a few const functions for now
240+
if_chain! {
241+
if args.is_empty();
242+
if let ExprKind::Path(qpath) = &callee.node;
243+
let def = self.tables.qpath_def(qpath, callee.hir_id);
244+
if let Some(def_id) = def.opt_def_id();
245+
let def_path = get_def_path(self.tcx, def_id);
246+
if let &["core", "num", impl_ty, "max_value"] = &def_path[..];
247+
then {
248+
let value = match impl_ty {
249+
"<impl i8>" => i8::max_value() as u128,
250+
"<impl i16>" => i16::max_value() as u128,
251+
"<impl i32>" => i32::max_value() as u128,
252+
"<impl i64>" => i64::max_value() as u128,
253+
"<impl i128>" => i128::max_value() as u128,
254+
_ => return None,
255+
};
256+
Some(Constant::Int(value))
257+
}
258+
else {
259+
None
260+
}
261+
}
262+
},
237263
// TODO: add other expressions
238264
_ => None,
239265
}

clippy_lints/src/types.rs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,31 @@ enum ArchSuffix {
10001000
None,
10011001
}
10021002

1003+
fn check_loss_of_sign(cx: &LateContext<'_, '_>, expr: &Expr, op: &Expr, cast_from: Ty<'_>, cast_to: Ty<'_>) {
1004+
if !cast_from.is_signed() || cast_to.is_signed() {
1005+
return;
1006+
}
1007+
1008+
// don't lint for positive constants
1009+
let const_val = constant(cx, &cx.tables, op);
1010+
if_chain! {
1011+
if let Some((const_val, _)) = const_val;
1012+
if let Constant::Int(n) = const_val;
1013+
if let ty::Int(ity) = cast_from.sty;
1014+
if sext(cx.tcx, n, ity) >= 0;
1015+
then {
1016+
return
1017+
}
1018+
}
1019+
1020+
span_lint(
1021+
cx,
1022+
CAST_SIGN_LOSS,
1023+
expr.span,
1024+
&format!("casting {} to {} may lose the sign of the value", cast_from, cast_to),
1025+
);
1026+
}
1027+
10031028
fn check_truncation_and_wrapping(cx: &LateContext<'_, '_>, expr: &Expr, cast_from: Ty<'_>, cast_to: Ty<'_>) {
10041029
let arch_64_suffix = " on targets with 64-bit wide pointers";
10051030
let arch_32_suffix = " on targets with 32-bit wide pointers";
@@ -1175,14 +1200,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CastPass {
11751200
}
11761201
},
11771202
(true, true) => {
1178-
if cast_from.is_signed() && !cast_to.is_signed() {
1179-
span_lint(
1180-
cx,
1181-
CAST_SIGN_LOSS,
1182-
expr.span,
1183-
&format!("casting {} to {} may lose the sign of the value", cast_from, cast_to),
1184-
);
1185-
}
1203+
check_loss_of_sign(cx, expr, ex, cast_from, cast_to);
11861204
check_truncation_and_wrapping(cx, expr, cast_from, cast_to);
11871205
check_lossless(cx, expr, ex, cast_from, cast_to);
11881206
},

clippy_lints/src/utils/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,21 @@ pub fn match_def_path(tcx: TyCtxt<'_, '_, '_>, def_id: DefId, path: &[&str]) ->
130130
apb.names.len() == path.len() && apb.names.into_iter().zip(path.iter()).all(|(a, &b)| *a == *b)
131131
}
132132

133+
/// Get the absolute path of `def_id` as a vector of `&str`.
134+
///
135+
/// # Examples
136+
/// ```rust,ignore
137+
/// let def_path = get_def_path(tcx, def_id);
138+
/// if let &["core", "option", "Option"] = &def_path[..] {
139+
/// // The given `def_id` is that of an `Option` type
140+
/// };
141+
/// ```
142+
pub fn get_def_path(tcx: TyCtxt<'_, '_, '_>, def_id: DefId) -> Vec<&'static str> {
143+
let mut apb = AbsolutePathBuffer { names: vec![] };
144+
tcx.push_item_path(&mut apb, def_id, false);
145+
apb.names.iter().map(|n| n.get()).collect()
146+
}
147+
133148
/// Check if type is struct, enum or union type with given def path.
134149
pub fn match_type(cx: &LateContext<'_, '_>, ty: Ty<'_>, path: &[&str]) -> bool {
135150
match ty.sty {

tests/ui/cast.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,15 @@ fn main() {
3434
(1u8 + 1u8) as u16;
3535
// Test clippy::cast_sign_loss
3636
1i32 as u32;
37+
-1i32 as u32;
3738
1isize as usize;
39+
-1isize as usize;
40+
0i8 as u8;
41+
i8::max_value() as u8;
42+
i16::max_value() as u16;
43+
i32::max_value() as u32;
44+
i64::max_value() as u64;
45+
i128::max_value() as u128;
3846
// Extra checks for *size
3947
// Test cast_unnecessary
4048
1i32 as i32;

tests/ui/cast.stderr

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,6 @@ error: casting i32 to i8 may truncate the value
7070
LL | 1i32 as i8;
7171
| ^^^^^^^^^^
7272

73-
error: casting i32 to u8 may lose the sign of the value
74-
--> $DIR/cast.rs:22:5
75-
|
76-
LL | 1i32 as u8;
77-
| ^^^^^^^^^^
78-
7973
error: casting i32 to u8 may truncate the value
8074
--> $DIR/cast.rs:22:5
8175
|
@@ -147,36 +141,36 @@ LL | (1u8 + 1u8) as u16;
147141
| ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)`
148142

149143
error: casting i32 to u32 may lose the sign of the value
150-
--> $DIR/cast.rs:36:5
144+
--> $DIR/cast.rs:37:5
151145
|
152-
LL | 1i32 as u32;
153-
| ^^^^^^^^^^^
146+
LL | -1i32 as u32;
147+
| ^^^^^^^^^^^^
154148

155149
error: casting isize to usize may lose the sign of the value
156-
--> $DIR/cast.rs:37:5
150+
--> $DIR/cast.rs:39:5
157151
|
158-
LL | 1isize as usize;
159-
| ^^^^^^^^^^^^^^^
152+
LL | -1isize as usize;
153+
| ^^^^^^^^^^^^^^^^
160154

161155
error: casting to the same type is unnecessary (`i32` -> `i32`)
162-
--> $DIR/cast.rs:40:5
156+
--> $DIR/cast.rs:48:5
163157
|
164158
LL | 1i32 as i32;
165159
| ^^^^^^^^^^^
166160
|
167161
= note: `-D clippy::unnecessary-cast` implied by `-D warnings`
168162

169163
error: casting to the same type is unnecessary (`f32` -> `f32`)
170-
--> $DIR/cast.rs:41:5
164+
--> $DIR/cast.rs:49:5
171165
|
172166
LL | 1f32 as f32;
173167
| ^^^^^^^^^^^
174168

175169
error: casting to the same type is unnecessary (`bool` -> `bool`)
176-
--> $DIR/cast.rs:42:5
170+
--> $DIR/cast.rs:50:5
177171
|
178172
LL | false as bool;
179173
| ^^^^^^^^^^^^^
180174

181-
error: aborting due to 28 previous errors
175+
error: aborting due to 27 previous errors
182176

tests/ui/cast_size.stderr

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,6 @@ error: casting isize to i32 may truncate the value on targets with 64-bit wide p
3838
LL | 1isize as i32;
3939
| ^^^^^^^^^^^^^
4040

41-
error: casting isize to u32 may lose the sign of the value
42-
--> $DIR/cast_size.rs:17:5
43-
|
44-
LL | 1isize as u32;
45-
| ^^^^^^^^^^^^^
46-
|
47-
= note: `-D clippy::cast-sign-loss` implied by `-D warnings`
48-
4941
error: casting isize to u32 may truncate the value on targets with 64-bit wide pointers
5042
--> $DIR/cast_size.rs:17:5
5143
|
@@ -78,12 +70,6 @@ error: casting i64 to isize may truncate the value on targets with 32-bit wide p
7870
LL | 1i64 as isize;
7971
| ^^^^^^^^^^^^^
8072

81-
error: casting i64 to usize may lose the sign of the value
82-
--> $DIR/cast_size.rs:22:5
83-
|
84-
LL | 1i64 as usize;
85-
| ^^^^^^^^^^^^^
86-
8773
error: casting i64 to usize may truncate the value on targets with 32-bit wide pointers
8874
--> $DIR/cast_size.rs:22:5
8975
|
@@ -114,11 +100,5 @@ error: casting u32 to isize may wrap around the value on targets with 32-bit wid
114100
LL | 1u32 as isize;
115101
| ^^^^^^^^^^^^^
116102

117-
error: casting i32 to usize may lose the sign of the value
118-
--> $DIR/cast_size.rs:28:5
119-
|
120-
LL | 1i32 as usize;
121-
| ^^^^^^^^^^^^^
122-
123-
error: aborting due to 19 previous errors
103+
error: aborting due to 16 previous errors
124104

0 commit comments

Comments
 (0)