@@ -4,7 +4,9 @@ use clippy_utils::{
4
4
diagnostics:: span_lint_and_then, get_parent_expr, match_def_path, source:: snippet, ty:: is_type_diagnostic_item,
5
5
visitors:: for_each_local_use_after_expr,
6
6
} ;
7
+ use rustc_ast:: LitKind ;
7
8
use rustc_errors:: Applicability ;
9
+ use rustc_hir:: BinOpKind ;
8
10
use rustc_hir:: Expr ;
9
11
use rustc_hir:: QPath ;
10
12
use rustc_hir:: { def:: Res , ExprKind } ;
@@ -31,33 +33,61 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
31
33
// parsed into a type that will always fail if it has a trailing newline.
32
34
for_each_local_use_after_expr ( cx, local_id, call. hir_id , |expr| {
33
35
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 ( _, substs) = parse_result_ty. kind ( )
39
- && let Some ( ok_ty) = substs[ 0 ] . as_type ( )
40
- && parse_fails_on_trailing_newline ( ok_ty)
41
36
{
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 ( call. span , "call to `.read_line()` here, \
50
- which leaves a trailing newline character in the buffer, \
51
- which in turn will cause `.parse()` to fail") ;
37
+ if let ExprKind :: MethodCall ( segment, .., span) = parent. kind
38
+ && segment. ident . name == sym ! ( parse)
39
+ && let parse_result_ty = cx. typeck_results ( ) . expr_ty ( parent)
40
+ && is_type_diagnostic_item ( cx, parse_result_ty, sym:: Result )
41
+ && let ty:: Adt ( _, substs) = parse_result_ty. kind ( )
42
+ && let Some ( ok_ty) = substs[ 0 ] . as_type ( )
43
+ && parse_fails_on_trailing_newline ( ok_ty)
44
+ {
45
+ let local_snippet: std:: borrow:: Cow < ' _ , str > = snippet ( cx, expr. span , "<expr>" ) ;
46
+ span_lint_and_then (
47
+ cx,
48
+ READ_LINE_WITHOUT_TRIM ,
49
+ span,
50
+ "calling `.parse()` without trimming the trailing newline character" ,
51
+ |diag| {
52
+ diag. span_note ( call. span , "call to `.read_line()` here, \
53
+ which leaves a trailing newline character in the buffer, \
54
+ which in turn will cause `.parse()` to fail") ;
52
55
53
- diag. span_suggestion (
54
- expr. span ,
55
- "try" ,
56
- format ! ( "{local_snippet}.trim_end()" ) ,
57
- Applicability :: MachineApplicable ,
58
- ) ;
59
- }
60
- ) ;
56
+ diag. span_suggestion (
57
+ expr. span ,
58
+ "try" ,
59
+ format ! ( "{local_snippet}.trim_end()" ) ,
60
+ Applicability :: MachineApplicable ,
61
+ ) ;
62
+ }
63
+ ) ;
64
+ } else if let ExprKind :: Binary ( binop, left, right) = parent. kind
65
+ && let BinOpKind :: Eq = binop. node
66
+ && let ExprKind :: Lit ( lit) = right. kind
67
+ && let LitKind :: Str ( sym, _) = lit. node
68
+ && !sym. as_str ( ) . ends_with ( '\n' )
69
+ {
70
+ span_lint_and_then (
71
+ cx,
72
+ READ_LINE_WITHOUT_TRIM ,
73
+ parent. span ,
74
+ "comparing a string literal without trimming the trailing newline character" ,
75
+ |diag| {
76
+ let local_snippet: std:: borrow:: Cow < ' _ , str > = snippet ( cx, expr. span , "<expr>" ) ;
77
+
78
+ diag. span_note ( call. span , "call to `.read_line()` here, \
79
+ which leaves a trailing newline character in the buffer, \
80
+ which in turn will cause the comparison to always fail") ;
81
+
82
+ diag. span_suggestion (
83
+ expr. span ,
84
+ "try" ,
85
+ format ! ( "{local_snippet}.trim_end()" ) ,
86
+ Applicability :: MachineApplicable ,
87
+ ) ;
88
+ }
89
+ ) ;
90
+ }
61
91
}
62
92
63
93
// only consider the first use to prevent this scenario:
0 commit comments