Skip to content

Commit 8131445

Browse files
committed
Auto merge of #7446 - Y-Nak:fix-7445, r=xFrednet,flip1995
`default_numeric_fallback`: Fix FP with floating literal Fix #7445 changelog: `default_numeric_fallback`: Fix FP with floating literal
2 parents bf4512e + 25e4c7d commit 8131445

8 files changed

+738
-43
lines changed

clippy_lints/src/default_numeric_fallback.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2-
use clippy_utils::source::snippet;
2+
use clippy_utils::numeric_literal;
3+
use clippy_utils::source::snippet_opt;
34
use if_chain::if_chain;
45
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
56
use rustc_errors::Applicability;
@@ -78,16 +79,25 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
7879
if let Some(ty_bound) = self.ty_bounds.last();
7980
if matches!(lit.node,
8081
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
81-
if !ty_bound.is_integral();
82+
if !ty_bound.is_numeric();
8283
then {
83-
let suffix = match lit_ty.kind() {
84-
ty::Int(IntTy::I32) => "i32",
85-
ty::Float(FloatTy::F64) => "f64",
84+
let (suffix, is_float) = match lit_ty.kind() {
85+
ty::Int(IntTy::I32) => ("i32", false),
86+
ty::Float(FloatTy::F64) => ("f64", true),
8687
// Default numeric fallback never results in other types.
8788
_ => return,
8889
};
8990

90-
let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix);
91+
let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
92+
src
93+
} else {
94+
match lit.node {
95+
LitKind::Int(src, _) => format!("{}", src),
96+
LitKind::Float(src, _) => format!("{}", src),
97+
_ => return,
98+
}
99+
};
100+
let sugg = numeric_literal::format(&src, Some(suffix), is_float);
91101
span_lint_and_sugg(
92102
self.cx,
93103
DEFAULT_NUMERIC_FALLBACK,
@@ -219,10 +229,10 @@ enum TyBound<'tcx> {
219229
}
220230

221231
impl<'tcx> TyBound<'tcx> {
222-
fn is_integral(self) -> bool {
232+
fn is_numeric(self) -> bool {
223233
match self {
224234
TyBound::Any => true,
225-
TyBound::Ty(t) => t.is_integral(),
235+
TyBound::Ty(t) => t.is_numeric(),
226236
TyBound::Nothing => false,
227237
}
228238
}

clippy_utils/src/numeric_literal.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ impl<'a> NumericLiteral<'a> {
162162
}
163163

164164
if let Some(suffix) = self.suffix {
165+
if output.ends_with('.') {
166+
output.push('0');
167+
}
165168
output.push('_');
166169
output.push_str(suffix);
167170
}
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// run-rustfix
2+
// aux-build:macro_rules.rs
3+
4+
#![warn(clippy::default_numeric_fallback)]
5+
#![allow(unused)]
6+
#![allow(clippy::never_loop)]
7+
#![allow(clippy::no_effect)]
8+
#![allow(clippy::unnecessary_operation)]
9+
#![allow(clippy::branches_sharing_code)]
10+
#![allow(clippy::match_single_binding)]
11+
12+
#[macro_use]
13+
extern crate macro_rules;
14+
15+
mod basic_expr {
16+
fn test() {
17+
// Should lint unsuffixed literals typed `f64`.
18+
let x = 0.12_f64;
19+
let x = [1.0_f64, 2.0_f64, 3.0_f64];
20+
let x = if true { (1.0_f64, 2.0_f64) } else { (3.0_f64, 4.0_f64) };
21+
let x = match 1.0_f64 {
22+
_ => 1.0_f64,
23+
};
24+
25+
// Should NOT lint suffixed literals.
26+
let x = 0.12_f64;
27+
28+
// Should NOT lint literals in init expr if `Local` has a type annotation.
29+
let x: f64 = 0.1;
30+
let x: [f64; 3] = [1., 2., 3.];
31+
let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
32+
let x: _ = 1.;
33+
}
34+
}
35+
36+
mod nested_local {
37+
fn test() {
38+
let x: _ = {
39+
// Should lint this because this literal is not bound to any types.
40+
let y = 1.0_f64;
41+
42+
// Should NOT lint this because this literal is bound to `_` of outer `Local`.
43+
1.
44+
};
45+
46+
let x: _ = if true {
47+
// Should lint this because this literal is not bound to any types.
48+
let y = 1.0_f64;
49+
50+
// Should NOT lint this because this literal is bound to `_` of outer `Local`.
51+
1.
52+
} else {
53+
// Should lint this because this literal is not bound to any types.
54+
let y = 1.0_f64;
55+
56+
// Should NOT lint this because this literal is bound to `_` of outer `Local`.
57+
2.
58+
};
59+
}
60+
}
61+
62+
mod function_def {
63+
fn ret_f64() -> f64 {
64+
// Even though the output type is specified,
65+
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
66+
1.0_f64
67+
}
68+
69+
fn test() {
70+
// Should lint this because return type is inferred to `f64` and NOT bound to a concrete
71+
// type.
72+
let f = || -> _ { 1.0_f64 };
73+
74+
// Even though the output type is specified,
75+
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
76+
let f = || -> f64 { 1.0_f64 };
77+
}
78+
}
79+
80+
mod function_calls {
81+
fn concrete_arg(f: f64) {}
82+
83+
fn generic_arg<T>(t: T) {}
84+
85+
fn test() {
86+
// Should NOT lint this because the argument type is bound to a concrete type.
87+
concrete_arg(1.);
88+
89+
// Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
90+
generic_arg(1.0_f64);
91+
92+
// Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
93+
let x: _ = generic_arg(1.0_f64);
94+
}
95+
}
96+
97+
mod struct_ctor {
98+
struct ConcreteStruct {
99+
x: f64,
100+
}
101+
102+
struct GenericStruct<T> {
103+
x: T,
104+
}
105+
106+
fn test() {
107+
// Should NOT lint this because the field type is bound to a concrete type.
108+
ConcreteStruct { x: 1. };
109+
110+
// Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
111+
GenericStruct { x: 1.0_f64 };
112+
113+
// Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
114+
let _ = GenericStruct { x: 1.0_f64 };
115+
}
116+
}
117+
118+
mod enum_ctor {
119+
enum ConcreteEnum {
120+
X(f64),
121+
}
122+
123+
enum GenericEnum<T> {
124+
X(T),
125+
}
126+
127+
fn test() {
128+
// Should NOT lint this because the field type is bound to a concrete type.
129+
ConcreteEnum::X(1.);
130+
131+
// Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
132+
GenericEnum::X(1.0_f64);
133+
}
134+
}
135+
136+
mod method_calls {
137+
struct StructForMethodCallTest {}
138+
139+
impl StructForMethodCallTest {
140+
fn concrete_arg(&self, f: f64) {}
141+
142+
fn generic_arg<T>(&self, t: T) {}
143+
}
144+
145+
fn test() {
146+
let s = StructForMethodCallTest {};
147+
148+
// Should NOT lint this because the argument type is bound to a concrete type.
149+
s.concrete_arg(1.);
150+
151+
// Should lint this because the argument type is bound to a concrete type.
152+
s.generic_arg(1.0_f64);
153+
}
154+
}
155+
156+
mod in_macro {
157+
macro_rules! internal_macro {
158+
() => {
159+
let x = 22.0_f64;
160+
};
161+
}
162+
163+
// Should lint in internal macro.
164+
fn internal() {
165+
internal_macro!();
166+
}
167+
168+
// Should NOT lint in external macro.
169+
fn external() {
170+
default_numeric_fallback!();
171+
}
172+
}
173+
174+
fn main() {}

0 commit comments

Comments
 (0)