diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index d755112a178e..33a577f336de 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -64,7 +64,7 @@ struct NumericFallbackVisitor<'a, 'tcx> { impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { - ty_bounds: Vec::new(), + ty_bounds: vec![TyBound::Nothing], cx, } } @@ -121,6 +121,42 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { } }, + ExprKind::Struct(qpath, fields, base) => { + if_chain! { + if let Some(def_id) = self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(); + let ty = self.cx.tcx.type_of(def_id); + if let Some(adt_def) = ty.ty_adt_def(); + if adt_def.is_struct(); + if let Some(variant) = adt_def.variants.iter().next(); + then { + let fields_def = &variant.fields; + + // Push field type then visit each field expr. + for field in fields.iter() { + let bound = + fields_def + .iter() + .find_map(|f_def| { + if f_def.ident == field.ident + { Some(self.cx.tcx.type_of(f_def.did)) } + else { None } + }); + self.ty_bounds.push(bound.into()); + self.visit_expr(field.expr); + self.ty_bounds.pop(); + } + + // Visit base with no bound. + if let Some(base) = base { + self.ty_bounds.push(TyBound::Nothing); + self.visit_expr(base); + self.ty_bounds.pop(); + } + return; + } + } + }, + ExprKind::Lit(lit) => { let ty = self.cx.typeck_results().expr_ty(expr); self.check_lit(lit, ty); @@ -166,13 +202,13 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option { +enum TyBound<'tcx> { Any, - Ty(Ty<'ctx>), + Ty(Ty<'tcx>), Nothing, } -impl<'ctx> TyBound<'ctx> { +impl<'tcx> TyBound<'tcx> { fn is_integral(self) -> bool { match self { TyBound::Any => true, @@ -181,3 +217,12 @@ impl<'ctx> TyBound<'ctx> { } } } + +impl<'tcx> From>> for TyBound<'tcx> { + fn from(v: Option>) -> Self { + match v { + Some(t) => TyBound::Ty(t), + None => TyBound::Nothing, + } + } +} diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index c48810944693..0b3758952ac6 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -39,6 +39,20 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 1 }; + + let x: _ = if true { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + } else { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 2 + }; } } @@ -46,7 +60,7 @@ mod function_def { fn ret_i32() -> i32 { // Even though the output type is specified, // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. - 23 + 1 } fn test() { @@ -77,6 +91,27 @@ mod function_calls { } } +mod struct_ctor { + struct ConcreteStruct { + x: i32, + } + + struct GenericStruct { + x: T, + } + + fn test() { + // Should NOT lint this because the field type is bound to a concrete type. + ConcreteStruct { x: 1 }; + + // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type. + GenericStruct { x: 1 }; + + // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type. + let _ = GenericStruct { x: 1 }; + } +} + mod method_calls { struct StructForMethodCallTest {} diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index c71d05d79934..50fcdfb510e5 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -112,7 +112,47 @@ LL | let y = 1; = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:73:21 + --> $DIR/default_numeric_fallback.rs:45:21 + | +LL | let y = 1; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:51:21 + | +LL | let y = 1; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:63:9 + | +LL | 1 + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:69:27 + | +LL | let f = || -> _ { 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:73:29 + | +LL | let f = || -> i32 { 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:87:21 | LL | generic_arg(1); | ^ @@ -120,7 +160,7 @@ LL | generic_arg(1); = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:76:32 + --> $DIR/default_numeric_fallback.rs:90:32 | LL | let x: _ = generic_arg(1); | ^ @@ -128,12 +168,28 @@ LL | let x: _ = generic_arg(1); = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:96:23 + --> $DIR/default_numeric_fallback.rs:108:28 + | +LL | GenericStruct { x: 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:111:36 + | +LL | let _ = GenericStruct { x: 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:131:23 | LL | s.generic_arg(1); | ^ | = help: consider adding suffix to avoid default numeric fallback -error: aborting due to 17 previous errors +error: aborting due to 24 previous errors