1 use clippy_utils::diagnostics::span_lint; 2 use rustc_hir::{Expr, ExprKind}; 3 use rustc_lint::{LateContext, LateLintPass}; 4 use rustc_middle::ty; 5 use rustc_session::{declare_lint_pass, declare_tool_lint}; 6 7 declare_clippy_lint! { 8 /// ### What it does 9 /// Checks for needlessly including a base struct on update 10 /// when all fields are changed anyway. 11 /// 12 /// This lint is not applied to structs marked with 13 /// [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html). 14 /// 15 /// ### Why is this bad? 16 /// This will cost resources (because the base has to be 17 /// somewhere), and make the code less readable. 18 /// 19 /// ### Example 20 /// ```rust 21 /// # struct Point { 22 /// # x: i32, 23 /// # y: i32, 24 /// # z: i32, 25 /// # } 26 /// # let zero_point = Point { x: 0, y: 0, z: 0 }; 27 /// Point { 28 /// x: 1, 29 /// y: 1, 30 /// z: 1, 31 /// ..zero_point 32 /// }; 33 /// ``` 34 /// 35 /// Use instead: 36 /// ```rust,ignore 37 /// // Missing field `z` 38 /// Point { 39 /// x: 1, 40 /// y: 1, 41 /// ..zero_point 42 /// }; 43 /// ``` 44 #[clippy::version = "pre 1.29.0"] 45 pub NEEDLESS_UPDATE, 46 complexity, 47 "using `Foo { ..base }` when there are no missing fields" 48 } 49 50 declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]); 51 52 impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)53 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 54 if let ExprKind::Struct(_, fields, Some(base)) = expr.kind { 55 let ty = cx.typeck_results().expr_ty(expr); 56 if let ty::Adt(def, _) = ty.kind() { 57 if fields.len() == def.non_enum_variant().fields.len() 58 && !def.variant(0_usize.into()).is_field_list_non_exhaustive() 59 { 60 span_lint( 61 cx, 62 NEEDLESS_UPDATE, 63 base.span, 64 "struct update has no effect, all the fields in the struct have already been specified", 65 ); 66 } 67 } 68 } 69 } 70 } 71