about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/methods/read_line_without_trim.rs93
1 files changed, 62 insertions, 31 deletions
diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs
index 3b903ec5cdb..f4b4f73af56 100644
--- a/clippy_lints/src/methods/read_line_without_trim.rs
+++ b/clippy_lints/src/methods/read_line_without_trim.rs
@@ -5,9 +5,10 @@ use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::for_each_local_use_after_expr;
 use clippy_utils::{get_parent_expr, match_def_path};
+use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::{Expr, ExprKind, QPath};
+use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::sym;
@@ -30,37 +31,67 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
         // now let's check if the first use of the string passed to `::read_line()` is
         // parsed into a type that will always fail if it has a trailing newline.
         for_each_local_use_after_expr(cx, local_id, call.hir_id, |expr| {
-            if let Some(parent) = get_parent_expr(cx, expr)
-                && let ExprKind::MethodCall(segment, .., span) = parent.kind
-                && segment.ident.name == sym!(parse)
-                && let parse_result_ty = cx.typeck_results().expr_ty(parent)
-                && is_type_diagnostic_item(cx, parse_result_ty, sym::Result)
-                && let ty::Adt(_, args) = parse_result_ty.kind()
-                && let Some(ok_ty) = args[0].as_type()
-                && parse_fails_on_trailing_newline(ok_ty)
-            {
-                let local_snippet = snippet(cx, expr.span, "<expr>");
-                span_lint_and_then(
-                    cx,
-                    READ_LINE_WITHOUT_TRIM,
-                    span,
-                    "calling `.parse()` without trimming the trailing newline character",
-                    |diag| {
-                        diag.span_note(
-                            call.span,
-                            "call to `.read_line()` here, \
-                            which leaves a trailing newline character in the buffer, \
-                            which in turn will cause `.parse()` to fail",
-                        );
+            if let Some(parent) = get_parent_expr(cx, expr) {
+                if let ExprKind::MethodCall(segment, .., span) = parent.kind
+                    && segment.ident.name == sym!(parse)
+                    && let parse_result_ty = cx.typeck_results().expr_ty(parent)
+                    && is_type_diagnostic_item(cx, parse_result_ty, sym::Result)
+                    && let ty::Adt(_, substs) = parse_result_ty.kind()
+                    && let Some(ok_ty) = substs[0].as_type()
+                    && parse_fails_on_trailing_newline(ok_ty)
+                {
+                    let local_snippet: std::borrow::Cow<'_, str> = snippet(cx, expr.span, "<expr>");
+                    span_lint_and_then(
+                        cx,
+                        READ_LINE_WITHOUT_TRIM,
+                        span,
+                        "calling `.parse()` without trimming the trailing newline character",
+                        |diag| {
+                            diag.span_note(
+                                call.span,
+                                "call to `.read_line()` here, \
+                                which leaves a trailing newline character in the buffer, \
+                                which in turn will cause `.parse()` to fail",
+                            );
 
-                        diag.span_suggestion(
-                            expr.span,
-                            "try",
-                            format!("{local_snippet}.trim_end()"),
-                            Applicability::MachineApplicable,
-                        );
-                    },
-                );
+                            diag.span_suggestion(
+                                expr.span,
+                                "try",
+                                format!("{local_snippet}.trim_end()"),
+                                Applicability::MachineApplicable,
+                            );
+                        },
+                    );
+                } else if let ExprKind::Binary(binop, _, right) = parent.kind
+                    && let BinOpKind::Eq = binop.node
+                    && let ExprKind::Lit(lit) = right.kind
+                    && let LitKind::Str(sym, _) = lit.node
+                    && !sym.as_str().ends_with('\n')
+                {
+                    span_lint_and_then(
+                        cx,
+                        READ_LINE_WITHOUT_TRIM,
+                        parent.span,
+                        "comparing a string literal without trimming the trailing newline character",
+                        |diag| {
+                            let local_snippet = snippet(cx, expr.span, "<expr>");
+
+                            diag.span_note(
+                                call.span,
+                                "call to `.read_line()` here, \
+                                which leaves a trailing newline character in the buffer, \
+                                which in turn will cause the comparison to always fail",
+                            );
+
+                            diag.span_suggestion(
+                                expr.span,
+                                "try",
+                                format!("{local_snippet}.trim_end()"),
+                                Applicability::MachineApplicable,
+                            );
+                        },
+                    );
+                }
             }
 
             // only consider the first use to prevent this scenario: