|
1 | 1 | use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; |
2 | 2 | use clippy_utils::msrvs::{self, Msrv}; |
3 | 3 | use clippy_utils::source::{snippet, snippet_with_applicability}; |
| 4 | +use clippy_utils::sugg::Sugg; |
4 | 5 | use clippy_utils::ty::is_non_aggregate_primitive_type; |
5 | | -use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res}; |
| 6 | +use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators}; |
6 | 7 | use if_chain::if_chain; |
7 | 8 | use rustc_errors::Applicability; |
8 | 9 | use rustc_hir::LangItem::OptionNone; |
9 | | -use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; |
| 10 | +use rustc_hir::{Expr, ExprKind}; |
10 | 11 | use rustc_lint::{LateContext, LateLintPass}; |
11 | 12 | use rustc_middle::lint::in_external_macro; |
12 | 13 | use rustc_session::{declare_tool_lint, impl_lint_pass}; |
@@ -101,40 +102,26 @@ declare_clippy_lint! { |
101 | 102 | impl_lint_pass!(MemReplace => |
102 | 103 | [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); |
103 | 104 |
|
104 | | -fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { |
105 | | - // Check that second argument is `Option::None` |
106 | | - if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { |
107 | | - // Since this is a late pass (already type-checked), |
108 | | - // and we already know that the second argument is an |
109 | | - // `Option`, we do not need to check the first |
110 | | - // argument's type. All that's left is to get |
111 | | - // replacee's path. |
112 | | - let replaced_path = match dest.kind { |
113 | | - ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => { |
114 | | - if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind { |
115 | | - replaced_path |
116 | | - } else { |
117 | | - return; |
118 | | - } |
119 | | - }, |
120 | | - ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path, |
121 | | - _ => return, |
122 | | - }; |
123 | | - |
124 | | - let mut applicability = Applicability::MachineApplicable; |
125 | | - span_lint_and_sugg( |
126 | | - cx, |
127 | | - MEM_REPLACE_OPTION_WITH_NONE, |
128 | | - expr_span, |
129 | | - "replacing an `Option` with `None`", |
130 | | - "consider `Option::take()` instead", |
131 | | - format!( |
132 | | - "{}.take()", |
133 | | - snippet_with_applicability(cx, replaced_path.span, "", &mut applicability) |
134 | | - ), |
135 | | - applicability, |
136 | | - ); |
137 | | - } |
| 105 | +fn check_replace_option_with_none(cx: &LateContext<'_>, dest: &Expr<'_>, expr_span: Span) { |
| 106 | + // Since this is a late pass (already type-checked), |
| 107 | + // and we already know that the second argument is an |
| 108 | + // `Option`, we do not need to check the first |
| 109 | + // argument's type. All that's left is to get |
| 110 | + // the replacee's expr after peeling off the `&mut` |
| 111 | + let sugg_expr = peel_ref_operators(cx, dest); |
| 112 | + let mut applicability = Applicability::MachineApplicable; |
| 113 | + span_lint_and_sugg( |
| 114 | + cx, |
| 115 | + MEM_REPLACE_OPTION_WITH_NONE, |
| 116 | + expr_span, |
| 117 | + "replacing an `Option` with `None`", |
| 118 | + "consider `Option::take()` instead", |
| 119 | + format!( |
| 120 | + "{}.take()", |
| 121 | + Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_par() |
| 122 | + ), |
| 123 | + applicability, |
| 124 | + ); |
138 | 125 | } |
139 | 126 |
|
140 | 127 | fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { |
@@ -200,10 +187,6 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< |
200 | 187 | if is_non_aggregate_primitive_type(expr_type) { |
201 | 188 | return; |
202 | 189 | } |
203 | | - // disable lint for Option since it is covered in another lint |
204 | | - if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { |
205 | | - return; |
206 | | - } |
207 | 190 | if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) { |
208 | 191 | span_lint_and_then( |
209 | 192 | cx, |
@@ -246,11 +229,13 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { |
246 | 229 | if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); |
247 | 230 | if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id); |
248 | 231 | then { |
249 | | - check_replace_option_with_none(cx, src, dest, expr.span); |
250 | | - check_replace_with_uninit(cx, src, dest, expr.span); |
251 | | - if self.msrv.meets(msrvs::MEM_TAKE) { |
| 232 | + // Check that second argument is `Option::None` |
| 233 | + if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { |
| 234 | + check_replace_option_with_none(cx, dest, expr.span); |
| 235 | + } else if self.msrv.meets(msrvs::MEM_TAKE) { |
252 | 236 | check_replace_with_default(cx, src, dest, expr.span); |
253 | 237 | } |
| 238 | + check_replace_with_uninit(cx, src, dest, expr.span); |
254 | 239 | } |
255 | 240 | } |
256 | 241 | } |
|
0 commit comments