|  | 
|  | 1 | +use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet}; | 
|  | 2 | +use crate::utils::{span_lint_and_note, span_lint_and_sugg}; | 
|  | 3 | +use if_chain::if_chain; | 
|  | 4 | +use rustc_data_structures::fx::FxHashSet; | 
|  | 5 | +use rustc_errors::Applicability; | 
|  | 6 | +use rustc_hir::def::Res; | 
|  | 7 | +use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; | 
|  | 8 | +use rustc_lint::{LateContext, LateLintPass}; | 
|  | 9 | +use rustc_middle::ty::{self, Adt, Ty}; | 
|  | 10 | +use rustc_session::{declare_tool_lint, impl_lint_pass}; | 
|  | 11 | +use rustc_span::symbol::{Ident, Symbol}; | 
|  | 12 | +use rustc_span::Span; | 
|  | 13 | + | 
|  | 14 | +declare_clippy_lint! { | 
|  | 15 | +    /// **What it does:** Checks for literal calls to `Default::default()`. | 
|  | 16 | +    /// | 
|  | 17 | +    /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is | 
|  | 18 | +    /// being gotten than the generic `Default`. | 
|  | 19 | +    /// | 
|  | 20 | +    /// **Known problems:** None. | 
|  | 21 | +    /// | 
|  | 22 | +    /// **Example:** | 
|  | 23 | +    /// ```rust | 
|  | 24 | +    /// // Bad | 
|  | 25 | +    /// let s: String = Default::default(); | 
|  | 26 | +    /// | 
|  | 27 | +    /// // Good | 
|  | 28 | +    /// let s = String::default(); | 
|  | 29 | +    /// ``` | 
|  | 30 | +    pub DEFAULT_TRAIT_ACCESS, | 
|  | 31 | +    pedantic, | 
|  | 32 | +    "checks for literal calls to `Default::default()`" | 
|  | 33 | +} | 
|  | 34 | + | 
|  | 35 | +declare_clippy_lint! { | 
|  | 36 | +    /// **What it does:** Checks for immediate reassignment of fields initialized | 
|  | 37 | +    /// with Default::default(). | 
|  | 38 | +    /// | 
|  | 39 | +    /// **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax). | 
|  | 40 | +    /// | 
|  | 41 | +    /// **Known problems:** Assignments to patterns that are of tuple type are not linted. | 
|  | 42 | +    /// | 
|  | 43 | +    /// **Example:** | 
|  | 44 | +    /// Bad: | 
|  | 45 | +    /// ``` | 
|  | 46 | +    /// # #[derive(Default)] | 
|  | 47 | +    /// # struct A { i: i32 } | 
|  | 48 | +    /// let mut a: A = Default::default(); | 
|  | 49 | +    /// a.i = 42; | 
|  | 50 | +    /// ``` | 
|  | 51 | +    /// Use instead: | 
|  | 52 | +    /// ``` | 
|  | 53 | +    /// # #[derive(Default)] | 
|  | 54 | +    /// # struct A { i: i32 } | 
|  | 55 | +    /// let a = A { | 
|  | 56 | +    ///     i: 42, | 
|  | 57 | +    ///     .. Default::default() | 
|  | 58 | +    /// }; | 
|  | 59 | +    /// ``` | 
|  | 60 | +    pub FIELD_REASSIGN_WITH_DEFAULT, | 
|  | 61 | +    style, | 
|  | 62 | +    "binding initialized with Default should have its fields set in the initializer" | 
|  | 63 | +} | 
|  | 64 | + | 
|  | 65 | +#[derive(Default)] | 
|  | 66 | +pub struct Default { | 
|  | 67 | +    // Spans linted by `field_reassign_with_default`. | 
|  | 68 | +    reassigned_linted: FxHashSet<Span>, | 
|  | 69 | +} | 
|  | 70 | + | 
|  | 71 | +impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); | 
|  | 72 | + | 
|  | 73 | +impl LateLintPass<'_> for Default { | 
|  | 74 | +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | 
|  | 75 | +        if_chain! { | 
|  | 76 | +            // Avoid cases already linted by `field_reassign_with_default` | 
|  | 77 | +            if !self.reassigned_linted.contains(&expr.span); | 
|  | 78 | +            if let ExprKind::Call(ref path, ..) = expr.kind; | 
|  | 79 | +            if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); | 
|  | 80 | +            if let ExprKind::Path(ref qpath) = path.kind; | 
|  | 81 | +            if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); | 
|  | 82 | +            if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); | 
|  | 83 | +            // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type. | 
|  | 84 | +            if let QPath::Resolved(None, _path) = qpath; | 
|  | 85 | +            then { | 
|  | 86 | +                let expr_ty = cx.typeck_results().expr_ty(expr); | 
|  | 87 | +                if let ty::Adt(def, ..) = expr_ty.kind() { | 
|  | 88 | +                    // TODO: Work out a way to put "whatever the imported way of referencing | 
|  | 89 | +                    // this type in this file" rather than a fully-qualified type. | 
|  | 90 | +                    let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); | 
|  | 91 | +                    span_lint_and_sugg( | 
|  | 92 | +                        cx, | 
|  | 93 | +                        DEFAULT_TRAIT_ACCESS, | 
|  | 94 | +                        expr.span, | 
|  | 95 | +                        &format!("calling `{}` is more clear than this expression", replacement), | 
|  | 96 | +                        "try", | 
|  | 97 | +                        replacement, | 
|  | 98 | +                        Applicability::Unspecified, // First resolve the TODO above | 
|  | 99 | +                    ); | 
|  | 100 | +                } | 
|  | 101 | +            } | 
|  | 102 | +        } | 
|  | 103 | +    } | 
|  | 104 | + | 
|  | 105 | +    fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { | 
|  | 106 | +        // find all binding statements like `let mut _ = T::default()` where `T::default()` is the | 
|  | 107 | +        // `default` method of the `Default` trait, and store statement index in current block being | 
|  | 108 | +        // checked and the name of the bound variable | 
|  | 109 | +        let binding_statements_using_default = enumerate_bindings_using_default(cx, block); | 
|  | 110 | + | 
|  | 111 | +        // start from the `let mut _ = _::default();` and look at all the following | 
|  | 112 | +        // statements, see if they re-assign the fields of the binding | 
|  | 113 | +        for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default { | 
|  | 114 | +            // the last statement of a block cannot trigger the lint | 
|  | 115 | +            if stmt_idx == block.stmts.len() - 1 { | 
|  | 116 | +                break; | 
|  | 117 | +            } | 
|  | 118 | + | 
|  | 119 | +            // find all "later statement"'s where the fields of the binding set as | 
|  | 120 | +            // Default::default() get reassigned, unless the reassignment refers to the original binding | 
|  | 121 | +            let mut first_assign = None; | 
|  | 122 | +            let mut assigned_fields = Vec::new(); | 
|  | 123 | +            let mut cancel_lint = false; | 
|  | 124 | +            for consecutive_statement in &block.stmts[stmt_idx + 1..] { | 
|  | 125 | +                // interrupt if the statement is a let binding (`Local`) that shadows the original | 
|  | 126 | +                // binding | 
|  | 127 | +                if stmt_shadows_binding(consecutive_statement, binding_name) { | 
|  | 128 | +                    break; | 
|  | 129 | +                } | 
|  | 130 | +                // find out if and which field was set by this `consecutive_statement` | 
|  | 131 | +                else if let Some((field_ident, assign_rhs)) = | 
|  | 132 | +                    field_reassigned_by_stmt(consecutive_statement, binding_name) | 
|  | 133 | +                { | 
|  | 134 | +                    // interrupt and cancel lint if assign_rhs references the original binding | 
|  | 135 | +                    if contains_name(binding_name, assign_rhs) { | 
|  | 136 | +                        cancel_lint = true; | 
|  | 137 | +                        break; | 
|  | 138 | +                    } | 
|  | 139 | + | 
|  | 140 | +                    // if the field was previously assigned, replace the assignment, otherwise insert the assignment | 
|  | 141 | +                    if let Some(prev) = assigned_fields | 
|  | 142 | +                        .iter_mut() | 
|  | 143 | +                        .find(|(field_name, _)| field_name == &field_ident.name) | 
|  | 144 | +                    { | 
|  | 145 | +                        *prev = (field_ident.name, assign_rhs); | 
|  | 146 | +                    } else { | 
|  | 147 | +                        assigned_fields.push((field_ident.name, assign_rhs)); | 
|  | 148 | +                    } | 
|  | 149 | + | 
|  | 150 | +                    // also set first instance of error for help message | 
|  | 151 | +                    if first_assign.is_none() { | 
|  | 152 | +                        first_assign = Some(consecutive_statement); | 
|  | 153 | +                    } | 
|  | 154 | +                } | 
|  | 155 | +                // interrupt also if no field was assigned, since we only want to look at consecutive statements | 
|  | 156 | +                else { | 
|  | 157 | +                    break; | 
|  | 158 | +                } | 
|  | 159 | +            } | 
|  | 160 | + | 
|  | 161 | +            // if there are incorrectly assigned fields, do a span_lint_and_note to suggest | 
|  | 162 | +            // construction using `Ty { fields, ..Default::default() }` | 
|  | 163 | +            if !assigned_fields.is_empty() && !cancel_lint { | 
|  | 164 | +                // take the original assignment as span | 
|  | 165 | +                let stmt = &block.stmts[stmt_idx]; | 
|  | 166 | + | 
|  | 167 | +                if let StmtKind::Local(preceding_local) = &stmt.kind { | 
|  | 168 | +                    // filter out fields like `= Default::default()`, because the FRU already covers them | 
|  | 169 | +                    let assigned_fields = assigned_fields | 
|  | 170 | +                        .into_iter() | 
|  | 171 | +                        .filter(|(_, rhs)| !is_expr_default(rhs, cx)) | 
|  | 172 | +                        .collect::<Vec<(Symbol, &Expr<'_>)>>(); | 
|  | 173 | + | 
|  | 174 | +                    // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. | 
|  | 175 | +                    let ext_with_default = !fields_of_type(binding_type) | 
|  | 176 | +                        .iter() | 
|  | 177 | +                        .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); | 
|  | 178 | + | 
|  | 179 | +                    let field_list = assigned_fields | 
|  | 180 | +                        .into_iter() | 
|  | 181 | +                        .map(|(field, rhs)| { | 
|  | 182 | +                            // extract and store the assigned value for help message | 
|  | 183 | +                            let value_snippet = snippet(cx, rhs.span, ".."); | 
|  | 184 | +                            format!("{}: {}", field, value_snippet) | 
|  | 185 | +                        }) | 
|  | 186 | +                        .collect::<Vec<String>>() | 
|  | 187 | +                        .join(", "); | 
|  | 188 | + | 
|  | 189 | +                    let sugg = if ext_with_default { | 
|  | 190 | +                        if field_list.is_empty() { | 
|  | 191 | +                            format!("{}::default()", binding_type) | 
|  | 192 | +                        } else { | 
|  | 193 | +                            format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) | 
|  | 194 | +                        } | 
|  | 195 | +                    } else { | 
|  | 196 | +                        format!("{} {{ {} }}", binding_type, field_list) | 
|  | 197 | +                    }; | 
|  | 198 | + | 
|  | 199 | +                    // span lint once per statement that binds default | 
|  | 200 | +                    span_lint_and_note( | 
|  | 201 | +                        cx, | 
|  | 202 | +                        FIELD_REASSIGN_WITH_DEFAULT, | 
|  | 203 | +                        first_assign.unwrap().span, | 
|  | 204 | +                        "field assignment outside of initializer for an instance created with Default::default()", | 
|  | 205 | +                        Some(preceding_local.span), | 
|  | 206 | +                        &format!( | 
|  | 207 | +                            "consider initializing the variable with `{}` and removing relevant reassignments", | 
|  | 208 | +                            sugg | 
|  | 209 | +                        ), | 
|  | 210 | +                    ); | 
|  | 211 | +                    self.reassigned_linted.insert(span); | 
|  | 212 | +                } | 
|  | 213 | +            } | 
|  | 214 | +        } | 
|  | 215 | +    } | 
|  | 216 | +} | 
|  | 217 | + | 
|  | 218 | +/// Checks if the given expression is the `default` method belonging to the `Default` trait. | 
|  | 219 | +fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { | 
|  | 220 | +    if_chain! { | 
|  | 221 | +        if let ExprKind::Call(ref fn_expr, _) = &expr.kind; | 
|  | 222 | +        if let ExprKind::Path(qpath) = &fn_expr.kind; | 
|  | 223 | +        if let Res::Def(_, def_id) = qpath_res(cx, qpath, fn_expr.hir_id); | 
|  | 224 | +        then { | 
|  | 225 | +            // right hand side of assignment is `Default::default` | 
|  | 226 | +            match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD) | 
|  | 227 | +        } else { | 
|  | 228 | +            false | 
|  | 229 | +        } | 
|  | 230 | +    } | 
|  | 231 | +} | 
|  | 232 | + | 
|  | 233 | +/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except | 
|  | 234 | +/// for when the pattern type is a tuple. | 
|  | 235 | +fn enumerate_bindings_using_default<'tcx>( | 
|  | 236 | +    cx: &LateContext<'tcx>, | 
|  | 237 | +    block: &Block<'tcx>, | 
|  | 238 | +) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> { | 
|  | 239 | +    block | 
|  | 240 | +        .stmts | 
|  | 241 | +        .iter() | 
|  | 242 | +        .enumerate() | 
|  | 243 | +        .filter_map(|(idx, stmt)| { | 
|  | 244 | +            if_chain! { | 
|  | 245 | +                // only take `let ...` statements | 
|  | 246 | +                if let StmtKind::Local(ref local) = stmt.kind; | 
|  | 247 | +                // only take bindings to identifiers | 
|  | 248 | +                if let PatKind::Binding(_, _, ident, _) = local.pat.kind; | 
|  | 249 | +                // that are not tuples | 
|  | 250 | +                let ty = cx.typeck_results().pat_ty(local.pat); | 
|  | 251 | +                if !matches!(ty.kind(), ty::Tuple(_)); | 
|  | 252 | +                // only when assigning `... = Default::default()` | 
|  | 253 | +                if let Some(ref expr) = local.init; | 
|  | 254 | +                if is_expr_default(expr, cx); | 
|  | 255 | +                then { | 
|  | 256 | +                    Some((idx, ident.name, ty, expr.span)) | 
|  | 257 | +                } else { | 
|  | 258 | +                    None | 
|  | 259 | +                } | 
|  | 260 | +            } | 
|  | 261 | +        }) | 
|  | 262 | +        .collect() | 
|  | 263 | +} | 
|  | 264 | + | 
|  | 265 | +fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool { | 
|  | 266 | +    if let StmtKind::Local(local) = &this.kind { | 
|  | 267 | +        if let PatKind::Binding(_, _, ident, _) = local.pat.kind { | 
|  | 268 | +            return ident.name == shadowed; | 
|  | 269 | +        } | 
|  | 270 | +    } | 
|  | 271 | +    false | 
|  | 272 | +} | 
|  | 273 | + | 
|  | 274 | +/// Returns the reassigned field and the assigning expression (right-hand side of assign). | 
|  | 275 | +fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { | 
|  | 276 | +    if_chain! { | 
|  | 277 | +        // only take assignments | 
|  | 278 | +        if let StmtKind::Semi(ref later_expr) = this.kind; | 
|  | 279 | +        if let ExprKind::Assign(ref assign_lhs, ref assign_rhs, _) = later_expr.kind; | 
|  | 280 | +        // only take assignments to fields where the left-hand side field is a field of | 
|  | 281 | +        // the same binding as the previous statement | 
|  | 282 | +        if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind; | 
|  | 283 | +        if let ExprKind::Path(ref qpath) = binding.kind; | 
|  | 284 | +        if let QPath::Resolved(_, path) = qpath; | 
|  | 285 | +        if let Some(second_binding_name) = path.segments.last(); | 
|  | 286 | +        if second_binding_name.ident.name == binding_name; | 
|  | 287 | +        then { | 
|  | 288 | +            Some((field_ident, assign_rhs)) | 
|  | 289 | +        } else { | 
|  | 290 | +            None | 
|  | 291 | +        } | 
|  | 292 | +    } | 
|  | 293 | +} | 
|  | 294 | + | 
|  | 295 | +/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs. | 
|  | 296 | +fn fields_of_type(ty: Ty<'_>) -> Vec<Ident> { | 
|  | 297 | +    if let Adt(adt, _) = ty.kind() { | 
|  | 298 | +        if adt.is_struct() { | 
|  | 299 | +            let variant = &adt.non_enum_variant(); | 
|  | 300 | +            return variant.fields.iter().map(|f| f.ident).collect(); | 
|  | 301 | +        } | 
|  | 302 | +    } | 
|  | 303 | +    vec![] | 
|  | 304 | +} | 
0 commit comments