]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/needless_late_init.rs
Auto merge of #9148 - arieluy:then_some_unwrap_or, r=Jarcho
[rust.git] / clippy_lints / src / needless_late_init.rs
index 094a3f111ba5ae5d9f4b17415166ce4e7b92c09c..ff2999b1f4a51da2137295baa6ad007c06348322 100644 (file)
@@ -1,10 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::path_to_local;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::visitors::{expr_visitor, is_local_used};
-use rustc_errors::Applicability;
+use clippy_utils::ty::needs_ordered_drop;
+use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used};
+use rustc_errors::{Applicability, MultiSpan};
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind};
+use rustc_hir::{
+    BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
+    StmtKind,
+};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::Span;
@@ -52,7 +56,7 @@
     ///     -1
     /// };
     /// ```
-    #[clippy::version = "1.58.0"]
+    #[clippy::version = "1.59.0"]
     pub NEEDLESS_LATE_INIT,
     style,
     "late initializations that can be replaced by a `let` statement with an initializer"
@@ -73,6 +77,31 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) ->
     seen
 }
 
+fn contains_let(cond: &Expr<'_>) -> bool {
+    let mut seen = false;
+    expr_visitor_no_bodies(|expr| {
+        if let ExprKind::Let(_) = expr.kind {
+            seen = true;
+        }
+
+        !seen
+    })
+    .visit_expr(cond);
+
+    seen
+}
+
+fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
+    let StmtKind::Local(local) = stmt.kind else { return false };
+    !local.pat.walk_short(|pat| {
+        if let PatKind::Binding(.., None) = pat.kind {
+            !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat))
+        } else {
+            true
+        }
+    })
+}
+
 #[derive(Debug)]
 struct LocalAssign {
     lhs_id: HirId,
@@ -156,23 +185,24 @@ fn assignment_suggestions<'tcx>(
 
     let suggestions = assignments
         .iter()
-        .map(|assignment| Some((assignment.span.until(assignment.rhs_span), String::new())))
-        .chain(assignments.iter().map(|assignment| {
-            Some((
+        .flat_map(|assignment| {
+            [
+                assignment.span.until(assignment.rhs_span),
                 assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()),
-                String::new(),
-            ))
-        }))
-        .collect::<Option<Vec<(Span, String)>>>()?;
-
-    let applicability = if suggestions.len() > 1 {
+            ]
+        })
+        .map(|span| (span, String::new()))
+        .collect::<Vec<(Span, String)>>();
+
+    match suggestions.len() {
+        // All of `exprs` are never types
+        // https://github.com/rust-lang/rust-clippy/issues/8911
+        0 => None,
+        1 => Some((Applicability::MachineApplicable, suggestions)),
         // multiple suggestions don't work with rustfix in multipart_suggest
         // https://github.com/rust-lang/rustfix/issues/141
-        Applicability::Unspecified
-    } else {
-        Applicability::MachineApplicable
-    };
-    Some((applicability, suggestions))
+        _ => Some((Applicability::Unspecified, suggestions)),
+    }
 }
 
 struct Usage<'tcx> {
@@ -187,11 +217,14 @@ fn first_usage<'tcx>(
     local_stmt_id: HirId,
     block: &'tcx Block<'tcx>,
 ) -> Option<Usage<'tcx>> {
+    let significant_drop = needs_ordered_drop(cx, cx.typeck_results().node_type(binding_id));
+
     block
         .stmts
         .iter()
         .skip_while(|stmt| stmt.hir_id != local_stmt_id)
         .skip(1)
+        .take_while(|stmt| !significant_drop || !stmt_needs_ordered_drop(cx, stmt))
         .find(|&stmt| is_local_used(cx, stmt, binding_id))
         .and_then(|stmt| match stmt.kind {
             StmtKind::Expr(expr) => Some(Usage {
@@ -235,17 +268,20 @@ fn check<'tcx>(
     match usage.expr.kind {
         ExprKind::Assign(..) => {
             let assign = LocalAssign::new(cx, usage.expr, binding_id)?;
+            let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]);
+            msg_span.push_span_label(local_stmt.span, "created here");
+            msg_span.push_span_label(assign.span, "initialised here");
 
             span_lint_and_then(
                 cx,
                 NEEDLESS_LATE_INIT,
-                local_stmt.span,
-                "unneeded late initalization",
+                msg_span,
+                "unneeded late initialization",
                 |diag| {
                     diag.tool_only_span_suggestion(
                         local_stmt.span,
                         "remove the local",
-                        String::new(),
+                        "",
                         Applicability::MachineApplicable,
                     );
 
@@ -258,14 +294,14 @@ fn check<'tcx>(
                 },
             );
         },
-        ExprKind::If(_, then_expr, Some(else_expr)) => {
+        ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => {
             let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
 
             span_lint_and_then(
                 cx,
                 NEEDLESS_LATE_INIT,
                 local_stmt.span,
-                "unneeded late initalization",
+                "unneeded late initialization",
                 |diag| {
                     diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
 
@@ -282,7 +318,7 @@ fn check<'tcx>(
                         diag.span_suggestion(
                             usage.stmt.span.shrink_to_hi(),
                             "add a semicolon after the `if` expression",
-                            ";".to_string(),
+                            ";",
                             applicability,
                         );
                     }
@@ -296,7 +332,7 @@ fn check<'tcx>(
                 cx,
                 NEEDLESS_LATE_INIT,
                 local_stmt.span,
-                "unneeded late initalization",
+                "unneeded late initialization",
                 |diag| {
                     diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
 
@@ -317,7 +353,7 @@ fn check<'tcx>(
                         diag.span_suggestion(
                             usage.stmt.span.shrink_to_hi(),
                             "add a semicolon after the `match` expression",
-                            ";".to_string(),
+                            ";",
                             applicability,
                         );
                     }
@@ -330,15 +366,14 @@ fn check<'tcx>(
     Some(())
 }
 
-impl LateLintPass<'tcx> for NeedlessLateInit {
+impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
         let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
-
         if_chain! {
             if let Local {
                 init: None,
                 pat: &Pat {
-                    kind: PatKind::Binding(_, binding_id, _, None),
+                    kind: PatKind::Binding(BindingAnnotation::Unannotated, binding_id, _, None),
                     ..
                 },
                 source: LocalSource::Normal,