|
1 |
| -use crate::methods::method_call; |
2 | 1 | use clippy_utils::diagnostics::span_lint_and_sugg;
|
3 |
| -use clippy_utils::{peel_blocks, sym}; |
| 2 | +use clippy_utils::source::SpanRangeExt; |
| 3 | +use clippy_utils::ty::is_type_diagnostic_item; |
| 4 | +use clippy_utils::{peel_blocks, peel_hir_expr_while, sym}; |
4 | 5 | use rustc_ast::LitKind;
|
5 | 6 | use rustc_errors::Applicability;
|
6 | 7 | use rustc_hir::{Expr, ExprKind};
|
7 | 8 | use rustc_lint::{LateContext, LateLintPass};
|
8 |
| -use rustc_middle::ty; |
9 | 9 | use rustc_session::declare_lint_pass;
|
10 |
| -use rustc_span::{BytePos, Span}; |
11 | 10 |
|
12 | 11 | declare_clippy_lint! {
|
13 | 12 | /// ### What it does
|
@@ -43,53 +42,58 @@ declare_clippy_lint! {
|
43 | 42 |
|
44 | 43 | declare_lint_pass!(IneffectiveOpenOptions => [INEFFECTIVE_OPEN_OPTIONS]);
|
45 | 44 |
|
46 |
| -fn index_if_arg_is_boolean(args: &[Expr<'_>], call_span: Span) -> Option<Span> { |
47 |
| - if let [arg] = args |
48 |
| - && let ExprKind::Lit(lit) = peel_blocks(arg).kind |
49 |
| - && lit.node == LitKind::Bool(true) |
50 |
| - { |
51 |
| - // The `.` is not included in the span so we cheat a little bit to include it as well. |
52 |
| - Some(call_span.with_lo(call_span.lo() - BytePos(1))) |
53 |
| - } else { |
54 |
| - None |
55 |
| - } |
56 |
| -} |
57 |
| - |
58 | 45 | impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions {
|
59 | 46 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
60 |
| - let Some((sym::open, mut receiver, [_arg], _, _)) = method_call(expr) else { |
61 |
| - return; |
62 |
| - }; |
63 |
| - let receiver_ty = cx.typeck_results().expr_ty(receiver); |
64 |
| - match receiver_ty.peel_refs().kind() { |
65 |
| - ty::Adt(adt, _) if cx.tcx.is_diagnostic_item(sym::FsOpenOptions, adt.did()) => {}, |
66 |
| - _ => return, |
67 |
| - } |
68 |
| - |
69 |
| - let mut append = None; |
70 |
| - let mut write = None; |
| 47 | + if let ExprKind::MethodCall(name, recv, [_], _) = expr.kind |
| 48 | + && name.ident.name == sym::open |
| 49 | + && !expr.span.from_expansion() |
| 50 | + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::FsOpenOptions) |
| 51 | + { |
| 52 | + let mut append = false; |
| 53 | + let mut write = None; |
| 54 | + peel_hir_expr_while(recv, |e| { |
| 55 | + if let ExprKind::MethodCall(name, recv, args, call_span) = e.kind |
| 56 | + && !e.span.from_expansion() |
| 57 | + { |
| 58 | + if let [arg] = args |
| 59 | + && let ExprKind::Lit(lit) = peel_blocks(arg).kind |
| 60 | + && matches!(lit.node, LitKind::Bool(true)) |
| 61 | + && !arg.span.from_expansion() |
| 62 | + && !lit.span.from_expansion() |
| 63 | + { |
| 64 | + match name.ident.name { |
| 65 | + sym::append => append = true, |
| 66 | + sym::write |
| 67 | + if let Some(range) = call_span.map_range(cx, |_, text, range| { |
| 68 | + if text.get(..range.start)?.ends_with('.') { |
| 69 | + Some(range.start - 1..range.end) |
| 70 | + } else { |
| 71 | + None |
| 72 | + } |
| 73 | + }) => |
| 74 | + { |
| 75 | + write = Some(call_span.with_lo(range.start)); |
| 76 | + }, |
| 77 | + _ => {}, |
| 78 | + } |
| 79 | + } |
| 80 | + Some(recv) |
| 81 | + } else { |
| 82 | + None |
| 83 | + } |
| 84 | + }); |
71 | 85 |
|
72 |
| - while let Some((name, recv, args, _, span)) = method_call(receiver) { |
73 |
| - if name == sym::append { |
74 |
| - append = index_if_arg_is_boolean(args, span); |
75 |
| - } else if name == sym::write { |
76 |
| - write = index_if_arg_is_boolean(args, span); |
| 86 | + if append && let Some(write_span) = write { |
| 87 | + span_lint_and_sugg( |
| 88 | + cx, |
| 89 | + INEFFECTIVE_OPEN_OPTIONS, |
| 90 | + write_span, |
| 91 | + "unnecessary use of `.write(true)` because there is `.append(true)`", |
| 92 | + "remove `.write(true)`", |
| 93 | + String::new(), |
| 94 | + Applicability::MachineApplicable, |
| 95 | + ); |
77 | 96 | }
|
78 |
| - receiver = recv; |
79 |
| - } |
80 |
| - |
81 |
| - if let Some(write_span) = write |
82 |
| - && append.is_some() |
83 |
| - { |
84 |
| - span_lint_and_sugg( |
85 |
| - cx, |
86 |
| - INEFFECTIVE_OPEN_OPTIONS, |
87 |
| - write_span, |
88 |
| - "unnecessary use of `.write(true)` because there is `.append(true)`", |
89 |
| - "remove `.write(true)`", |
90 |
| - String::new(), |
91 |
| - Applicability::MachineApplicable, |
92 |
| - ); |
93 | 97 | }
|
94 | 98 | }
|
95 | 99 | }
|
0 commit comments