@@ -14,8 +14,8 @@ use if_chain::if_chain;
1414use crate :: syntax:: ast;
1515use crate :: syntax:: source_map:: Span ;
1616use crate :: syntax:: visit:: FnKind ;
17+ use crate :: syntax_pos:: BytePos ;
1718use crate :: rustc_errors:: Applicability ;
18-
1919use crate :: utils:: { in_macro, match_path_ast, snippet_opt, span_lint_and_then, span_note_and_lint} ;
2020
2121/// **What it does:** Checks for return statements at the end of a block.
@@ -68,6 +68,25 @@ declare_clippy_lint! {
6868 the end of a block"
6969}
7070
71+ /// **What it does:** Checks for unit (`()`) expressions that can be removed.
72+ ///
73+ /// **Why is this bad?** Such expressions add no value, but can make the code
74+ /// less readable. Depending on formatting they can make a `break` or `return`
75+ /// statement look like a function call.
76+ ///
77+ /// **Known problems:** The lint currently misses unit return types in types,
78+ /// e.g. the `F` in `fn generic_unit<F: Fn() -> ()>(f: F) { .. }`.
79+ ///
80+ /// **Example:**
81+ /// ```rust
82+ /// fn return_unit() -> () { () }
83+ /// ```
84+ declare_clippy_lint ! {
85+ pub UNUSED_UNIT ,
86+ style,
87+ "needless unit expression"
88+ }
89+
7190#[ derive( Copy , Clone ) ]
7291pub struct ReturnPass ;
7392
@@ -162,23 +181,98 @@ impl ReturnPass {
162181
163182impl LintPass for ReturnPass {
164183 fn get_lints ( & self ) -> LintArray {
165- lint_array ! ( NEEDLESS_RETURN , LET_AND_RETURN )
184+ lint_array ! ( NEEDLESS_RETURN , LET_AND_RETURN , UNUSED_UNIT )
166185 }
167186}
168187
169188impl EarlyLintPass for ReturnPass {
170- fn check_fn ( & mut self , cx : & EarlyContext < ' _ > , kind : FnKind < ' _ > , _ : & ast:: FnDecl , _ : Span , _: ast:: NodeId ) {
189+ fn check_fn ( & mut self , cx : & EarlyContext < ' _ > , kind : FnKind < ' _ > , decl : & ast:: FnDecl , span : Span , _: ast:: NodeId ) {
171190 match kind {
172191 FnKind :: ItemFn ( .., block) | FnKind :: Method ( .., block) => self . check_block_return ( cx, block) ,
173192 FnKind :: Closure ( body) => self . check_final_expr ( cx, body, Some ( body. span ) ) ,
174193 }
194+ if_chain ! {
195+ if let ast:: FunctionRetTy :: Ty ( ref ty) = decl. output;
196+ if let ast:: TyKind :: Tup ( ref vals) = ty. node;
197+ if vals. is_empty( ) && !in_macro( ty. span) && get_def( span) == get_def( ty. span) ;
198+ then {
199+ let ( rspan, appl) = if let Ok ( fn_source) =
200+ cx. sess( ) . source_map( )
201+ . span_to_snippet( span. with_hi( ty. span. hi( ) ) ) {
202+ if let Some ( rpos) = fn_source. rfind( "->" ) {
203+ ( ty. span. with_lo( BytePos ( span. lo( ) . 0 + rpos as u32 ) ) ,
204+ Applicability :: MachineApplicable )
205+ } else {
206+ ( ty. span, Applicability :: MaybeIncorrect )
207+ }
208+ } else {
209+ ( ty. span, Applicability :: MaybeIncorrect )
210+ } ;
211+ span_lint_and_then( cx, UNUSED_UNIT , rspan, "unneeded unit return type" , |db| {
212+ db. span_suggestion_with_applicability(
213+ rspan,
214+ "remove the `-> ()`" ,
215+ String :: new( ) ,
216+ appl,
217+ ) ;
218+ } ) ;
219+ }
220+ }
175221 }
176222
177223 fn check_block ( & mut self , cx : & EarlyContext < ' _ > , block : & ast:: Block ) {
178224 self . check_let_return ( cx, block) ;
225+ if_chain ! {
226+ if let Some ( ref stmt) = block. stmts. last( ) ;
227+ if let ast:: StmtKind :: Expr ( ref expr) = stmt. node;
228+ if is_unit_expr( expr) && !in_macro( expr. span) ;
229+ then {
230+ let sp = expr. span;
231+ span_lint_and_then( cx, UNUSED_UNIT , sp, "unneeded unit expression" , |db| {
232+ db. span_suggestion_with_applicability(
233+ sp,
234+ "remove the final `()`" ,
235+ String :: new( ) ,
236+ Applicability :: MachineApplicable ,
237+ ) ;
238+ } ) ;
239+ }
240+ }
241+ }
242+
243+ fn check_expr ( & mut self , cx : & EarlyContext < ' _ > , e : & ast:: Expr ) {
244+ match e. node {
245+ ast:: ExprKind :: Ret ( Some ( ref expr) ) | ast:: ExprKind :: Break ( _, Some ( ref expr) ) => {
246+ if is_unit_expr ( expr) && !in_macro ( expr. span ) {
247+ span_lint_and_then ( cx, UNUSED_UNIT , expr. span , "unneeded `()`" , |db| {
248+ db. span_suggestion_with_applicability (
249+ expr. span ,
250+ "remove the `()`" ,
251+ String :: new ( ) ,
252+ Applicability :: MachineApplicable ,
253+ ) ;
254+ } ) ;
255+ }
256+ }
257+ _ => ( )
258+ }
179259 }
180260}
181261
182262fn attr_is_cfg ( attr : & ast:: Attribute ) -> bool {
183263 attr. meta_item_list ( ) . is_some ( ) && attr. name ( ) == "cfg"
184264}
265+
266+ // get the def site
267+ fn get_def ( span : Span ) -> Option < Span > {
268+ span. ctxt ( ) . outer ( ) . expn_info ( ) . and_then ( |info| info. def_site )
269+ }
270+
271+ // is this expr a `()` unit?
272+ fn is_unit_expr ( expr : & ast:: Expr ) -> bool {
273+ if let ast:: ExprKind :: Tup ( ref vals) = expr. node {
274+ vals. is_empty ( )
275+ } else {
276+ false
277+ }
278+ }
0 commit comments