]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/default.rs
Auto merge of #8487 - dswij:8478, r=giraffate
[rust.git] / clippy_lints / src / default.rs
index 710da8fe9e037ea6bc21eceeafc3afe205c07410..f7e4bc24321c5a8ac58461d41ef60683a3bc9102 100644 (file)
@@ -1,27 +1,27 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths};
+use clippy_utils::ty::{has_drop, is_copy};
+use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::Span;
 
 declare_clippy_lint! {
-    /// **What it does:** Checks for literal calls to `Default::default()`.
+    /// ### What it does
+    /// Checks for literal calls to `Default::default()`.
     ///
-    /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
+    /// ### Why is this bad?
+    /// It's more clear to the reader to use the name of the type whose default is
     /// being gotten than the generic `Default`.
     ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
+    /// ### Example
     /// ```rust
     /// // Bad
     /// let s: String = Default::default();
     /// // Good
     /// let s = String::default();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DEFAULT_TRAIT_ACCESS,
     pedantic,
     "checks for literal calls to `Default::default()`"
 }
 
 declare_clippy_lint! {
-    /// **What it does:** Checks for immediate reassignment of fields initialized
+    /// ### What it does
+    /// Checks for immediate reassignment of fields initialized
     /// with Default::default().
     ///
-    /// **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).
+    /// ### 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).
     ///
-    /// **Known problems:** Assignments to patterns that are of tuple type are not linted.
+    /// ### Known problems
+    /// Assignments to patterns that are of tuple type are not linted.
     ///
-    /// **Example:**
+    /// ### Example
     /// Bad:
     /// ```
     /// # #[derive(Default)]
@@ -59,6 +63,7 @@
     ///     .. Default::default()
     /// };
     /// ```
+    #[clippy::version = "1.49.0"]
     pub FIELD_REASSIGN_WITH_DEFAULT,
     style,
     "binding initialized with Default should have its fields set in the initializer"
@@ -72,9 +77,10 @@ pub struct Default {
 
 impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
 
-impl LateLintPass<'_> for Default {
+impl<'tcx> LateLintPass<'tcx> for Default {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
+            if !expr.span.from_expansion();
             // Avoid cases already linted by `field_reassign_with_default`
             if !self.reassigned_linted.contains(&expr.span);
             if let ExprKind::Call(path, ..) = expr.kind;
@@ -82,6 +88,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if let ExprKind::Path(ref qpath) = path.kind;
             if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
             if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
+            if !is_update_syntax_base(cx, expr);
             // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
             if let QPath::Resolved(None, _path) = qpath;
             let expr_ty = cx.typeck_results().expr_ty(expr);
@@ -89,7 +96,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             then {
                 // TODO: Work out a way to put "whatever the imported way of referencing
                 // this type in this file" rather than a fully-qualified type.
-                let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
+                let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did()));
                 span_lint_and_sugg(
                     cx,
                     DEFAULT_TRAIT_ACCESS,
@@ -104,7 +111,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     }
 
     #[allow(clippy::too_many_lines)]
-    fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
+    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
         // start from the `let mut _ = _::default();` and look at all the following
         // statements, see if they re-assign the fields of the binding
         let stmts_head = match block.stmts {
@@ -121,7 +128,7 @@ fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
                 if let StmtKind::Local(local) = stmt.kind;
                 if let Some(expr) = local.init;
                 if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
-                if !in_external_macro(cx.tcx.sess, expr.span);
+                if !expr.span.from_expansion();
                 // only take bindings to identifiers
                 if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
                 // only when assigning `... = Default::default()`
@@ -130,12 +137,19 @@ fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
                 if let Some(adt) = binding_type.ty_adt_def();
                 if adt.is_struct();
                 let variant = adt.non_enum_variant();
-                if adt.did.is_local() || !variant.is_field_list_non_exhaustive();
+                if adt.did().is_local() || !variant.is_field_list_non_exhaustive();
                 let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id();
                 if variant
                     .fields
                     .iter()
                     .all(|field| field.vis.is_accessible_from(module_did, cx.tcx));
+                let all_fields_are_copy = variant
+                    .fields
+                    .iter()
+                    .all(|field| {
+                        is_copy(cx, cx.tcx.type_of(field.did))
+                    });
+                if !has_drop(cx, binding_type) || all_fields_are_copy;
                 then {
                     (local, variant, ident.name, binding_type, expr.span)
                 } else {
@@ -185,7 +199,7 @@ fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
                 let ext_with_default = !variant
                     .fields
                     .iter()
-                    .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name));
+                    .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
 
                 let field_list = assigned_fields
                     .into_iter()
@@ -202,7 +216,7 @@ fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
                     if let ty::Adt(adt_def, substs) = binding_type.kind();
                     if !substs.is_empty();
                     then {
-                        let adt_def_ty_name = cx.tcx.item_name(adt_def.did);
+                        let adt_def_ty_name = cx.tcx.item_name(adt_def.did());
                         let generic_args = substs.iter().collect::<Vec<_>>();
                         let tys_str = generic_args
                             .iter()
@@ -277,3 +291,16 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
         }
     }
 }
+
+/// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }`
+fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    if_chain! {
+        if let Some(parent) = get_parent_expr(cx, expr);
+        if let ExprKind::Struct(_, _, Some(base)) = parent.kind;
+        then {
+            base.hir_id == expr.hir_id
+        } else {
+            false
+        }
+    }
+}