Skip to content

Commit cfddd91

Browse files
committed
[read_line_without_trim]: catch string eq checks
1 parent b09fb8a commit cfddd91

File tree

1 file changed

+62
-31
lines changed

1 file changed

+62
-31
lines changed

clippy_lints/src/methods/read_line_without_trim.rs

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ use clippy_utils::source::snippet;
55
use clippy_utils::ty::is_type_diagnostic_item;
66
use clippy_utils::visitors::for_each_local_use_after_expr;
77
use clippy_utils::{get_parent_expr, match_def_path};
8+
use rustc_ast::LitKind;
89
use rustc_errors::Applicability;
910
use rustc_hir::def::Res;
10-
use rustc_hir::{Expr, ExprKind, QPath};
11+
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
1112
use rustc_lint::LateContext;
1213
use rustc_middle::ty::{self, Ty};
1314
use rustc_span::sym;
@@ -30,37 +31,67 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
3031
// now let's check if the first use of the string passed to `::read_line()` is
3132
// parsed into a type that will always fail if it has a trailing newline.
3233
for_each_local_use_after_expr(cx, local_id, call.hir_id, |expr| {
33-
if let Some(parent) = get_parent_expr(cx, expr)
34-
&& let ExprKind::MethodCall(segment, .., span) = parent.kind
35-
&& segment.ident.name == sym!(parse)
36-
&& let parse_result_ty = cx.typeck_results().expr_ty(parent)
37-
&& is_type_diagnostic_item(cx, parse_result_ty, sym::Result)
38-
&& let ty::Adt(_, args) = parse_result_ty.kind()
39-
&& let Some(ok_ty) = args[0].as_type()
40-
&& parse_fails_on_trailing_newline(ok_ty)
41-
{
42-
let local_snippet = snippet(cx, expr.span, "<expr>");
43-
span_lint_and_then(
44-
cx,
45-
READ_LINE_WITHOUT_TRIM,
46-
span,
47-
"calling `.parse()` without trimming the trailing newline character",
48-
|diag| {
49-
diag.span_note(
50-
call.span,
51-
"call to `.read_line()` here, \
52-
which leaves a trailing newline character in the buffer, \
53-
which in turn will cause `.parse()` to fail",
54-
);
34+
if let Some(parent) = get_parent_expr(cx, expr) {
35+
if let ExprKind::MethodCall(segment, .., span) = parent.kind
36+
&& segment.ident.name == sym!(parse)
37+
&& let parse_result_ty = cx.typeck_results().expr_ty(parent)
38+
&& is_type_diagnostic_item(cx, parse_result_ty, sym::Result)
39+
&& let ty::Adt(_, substs) = parse_result_ty.kind()
40+
&& let Some(ok_ty) = substs[0].as_type()
41+
&& parse_fails_on_trailing_newline(ok_ty)
42+
{
43+
let local_snippet: std::borrow::Cow<'_, str> = snippet(cx, expr.span, "<expr>");
44+
span_lint_and_then(
45+
cx,
46+
READ_LINE_WITHOUT_TRIM,
47+
span,
48+
"calling `.parse()` without trimming the trailing newline character",
49+
|diag| {
50+
diag.span_note(
51+
call.span,
52+
"call to `.read_line()` here, \
53+
which leaves a trailing newline character in the buffer, \
54+
which in turn will cause `.parse()` to fail",
55+
);
5556

56-
diag.span_suggestion(
57-
expr.span,
58-
"try",
59-
format!("{local_snippet}.trim_end()"),
60-
Applicability::MachineApplicable,
61-
);
62-
},
63-
);
57+
diag.span_suggestion(
58+
expr.span,
59+
"try",
60+
format!("{local_snippet}.trim_end()"),
61+
Applicability::MachineApplicable,
62+
);
63+
},
64+
);
65+
} else if let ExprKind::Binary(binop, _, right) = parent.kind
66+
&& let BinOpKind::Eq = binop.node
67+
&& let ExprKind::Lit(lit) = right.kind
68+
&& let LitKind::Str(sym, _) = lit.node
69+
&& !sym.as_str().ends_with('\n')
70+
{
71+
span_lint_and_then(
72+
cx,
73+
READ_LINE_WITHOUT_TRIM,
74+
parent.span,
75+
"comparing a string literal without trimming the trailing newline character",
76+
|diag| {
77+
let local_snippet = snippet(cx, expr.span, "<expr>");
78+
79+
diag.span_note(
80+
call.span,
81+
"call to `.read_line()` here, \
82+
which leaves a trailing newline character in the buffer, \
83+
which in turn will cause the comparison to always fail",
84+
);
85+
86+
diag.span_suggestion(
87+
expr.span,
88+
"try",
89+
format!("{local_snippet}.trim_end()"),
90+
Applicability::MachineApplicable,
91+
);
92+
},
93+
);
94+
}
6495
}
6596

6697
// only consider the first use to prevent this scenario:

0 commit comments

Comments
 (0)