@@ -8,7 +8,7 @@ use clippy_utils::{
88 is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core,
99} ;
1010use rustc_errors:: Applicability ;
11- use rustc_hir:: LangItem :: OptionNone ;
11+ use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
1212use rustc_hir:: { Expr , ExprKind } ;
1313use rustc_lint:: { LateContext , LateLintPass } ;
1414use rustc_session:: impl_lint_pass;
@@ -43,6 +43,31 @@ declare_clippy_lint! {
4343 "replacing an `Option` with `None` instead of `take()`"
4444}
4545
46+ declare_clippy_lint ! {
47+ /// ### What it does
48+ /// Checks for `mem::replace()` on an `Option` with `Some(…)`.
49+ ///
50+ /// ### Why is this bad?
51+ /// `Option` already has the method `replace()` for
52+ /// taking its current value (Some(…) or None) and replacing it with
53+ /// `Some(…)`.
54+ ///
55+ /// ### Example
56+ /// ```no_run
57+ /// let mut an_option = Some(0);
58+ /// let replaced = std::mem::replace(&mut an_option, Some(1));
59+ /// ```
60+ /// Is better expressed with:
61+ /// ```no_run
62+ /// let mut an_option = Some(0);
63+ /// let taken = an_option.replace(1);
64+ /// ```
65+ #[ clippy:: version = "1.86.0" ]
66+ pub MEM_REPLACE_OPTION_WITH_SOME ,
67+ style,
68+ "replacing an `Option` with `Some` instead of `replace()`"
69+ }
70+
4671declare_clippy_lint ! {
4772 /// ### What it does
4873 /// Checks for `mem::replace(&mut _, mem::uninitialized())`
@@ -101,7 +126,7 @@ declare_clippy_lint! {
101126}
102127
103128impl_lint_pass ! ( MemReplace =>
104- [ MEM_REPLACE_OPTION_WITH_NONE , MEM_REPLACE_WITH_UNINIT , MEM_REPLACE_WITH_DEFAULT ] ) ;
129+ [ MEM_REPLACE_OPTION_WITH_NONE , MEM_REPLACE_OPTION_WITH_SOME , MEM_REPLACE_WITH_UNINIT , MEM_REPLACE_WITH_DEFAULT ] ) ;
105130
106131fn check_replace_option_with_none ( cx : & LateContext < ' _ > , src : & Expr < ' _ > , dest : & Expr < ' _ > , expr_span : Span ) -> bool {
107132 if is_res_lang_ctor ( cx, path_res ( cx, src) , OptionNone ) {
@@ -130,6 +155,40 @@ fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &E
130155 }
131156}
132157
158+ fn check_replace_option_with_some (
159+ cx : & LateContext < ' _ > ,
160+ src : & Expr < ' _ > ,
161+ dest : & Expr < ' _ > ,
162+ expr_span : Span ,
163+ msrv : & Msrv ,
164+ ) -> bool {
165+ if msrv. meets ( msrvs:: OPTION_REPLACE )
166+ && let ExprKind :: Call ( src_func, [ src_arg] ) = src. kind
167+ && is_res_lang_ctor ( cx, path_res ( cx, src_func) , OptionSome )
168+ {
169+ // We do not have to check for a `const` context here, because `core::mem::replace()` and
170+ // `Option::replace()` have been const-stabilized simultaneously in version 1.83.0.
171+ let sugg_expr = peel_ref_operators ( cx, dest) ;
172+ let mut applicability = Applicability :: MachineApplicable ;
173+ span_lint_and_sugg (
174+ cx,
175+ MEM_REPLACE_OPTION_WITH_SOME ,
176+ expr_span,
177+ "replacing an `Option` with `Some(..)`" ,
178+ "consider `Option::replace()` instead" ,
179+ format ! (
180+ "{}.replace({})" ,
181+ Sugg :: hir_with_context( cx, sugg_expr, expr_span. ctxt( ) , "_" , & mut applicability) . maybe_par( ) ,
182+ snippet_with_applicability( cx, src_arg. span, "_" , & mut applicability)
183+ ) ,
184+ applicability,
185+ ) ;
186+ true
187+ } else {
188+ false
189+ }
190+ }
191+
133192fn check_replace_with_uninit ( cx : & LateContext < ' _ > , src : & Expr < ' _ > , dest : & Expr < ' _ > , expr_span : Span ) {
134193 if let Some ( method_def_id) = cx. typeck_results ( ) . type_dependent_def_id ( src. hir_id )
135194 // check if replacement is mem::MaybeUninit::uninit().assume_init()
@@ -249,6 +308,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
249308 {
250309 // Check that second argument is `Option::None`
251310 if !check_replace_option_with_none ( cx, src, dest, expr. span )
311+ && !check_replace_option_with_some ( cx, src, dest, expr. span , & self . msrv )
252312 && !check_replace_with_default ( cx, src, dest, expr, & self . msrv )
253313 {
254314 check_replace_with_uninit ( cx, src, dest, expr. span ) ;
0 commit comments