Skip to content

Commit 329923e

Browse files
committed
Auto merge of rust-lang#5257 - mlegner:cast_hex_fp, r=flip1995
Resolve false positives of unnecessary_cast for non-decimal integers This PR resolves false positives of `unnecessary_cast` for hexadecimal integers to floats and adds a corresponding test case. Fixes: rust-lang#5220 changelog: none
2 parents 8c7b3ad + 185fa0d commit 329923e

9 files changed

+274
-248
lines changed

clippy_lints/src/float_literal.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use crate::utils::span_lint_and_sugg;
2-
use crate::utils::sugg::format_numeric_literal;
1+
use crate::utils::{numeric_literal, span_lint_and_sugg};
32
use if_chain::if_chain;
43
use rustc::ty;
54
use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
@@ -109,7 +108,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatLiteral {
109108
expr.span,
110109
"literal cannot be represented as the underlying type without loss of precision",
111110
"consider changing the type or replacing it with",
112-
format_numeric_literal(&float_str, type_suffix, true),
111+
numeric_literal::format(&float_str, type_suffix, true),
113112
Applicability::MachineApplicable,
114113
);
115114
}
@@ -120,7 +119,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatLiteral {
120119
expr.span,
121120
"float has excessive precision",
122121
"consider changing the type or truncating it to",
123-
format_numeric_literal(&float_str, type_suffix, true),
122+
numeric_literal::format(&float_str, type_suffix, true),
124123
Applicability::MachineApplicable,
125124
);
126125
}

clippy_lints/src/floating_point_arithmetic.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::consts::{
22
constant, constant_simple, Constant,
33
Constant::{F32, F64},
44
};
5-
use crate::utils::{higher, span_lint_and_sugg, sugg, SpanlessEq};
5+
use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
66
use if_chain::if_chain;
77
use rustc::ty;
88
use rustc_errors::Applicability;
@@ -14,7 +14,7 @@ use rustc_span::source_map::Spanned;
1414
use rustc_ast::ast;
1515
use std::f32::consts as f32_consts;
1616
use std::f64::consts as f64_consts;
17-
use sugg::{format_numeric_literal, Sugg};
17+
use sugg::Sugg;
1818

1919
declare_clippy_lint! {
2020
/// **What it does:** Looks for floating-point expressions that
@@ -276,7 +276,7 @@ fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
276276
format!(
277277
"{}.powi({})",
278278
Sugg::hir(cx, &args[0], ".."),
279-
format_numeric_literal(&exponent.to_string(), None, false)
279+
numeric_literal::format(&exponent.to_string(), None, false)
280280
),
281281
)
282282
} else {

clippy_lints/src/literal_representation.rs

+6-220
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
//! Lints concerned with the grouping of digits with underscores in integral or
22
//! floating-point literal expressions.
33
4-
use crate::utils::{in_macro, snippet_opt, span_lint_and_sugg};
4+
use crate::utils::{
5+
in_macro,
6+
numeric_literal::{NumericLiteral, Radix},
7+
snippet_opt, span_lint_and_sugg,
8+
};
59
use if_chain::if_chain;
610
use rustc::lint::in_external_macro;
7-
use rustc_ast::ast::{Expr, ExprKind, Lit, LitFloatType, LitIntType, LitKind};
11+
use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
812
use rustc_errors::Applicability;
913
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
1014
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
@@ -103,224 +107,6 @@ declare_clippy_lint! {
103107
"using decimal representation when hexadecimal would be better"
104108
}
105109

106-
#[derive(Debug, PartialEq)]
107-
pub(super) enum Radix {
108-
Binary,
109-
Octal,
110-
Decimal,
111-
Hexadecimal,
112-
}
113-
114-
impl Radix {
115-
/// Returns a reasonable digit group size for this radix.
116-
#[must_use]
117-
fn suggest_grouping(&self) -> usize {
118-
match *self {
119-
Self::Binary | Self::Hexadecimal => 4,
120-
Self::Octal | Self::Decimal => 3,
121-
}
122-
}
123-
}
124-
125-
/// A helper method to format numeric literals with digit grouping.
126-
/// `lit` must be a valid numeric literal without suffix.
127-
pub fn format_numeric_literal(lit: &str, type_suffix: Option<&str>, float: bool) -> String {
128-
NumericLiteral::new(lit, type_suffix, float).format()
129-
}
130-
131-
#[derive(Debug)]
132-
pub(super) struct NumericLiteral<'a> {
133-
/// Which radix the literal was represented in.
134-
radix: Radix,
135-
/// The radix prefix, if present.
136-
prefix: Option<&'a str>,
137-
138-
/// The integer part of the number.
139-
integer: &'a str,
140-
/// The fraction part of the number.
141-
fraction: Option<&'a str>,
142-
/// The character used as exponent seperator (b'e' or b'E') and the exponent part.
143-
exponent: Option<(char, &'a str)>,
144-
145-
/// The type suffix, including preceding underscore if present.
146-
suffix: Option<&'a str>,
147-
}
148-
149-
impl<'a> NumericLiteral<'a> {
150-
fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
151-
if lit.kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
152-
let (unsuffixed, suffix) = split_suffix(&src, &lit.kind);
153-
let float = if let LitKind::Float(..) = lit.kind { true } else { false };
154-
Some(NumericLiteral::new(unsuffixed, suffix, float))
155-
} else {
156-
None
157-
}
158-
}
159-
160-
#[must_use]
161-
fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
162-
// Determine delimiter for radix prefix, if present, and radix.
163-
let radix = if lit.starts_with("0x") {
164-
Radix::Hexadecimal
165-
} else if lit.starts_with("0b") {
166-
Radix::Binary
167-
} else if lit.starts_with("0o") {
168-
Radix::Octal
169-
} else {
170-
Radix::Decimal
171-
};
172-
173-
// Grab part of the literal after prefix, if present.
174-
let (prefix, mut sans_prefix) = if let Radix::Decimal = radix {
175-
(None, lit)
176-
} else {
177-
let (p, s) = lit.split_at(2);
178-
(Some(p), s)
179-
};
180-
181-
if suffix.is_some() && sans_prefix.ends_with('_') {
182-
// The '_' before the suffix isn't part of the digits
183-
sans_prefix = &sans_prefix[..sans_prefix.len() - 1];
184-
}
185-
186-
let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float);
187-
188-
Self {
189-
radix,
190-
prefix,
191-
integer,
192-
fraction,
193-
exponent,
194-
suffix,
195-
}
196-
}
197-
198-
fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) {
199-
let mut integer = digits;
200-
let mut fraction = None;
201-
let mut exponent = None;
202-
203-
if float {
204-
for (i, c) in digits.char_indices() {
205-
match c {
206-
'.' => {
207-
integer = &digits[..i];
208-
fraction = Some(&digits[i + 1..]);
209-
},
210-
'e' | 'E' => {
211-
if integer.len() > i {
212-
integer = &digits[..i];
213-
} else {
214-
fraction = Some(&digits[integer.len() + 1..i]);
215-
};
216-
exponent = Some((c, &digits[i + 1..]));
217-
break;
218-
},
219-
_ => {},
220-
}
221-
}
222-
}
223-
224-
(integer, fraction, exponent)
225-
}
226-
227-
/// Returns literal formatted in a sensible way.
228-
fn format(&self) -> String {
229-
let mut output = String::new();
230-
231-
if let Some(prefix) = self.prefix {
232-
output.push_str(prefix);
233-
}
234-
235-
let group_size = self.radix.suggest_grouping();
236-
237-
Self::group_digits(
238-
&mut output,
239-
self.integer,
240-
group_size,
241-
true,
242-
self.radix == Radix::Hexadecimal,
243-
);
244-
245-
if let Some(fraction) = self.fraction {
246-
output.push('.');
247-
Self::group_digits(&mut output, fraction, group_size, false, false);
248-
}
249-
250-
if let Some((separator, exponent)) = self.exponent {
251-
output.push(separator);
252-
Self::group_digits(&mut output, exponent, group_size, true, false);
253-
}
254-
255-
if let Some(suffix) = self.suffix {
256-
output.push('_');
257-
output.push_str(suffix);
258-
}
259-
260-
output
261-
}
262-
263-
fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
264-
debug_assert!(group_size > 0);
265-
266-
let mut digits = input.chars().filter(|&c| c != '_');
267-
268-
let first_group_size;
269-
270-
if partial_group_first {
271-
first_group_size = (digits.clone().count() - 1) % group_size + 1;
272-
if pad {
273-
for _ in 0..group_size - first_group_size {
274-
output.push('0');
275-
}
276-
}
277-
} else {
278-
first_group_size = group_size;
279-
}
280-
281-
for _ in 0..first_group_size {
282-
if let Some(digit) = digits.next() {
283-
output.push(digit);
284-
}
285-
}
286-
287-
for (c, i) in digits.zip((0..group_size).cycle()) {
288-
if i == 0 {
289-
output.push('_');
290-
}
291-
output.push(c);
292-
}
293-
}
294-
}
295-
296-
fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
297-
debug_assert!(lit_kind.is_numeric());
298-
if let Some(suffix_length) = lit_suffix_length(lit_kind) {
299-
let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
300-
(unsuffixed, Some(suffix))
301-
} else {
302-
(src, None)
303-
}
304-
}
305-
306-
fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
307-
debug_assert!(lit_kind.is_numeric());
308-
let suffix = match lit_kind {
309-
LitKind::Int(_, int_lit_kind) => match int_lit_kind {
310-
LitIntType::Signed(int_ty) => Some(int_ty.name_str()),
311-
LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()),
312-
LitIntType::Unsuffixed => None,
313-
},
314-
LitKind::Float(_, float_lit_kind) => match float_lit_kind {
315-
LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()),
316-
LitFloatType::Unsuffixed => None,
317-
},
318-
_ => None,
319-
};
320-
321-
suffix.map(str::len)
322-
}
323-
324110
enum WarningType {
325111
UnreadableLiteral,
326112
InconsistentDigitGrouping,

clippy_lints/src/types.rs

+22-19
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ use crate::consts::{constant, Constant};
3030
use crate::utils::paths;
3131
use crate::utils::{
3232
clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, last_path_segment, match_def_path,
33-
match_path, method_chain_args, multispan_sugg, qpath_res, same_tys, sext, snippet, snippet_opt,
34-
snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg,
35-
span_lint_and_then, unsext,
33+
match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, qpath_res, same_tys, sext, snippet,
34+
snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help,
35+
span_lint_and_sugg, span_lint_and_then, unsext,
3636
};
3737

3838
declare_clippy_lint! {
@@ -1210,22 +1210,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Casts {
12101210
let (cast_from, cast_to) = (cx.tables.expr_ty(ex), cx.tables.expr_ty(expr));
12111211
lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to);
12121212
if let ExprKind::Lit(ref lit) = ex.kind {
1213-
if let LitKind::Int(n, _) = lit.node {
1214-
if cast_to.is_floating_point() {
1215-
let from_nbits = 128 - n.leading_zeros();
1216-
let to_nbits = fp_ty_mantissa_nbits(cast_to);
1217-
if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits {
1218-
span_lint_and_sugg(
1219-
cx,
1220-
UNNECESSARY_CAST,
1221-
expr.span,
1222-
&format!("casting integer literal to `{}` is unnecessary", cast_to),
1223-
"try",
1224-
format!("{}_{}", n, cast_to),
1225-
Applicability::MachineApplicable,
1226-
);
1227-
return;
1228-
}
1213+
if_chain! {
1214+
if let LitKind::Int(n, _) = lit.node;
1215+
if let Some(src) = snippet_opt(cx, lit.span);
1216+
if cast_to.is_floating_point();
1217+
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node);
1218+
let from_nbits = 128 - n.leading_zeros();
1219+
let to_nbits = fp_ty_mantissa_nbits(cast_to);
1220+
if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
1221+
then {
1222+
span_lint_and_sugg(
1223+
cx,
1224+
UNNECESSARY_CAST,
1225+
expr.span,
1226+
&format!("casting integer literal to `{}` is unnecessary", cast_to),
1227+
"try",
1228+
format!("{}_{}", n, cast_to),
1229+
Applicability::MachineApplicable,
1230+
);
1231+
return;
12291232
}
12301233
}
12311234
match lit.node {

clippy_lints/src/utils/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub mod higher;
1212
mod hir_utils;
1313
pub mod inspector;
1414
pub mod internal_lints;
15+
pub mod numeric_literal;
1516
pub mod paths;
1617
pub mod ptr;
1718
pub mod sugg;

0 commit comments

Comments
 (0)