Skip to content

Commit 93796b2

Browse files
committed
Add some restrictions to default_numeric_fallback to avoid FNs
1 parent 22d4483 commit 93796b2

File tree

4 files changed

+218
-45
lines changed

4 files changed

+218
-45
lines changed
Lines changed: 132 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
use rustc_ast::{LitFloatType, LitIntType, LitKind};
2-
use rustc_data_structures::fx::FxHashSet;
3-
use rustc_hir::{Expr, ExprKind, HirId, Stmt, StmtKind};
1+
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
2+
use rustc_hir::{
3+
intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
4+
Body, Expr, ExprKind, Lit, Stmt, StmtKind,
5+
};
46
use rustc_lint::{LateContext, LateLintPass};
5-
use rustc_middle::ty::{self, FloatTy, IntTy};
6-
use rustc_session::{declare_tool_lint, impl_lint_pass};
7+
use rustc_middle::{
8+
hir::map::Map,
9+
ty::{self, FloatTy, IntTy, Ty},
10+
};
11+
use rustc_session::{declare_lint_pass, declare_tool_lint};
712

813
use if_chain::if_chain;
914

@@ -33,53 +38,141 @@ declare_clippy_lint! {
3338
/// Use instead:
3439
/// ```rust
3540
/// let i = 10i32;
36-
/// let f: f64 = 1.23;
41+
/// let f = 1.23f64;
3742
/// ```
3843
pub DEFAULT_NUMERIC_FALLBACK,
3944
restriction,
4045
"usage of unconstrained numeric literals which may cause default numeric fallback."
4146
}
4247

43-
#[derive(Default)]
44-
pub struct DefaultNumericFallback {
45-
/// Hold `init` in `Local` if `Local` has a type annotation.
46-
bounded_inits: FxHashSet<HirId>,
48+
declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
49+
50+
impl LateLintPass<'_> for DefaultNumericFallback {
51+
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
52+
let mut visitor = NumericFallbackVisitor::new(cx);
53+
visitor.visit_body(body);
54+
}
4755
}
4856

49-
impl_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
57+
struct NumericFallbackVisitor<'a, 'tcx> {
58+
/// Stack manages type bound of exprs. The top element holds current expr type.
59+
ty_bounds: Vec<TyBound<'tcx>>,
5060

51-
impl LateLintPass<'_> for DefaultNumericFallback {
52-
fn check_stmt(&mut self, _: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
53-
if_chain! {
54-
if let StmtKind::Local(local) = stmt.kind;
55-
if local.ty.is_some();
56-
if let Some(init) = local.init;
57-
then {
58-
self.bounded_inits.insert(init.hir_id);
59-
}
61+
cx: &'a LateContext<'tcx>,
62+
}
63+
64+
impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
65+
fn new(cx: &'a LateContext<'tcx>) -> Self {
66+
Self {
67+
ty_bounds: vec![TyBound::Nothing],
68+
cx,
6069
}
6170
}
6271

63-
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
64-
let expr_ty = cx.typeck_results().expr_ty(expr);
65-
let hir_id = expr.hir_id;
72+
/// Check whether a passed literal has potential to cause fallback or not.
73+
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
74+
let ty_bound = self.ty_bounds.last().unwrap();
6675
if_chain! {
67-
if let ExprKind::Lit(ref lit) = expr.kind;
68-
if matches!(lit.node,
69-
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
70-
if matches!(expr_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64));
71-
if !self.bounded_inits.contains(&hir_id);
72-
if !cx.tcx.hir().parent_iter(hir_id).any(|(ref hir_id, _)| self.bounded_inits.contains(hir_id));
73-
then {
74-
span_lint_and_help(
75-
cx,
76-
DEFAULT_NUMERIC_FALLBACK,
77-
lit.span,
78-
"default numeric fallback might occur",
79-
None,
80-
"consider adding suffix to avoid default numeric fallback",
81-
)
82-
}
76+
if matches!(lit.node,
77+
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
78+
if matches!(lit_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64));
79+
if !ty_bound.is_integral();
80+
then {
81+
span_lint_and_help(
82+
self.cx,
83+
DEFAULT_NUMERIC_FALLBACK,
84+
lit.span,
85+
"default numeric fallback might occur",
86+
None,
87+
"consider adding suffix to avoid default numeric fallback",
88+
);
89+
}
90+
}
91+
}
92+
}
93+
94+
impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
95+
type Map = Map<'tcx>;
96+
97+
#[allow(clippy::too_many_lines)]
98+
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
99+
match &expr.kind {
100+
ExprKind::Call(func, args) => {
101+
if_chain! {
102+
if let ExprKind::Path(ref func_path) = func.kind;
103+
if let Some(def_id) = self.cx.qpath_res(func_path, func.hir_id).opt_def_id();
104+
then {
105+
let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
106+
for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) {
107+
// Push found arg type, then visit arg.
108+
self.ty_bounds.push(TyBound::Ty(bound));
109+
self.visit_expr(expr);
110+
self.ty_bounds.pop();
111+
}
112+
return;
113+
}
114+
}
115+
},
116+
117+
ExprKind::MethodCall(_, _, args, _) => {
118+
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
119+
let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
120+
for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) {
121+
self.ty_bounds.push(TyBound::Ty(bound));
122+
self.visit_expr(expr);
123+
self.ty_bounds.pop();
124+
}
125+
return;
126+
}
127+
},
128+
129+
ExprKind::Lit(lit) => {
130+
let ty = self.cx.typeck_results().expr_ty(expr);
131+
self.check_lit(lit, ty);
132+
return;
133+
},
134+
135+
_ => {},
136+
}
137+
138+
walk_expr(self, expr);
139+
}
140+
141+
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
142+
match stmt.kind {
143+
StmtKind::Local(local) => {
144+
if local.ty.is_some() {
145+
self.ty_bounds.push(TyBound::Any)
146+
} else {
147+
self.ty_bounds.push(TyBound::Nothing)
148+
}
149+
},
150+
151+
_ => self.ty_bounds.push(TyBound::Nothing),
152+
}
153+
154+
walk_stmt(self, stmt);
155+
self.ty_bounds.pop();
156+
}
157+
158+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
159+
NestedVisitorMap::None
160+
}
161+
}
162+
163+
#[derive(Debug, Clone, Copy)]
164+
enum TyBound<'ctx> {
165+
Any,
166+
Ty(Ty<'ctx>),
167+
Nothing,
168+
}
169+
170+
impl<'ctx> TyBound<'ctx> {
171+
fn is_integral(self) -> bool {
172+
match self {
173+
TyBound::Any => true,
174+
TyBound::Ty(t) => t.is_integral(),
175+
TyBound::Nothing => false,
83176
}
84177
}
85178
}

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10281028
store.register_late_pass(|| box strings::StringAdd);
10291029
store.register_late_pass(|| box implicit_return::ImplicitReturn);
10301030
store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
1031-
store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback::default());
1031+
store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback);
10321032

10331033
let msrv = conf.msrv.as_ref().and_then(|s| {
10341034
parse_msrv(s, None, None).or_else(|| {

tests/ui/default_numeric_fallback.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,58 @@
11
#![warn(clippy::default_numeric_fallback)]
22
#![allow(unused)]
3+
#![allow(clippy::never_loop)]
4+
#![allow(clippy::no_effect)]
5+
#![allow(clippy::unnecessary_operation)]
6+
7+
fn concrete_arg(x: i32) {}
8+
9+
fn generic_arg<T>(t: T) {}
10+
11+
struct ConcreteStruct {
12+
x: i32,
13+
}
14+
15+
struct StructForMethodCallTest {
16+
x: i32,
17+
}
18+
19+
impl StructForMethodCallTest {
20+
fn concrete_arg(&self, x: i32) {}
21+
22+
fn generic_arg<T>(&self, t: T) {}
23+
}
324

425
fn main() {
26+
let s = StructForMethodCallTest { x: 10_i32 };
27+
528
// Bad.
629
let x = 1;
730
let x = 0.1;
31+
832
let x = if true { 1 } else { 2 };
933

34+
let x: _ = {
35+
let y = 1;
36+
1
37+
};
38+
39+
generic_arg(10);
40+
s.generic_arg(10);
41+
let x: _ = generic_arg(10);
42+
let x: _ = s.generic_arg(10);
43+
1044
// Good.
1145
let x = 1_i32;
1246
let x: i32 = 1;
1347
let x: _ = 1;
1448
let x = 0.1_f64;
1549
let x: f64 = 0.1;
1650
let x: _ = 0.1;
51+
1752
let x: _ = if true { 1 } else { 2 };
53+
54+
concrete_arg(10);
55+
s.concrete_arg(10);
56+
let x = concrete_arg(10);
57+
let x = s.concrete_arg(10);
1858
}
Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: default numeric fallback might occur
2-
--> $DIR/default_numeric_fallback.rs:6:13
2+
--> $DIR/default_numeric_fallback.rs:29:13
33
|
44
LL | let x = 1;
55
| ^
@@ -8,28 +8,68 @@ LL | let x = 1;
88
= help: consider adding suffix to avoid default numeric fallback
99

1010
error: default numeric fallback might occur
11-
--> $DIR/default_numeric_fallback.rs:7:13
11+
--> $DIR/default_numeric_fallback.rs:30:13
1212
|
1313
LL | let x = 0.1;
1414
| ^^^
1515
|
1616
= help: consider adding suffix to avoid default numeric fallback
1717

1818
error: default numeric fallback might occur
19-
--> $DIR/default_numeric_fallback.rs:8:23
19+
--> $DIR/default_numeric_fallback.rs:32:23
2020
|
2121
LL | let x = if true { 1 } else { 2 };
2222
| ^
2323
|
2424
= help: consider adding suffix to avoid default numeric fallback
2525

2626
error: default numeric fallback might occur
27-
--> $DIR/default_numeric_fallback.rs:8:34
27+
--> $DIR/default_numeric_fallback.rs:32:34
2828
|
2929
LL | let x = if true { 1 } else { 2 };
3030
| ^
3131
|
3232
= help: consider adding suffix to avoid default numeric fallback
3333

34-
error: aborting due to 4 previous errors
34+
error: default numeric fallback might occur
35+
--> $DIR/default_numeric_fallback.rs:35:17
36+
|
37+
LL | let y = 1;
38+
| ^
39+
|
40+
= help: consider adding suffix to avoid default numeric fallback
41+
42+
error: default numeric fallback might occur
43+
--> $DIR/default_numeric_fallback.rs:39:17
44+
|
45+
LL | generic_arg(10);
46+
| ^^
47+
|
48+
= help: consider adding suffix to avoid default numeric fallback
49+
50+
error: default numeric fallback might occur
51+
--> $DIR/default_numeric_fallback.rs:40:19
52+
|
53+
LL | s.generic_arg(10);
54+
| ^^
55+
|
56+
= help: consider adding suffix to avoid default numeric fallback
57+
58+
error: default numeric fallback might occur
59+
--> $DIR/default_numeric_fallback.rs:41:28
60+
|
61+
LL | let x: _ = generic_arg(10);
62+
| ^^
63+
|
64+
= help: consider adding suffix to avoid default numeric fallback
65+
66+
error: default numeric fallback might occur
67+
--> $DIR/default_numeric_fallback.rs:42:30
68+
|
69+
LL | let x: _ = s.generic_arg(10);
70+
| ^^
71+
|
72+
= help: consider adding suffix to avoid default numeric fallback
73+
74+
error: aborting due to 9 previous errors
3575

0 commit comments

Comments
 (0)