]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/needless_continue.rs
Fix lint registration
[rust.git] / clippy_lints / src / needless_continue.rs
index 45e5fdd5ce82f92533cd022fd82d35bf651df9cd..98a3bce1ff38a1e9d107b54d8f725af240156c9a 100644 (file)
 //! ```
 //!
 //! This lint is **warn** by default.
-use rustc_lint::{EarlyContext, EarlyLintPass};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::{indent_of, snippet, snippet_block};
+use rustc_ast::ast;
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::{original_sp, DUMMY_SP};
 use rustc_span::Span;
-use syntax::ast;
-
-use crate::utils::{indent_of, snippet, snippet_block, span_lint_and_help};
 
 declare_clippy_lint! {
-    /// **What it does:** The lint checks for `if`-statements appearing in loops
+    /// ### What it does
+    /// The lint checks for `if`-statements appearing in loops
     /// that contain a `continue` statement in either their main blocks or their
     /// `else`-blocks, when omitting the `else`-block possibly with some
     /// rearrangement of code can make the code easier to understand.
     ///
-    /// **Why is this bad?** Having explicit `else` blocks for `if` statements
+    /// ### Why is this bad?
+    /// Having explicit `else` blocks for `if` statements
     /// containing `continue` in their THEN branch adds unnecessary branching and
     /// nesting to the code. Having an else block containing just `continue` can
     /// also be better written by grouping the statements following the whole `if`
     /// statement within the THEN block and omitting the else block completely.
     ///
-    /// **Known problems:** None
-    ///
-    /// **Example:**
+    /// ### Example
     /// ```rust
     /// # fn condition() -> bool { false }
     /// # fn update_condition() {}
     ///     # break;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_CONTINUE,
     pedantic,
     "`continue` statements that can be replaced by a rearrangement of code"
 declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]);
 
 impl EarlyLintPass for NeedlessContinue {
-    fn check_expr(&mut self, ctx: &EarlyContext<'_>, expr: &ast::Expr) {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
         if !expr.span.from_expansion() {
-            check_and_warn(ctx, expr);
+            check_and_warn(cx, expr);
         }
     }
 }
@@ -221,7 +221,7 @@ fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
 {
     if let ast::ExprKind::While(_, loop_block, label)
     | ast::ExprKind::ForLoop(_, _, loop_block, label)
-    | ast::ExprKind::Loop(loop_block, label) = &expr.kind
+    | ast::ExprKind::Loop(loop_block, label, ..) = &expr.kind
     {
         func(loop_block, label.as_ref());
     }
@@ -270,9 +270,11 @@ struct LintData<'a> {
     /// The 0-based index of the `if` statement in the containing loop block.
     stmt_idx: usize,
     /// The statements of the loop block.
-    block_stmts: &'a [ast::Stmt],
+    loop_block: &'a ast::Block,
 }
 
+const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant";
+
 const MSG_REDUNDANT_ELSE_BLOCK: &str = "this `else` block is redundant";
 
 const MSG_ELSE_BLOCK_NOT_NEEDED: &str = "there is no need for an explicit `else` block for this `if` \
@@ -283,39 +285,42 @@ struct LintData<'a> {
 
 const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause";
 
-fn emit_warning<'a>(ctx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) {
+const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression";
+
+fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) {
     // snip    is the whole *help* message that appears after the warning.
     // message is the warning message.
     // expr    is the expression which the lint warning message refers to.
     let (snip, message, expr) = match typ {
         LintType::ContinueInsideElseBlock => (
-            suggestion_snippet_for_continue_inside_else(ctx, data),
+            suggestion_snippet_for_continue_inside_else(cx, data),
             MSG_REDUNDANT_ELSE_BLOCK,
             data.else_expr,
         ),
         LintType::ContinueInsideThenBlock => (
-            suggestion_snippet_for_continue_inside_if(ctx, data),
+            suggestion_snippet_for_continue_inside_if(cx, data),
             MSG_ELSE_BLOCK_NOT_NEEDED,
             data.if_expr,
         ),
     };
     span_lint_and_help(
-        ctx,
+        cx,
         NEEDLESS_CONTINUE,
         expr.span,
         message,
+        None,
         &format!("{}\n{}", header, snip),
     );
 }
 
-fn suggestion_snippet_for_continue_inside_if<'a>(ctx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
-    let cond_code = snippet(ctx, data.if_cond.span, "..");
+fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
+    let cond_code = snippet(cx, data.if_cond.span, "..");
 
-    let continue_code = snippet_block(ctx, data.if_block.span, "..", Some(data.if_expr.span));
+    let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span));
 
-    let else_code = snippet_block(ctx, data.else_expr.span, "..", Some(data.if_expr.span));
+    let else_code = snippet_block(cx, data.else_expr.span, "..", Some(data.if_expr.span));
 
-    let indent_if = indent_of(ctx, data.if_expr.span).unwrap_or(0);
+    let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
     format!(
         "{indent}if {} {}\n{indent}{}",
         cond_code,
@@ -325,24 +330,24 @@ fn suggestion_snippet_for_continue_inside_if<'a>(ctx: &EarlyContext<'_>, data: &
     )
 }
 
-fn suggestion_snippet_for_continue_inside_else<'a>(ctx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
-    let cond_code = snippet(ctx, data.if_cond.span, "..");
+fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
+    let cond_code = snippet(cx, data.if_cond.span, "..");
 
     // Region B
-    let block_code = erode_from_back(&snippet_block(ctx, data.if_block.span, "..", Some(data.if_expr.span)));
+    let block_code = erode_from_back(&snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)));
 
     // Region C
     // These is the code in the loop block that follows the if/else construction
     // we are complaining about. We want to pull all of this code into the
     // `then` block of the `if` statement.
     let indent = span_of_first_expr_in_block(data.if_block)
-        .and_then(|span| indent_of(ctx, span))
+        .and_then(|span| indent_of(cx, span))
         .unwrap_or(0);
-    let to_annex = data.block_stmts[data.stmt_idx + 1..]
+    let to_annex = data.loop_block.stmts[data.stmt_idx + 1..]
         .iter()
-        .map(|stmt| original_sp(stmt.span, DUMMY_SP))
-        .map(|span| {
-            let snip = snippet_block(ctx, span, "..", None).into_owned();
+        .map(|stmt| {
+            let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span);
+            let snip = snippet_block(cx, span, "..", None).into_owned();
             snip.lines()
                 .map(|line| format!("{}{}", " ".repeat(indent), line))
                 .collect::<Vec<_>>()
@@ -351,7 +356,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(ctx: &EarlyContext<'_>, data:
         .collect::<Vec<_>>()
         .join("\n");
 
-    let indent_if = indent_of(ctx, data.if_expr.span).unwrap_or(0);
+    let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
     format!(
         "{indent_if}if {} {}\n{indent}// merged code follows:\n{}\n{indent_if}}}",
         cond_code,
@@ -362,7 +367,23 @@ fn suggestion_snippet_for_continue_inside_else<'a>(ctx: &EarlyContext<'_>, data:
     )
 }
 
-fn check_and_warn<'a>(ctx: &EarlyContext<'_>, expr: &'a ast::Expr) {
+fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) {
+    if_chain! {
+        if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind;
+        if let Some(last_stmt) = loop_block.stmts.last();
+        if let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind;
+        if let ast::ExprKind::Continue(_) = inner_expr.kind;
+        then {
+            span_lint_and_help(
+                cx,
+                NEEDLESS_CONTINUE,
+                last_stmt.span,
+                MSG_REDUNDANT_CONTINUE_EXPRESSION,
+                None,
+                DROP_CONTINUE_EXPRESSION_MSG,
+            );
+        }
+    }
     with_loop_block(expr, |loop_block, label| {
         for (i, stmt) in loop_block.stmts.iter().enumerate() {
             with_if_expr(stmt, |if_expr, cond, then_block, else_expr| {
@@ -372,17 +393,17 @@ fn check_and_warn<'a>(ctx: &EarlyContext<'_>, expr: &'a ast::Expr) {
                     if_cond: cond,
                     if_block: then_block,
                     else_expr,
-                    block_stmts: &loop_block.stmts,
+                    loop_block,
                 };
                 if needless_continue_in_else(else_expr, label) {
                     emit_warning(
-                        ctx,
+                        cx,
                         data,
                         DROP_ELSE_BLOCK_AND_MERGE_MSG,
                         LintType::ContinueInsideElseBlock,
                     );
                 } else if is_first_block_stmt_continue(then_block, label) {
-                    emit_warning(ctx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock);
+                    emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock);
                 }
             });
         }
@@ -401,7 +422,7 @@ fn check_and_warn<'a>(ctx: &EarlyContext<'_>, expr: &'a ast::Expr) {
 ///
 /// is transformed to
 ///
-/// ```ignore
+/// ```text
 ///     {
 ///         let x = 5;
 /// ```
@@ -415,15 +436,11 @@ fn erode_from_back(s: &str) -> String {
             break;
         }
     }
-    if ret.is_empty() {
-        s.to_string()
-    } else {
-        ret
-    }
+    if ret.is_empty() { s.to_string() } else { ret }
 }
 
 fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> {
-    block.stmts.iter().next().map(|stmt| stmt.span)
+    block.stmts.get(0).map(|stmt| stmt.span)
 }
 
 #[cfg(test)]