|
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