@@ -2,7 +2,7 @@ use crate::utils::{
22 match_def_path, match_qpath, paths, snippet_with_applicability, span_help_and_lint, span_lint_and_sugg,
33} ;
44use if_chain:: if_chain;
5- use rustc:: hir:: { BorrowKind , Expr , ExprKind , Mutability , QPath } ;
5+ use rustc:: hir:: { BorrowKind , Expr , ExprKind , HirVec , Mutability , QPath } ;
66use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
77use rustc:: { declare_lint_pass, declare_tool_lint} ;
88use rustc_errors:: Applicability ;
@@ -66,8 +66,127 @@ declare_clippy_lint! {
6666 "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
6767}
6868
69+ declare_clippy_lint ! {
70+ /// **What it does:** Checks for `std::mem::replace` on a value of type
71+ /// `T` with `T::default()`.
72+ ///
73+ /// **Why is this bad?** `std::mem` module already has the method `take` to
74+ /// take the current value and replace it with the default value of that type.
75+ ///
76+ /// **Known problems:** None.
77+ ///
78+ /// **Example:**
79+ /// ```rust
80+ /// let mut text = String::from("foo");
81+ /// let replaced = std::mem::replace(&mut text, String::default());
82+ /// ```
83+ /// Is better expressed with:
84+ /// ```rust
85+ /// let mut text = String::from("foo");
86+ /// let taken = std::mem::take(&mut text);
87+ /// ```
88+ pub MEM_REPLACE_WITH_DEFAULT ,
89+ nursery,
90+ "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
91+ }
92+
6993declare_lint_pass ! ( MemReplace =>
70- [ MEM_REPLACE_OPTION_WITH_NONE , MEM_REPLACE_WITH_UNINIT ] ) ;
94+ [ MEM_REPLACE_OPTION_WITH_NONE , MEM_REPLACE_WITH_UNINIT , MEM_REPLACE_WITH_DEFAULT ] ) ;
95+
96+ fn check_replace_option_with_none ( cx : & LateContext < ' _ , ' _ > , expr : & ' _ Expr , args : & HirVec < Expr > ) {
97+ if let ExprKind :: Path ( ref replacement_qpath) = args[ 1 ] . kind {
98+ // Check that second argument is `Option::None`
99+ if match_qpath ( replacement_qpath, & paths:: OPTION_NONE ) {
100+ // Since this is a late pass (already type-checked),
101+ // and we already know that the second argument is an
102+ // `Option`, we do not need to check the first
103+ // argument's type. All that's left is to get
104+ // replacee's path.
105+ let replaced_path = match args[ 0 ] . kind {
106+ ExprKind :: AddrOf ( BorrowKind :: Ref , Mutability :: Mutable , ref replaced) => {
107+ if let ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) = replaced. kind {
108+ replaced_path
109+ } else {
110+ return ;
111+ }
112+ } ,
113+ ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) => replaced_path,
114+ _ => return ,
115+ } ;
116+
117+ let mut applicability = Applicability :: MachineApplicable ;
118+ span_lint_and_sugg (
119+ cx,
120+ MEM_REPLACE_OPTION_WITH_NONE ,
121+ expr. span ,
122+ "replacing an `Option` with `None`" ,
123+ "consider `Option::take()` instead" ,
124+ format ! (
125+ "{}.take()" ,
126+ snippet_with_applicability( cx, replaced_path. span, "" , & mut applicability)
127+ ) ,
128+ applicability,
129+ ) ;
130+ }
131+ }
132+ }
133+
134+ fn check_replace_with_uninit ( cx : & LateContext < ' _ , ' _ > , expr : & ' _ Expr , args : & HirVec < Expr > ) {
135+ if let ExprKind :: Call ( ref repl_func, ref repl_args) = args[ 1 ] . kind {
136+ if_chain ! {
137+ if repl_args. is_empty( ) ;
138+ if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind;
139+ if let Some ( repl_def_id) = cx. tables. qpath_res( repl_func_qpath, repl_func. hir_id) . opt_def_id( ) ;
140+ then {
141+ if match_def_path( cx, repl_def_id, & paths:: MEM_UNINITIALIZED ) {
142+ span_help_and_lint(
143+ cx,
144+ MEM_REPLACE_WITH_UNINIT ,
145+ expr. span,
146+ "replacing with `mem::uninitialized()`" ,
147+ "consider using the `take_mut` crate instead" ,
148+ ) ;
149+ } else if match_def_path( cx, repl_def_id, & paths:: MEM_ZEROED ) &&
150+ !cx. tables. expr_ty( & args[ 1 ] ) . is_primitive( ) {
151+ span_help_and_lint(
152+ cx,
153+ MEM_REPLACE_WITH_UNINIT ,
154+ expr. span,
155+ "replacing with `mem::zeroed()`" ,
156+ "consider using a default value or the `take_mut` crate instead" ,
157+ ) ;
158+ }
159+ }
160+ }
161+ }
162+ }
163+
164+ fn check_replace_with_default ( cx : & LateContext < ' _ , ' _ > , expr : & ' _ Expr , args : & HirVec < Expr > ) {
165+ if let ExprKind :: Call ( ref repl_func, ref repl_args) = args[ 1 ] . kind {
166+ if_chain ! {
167+ if repl_args. is_empty( ) ;
168+ if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind;
169+ if let Some ( repl_def_id) = cx. tables. qpath_res( repl_func_qpath, repl_func. hir_id) . opt_def_id( ) ;
170+ if match_def_path( cx, repl_def_id, & paths:: DEFAULT_TRAIT_METHOD ) ;
171+ then {
172+ let mut applicability = Applicability :: MachineApplicable ;
173+
174+ span_lint_and_sugg(
175+ cx,
176+ MEM_REPLACE_WITH_DEFAULT ,
177+ expr. span,
178+ "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`" ,
179+ "consider using" ,
180+ format!(
181+ "std::mem::take({})" ,
182+ snippet_with_applicability( cx, args[ 0 ] . span, "" , & mut applicability)
183+ ) ,
184+ applicability,
185+ ) ;
186+ }
187+ }
188+ }
189+ }
71190
72191impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for MemReplace {
73192 fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
@@ -79,67 +198,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
79198 if let Some ( def_id) = cx. tables. qpath_res( func_qpath, func. hir_id) . opt_def_id( ) ;
80199 if match_def_path( cx, def_id, & paths:: MEM_REPLACE ) ;
81200
82- // Check that second argument is `Option::None`
83201 then {
84- if let ExprKind :: Path ( ref replacement_qpath) = func_args[ 1 ] . kind {
85- if match_qpath( replacement_qpath, & paths:: OPTION_NONE ) {
86-
87- // Since this is a late pass (already type-checked),
88- // and we already know that the second argument is an
89- // `Option`, we do not need to check the first
90- // argument's type. All that's left is to get
91- // replacee's path.
92- let replaced_path = match func_args[ 0 ] . kind {
93- ExprKind :: AddrOf ( BorrowKind :: Ref , Mutability :: Mutable , ref replaced) => {
94- if let ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) = replaced. kind {
95- replaced_path
96- } else {
97- return
98- }
99- } ,
100- ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) => replaced_path,
101- _ => return ,
102- } ;
103-
104- let mut applicability = Applicability :: MachineApplicable ;
105- span_lint_and_sugg(
106- cx,
107- MEM_REPLACE_OPTION_WITH_NONE ,
108- expr. span,
109- "replacing an `Option` with `None`" ,
110- "consider `Option::take()` instead" ,
111- format!( "{}.take()" , snippet_with_applicability( cx, replaced_path. span, "" , & mut applicability) ) ,
112- applicability,
113- ) ;
114- }
115- }
116- if let ExprKind :: Call ( ref repl_func, ref repl_args) = func_args[ 1 ] . kind {
117- if_chain! {
118- if repl_args. is_empty( ) ;
119- if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind;
120- if let Some ( repl_def_id) = cx. tables. qpath_res( repl_func_qpath, repl_func. hir_id) . opt_def_id( ) ;
121- then {
122- if match_def_path( cx, repl_def_id, & paths:: MEM_UNINITIALIZED ) {
123- span_help_and_lint(
124- cx,
125- MEM_REPLACE_WITH_UNINIT ,
126- expr. span,
127- "replacing with `mem::uninitialized()`" ,
128- "consider using the `take_mut` crate instead" ,
129- ) ;
130- } else if match_def_path( cx, repl_def_id, & paths:: MEM_ZEROED ) &&
131- !cx. tables. expr_ty( & func_args[ 1 ] ) . is_primitive( ) {
132- span_help_and_lint(
133- cx,
134- MEM_REPLACE_WITH_UNINIT ,
135- expr. span,
136- "replacing with `mem::zeroed()`" ,
137- "consider using a default value or the `take_mut` crate instead" ,
138- ) ;
139- }
140- }
141- }
142- }
202+ check_replace_option_with_none( cx, expr, & func_args) ;
203+ check_replace_with_uninit( cx, expr, & func_args) ;
204+ check_replace_with_default( cx, expr, & func_args) ;
143205 }
144206 }
145207 }
0 commit comments