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