@@ -83,6 +83,12 @@ declare_clippy_lint! {
83
83
"needless unit expression"
84
84
}
85
85
86
+ #[ derive( PartialEq , Eq , Copy , Clone ) ]
87
+ enum RetReplacement {
88
+ Empty ,
89
+ Unit ,
90
+ }
91
+
86
92
declare_lint_pass ! ( Return => [ NEEDLESS_RETURN , LET_AND_RETURN , UNUSED_UNIT ] ) ;
87
93
88
94
impl Return {
@@ -91,21 +97,32 @@ impl Return {
91
97
if let Some ( stmt) = block. stmts . last ( ) {
92
98
match stmt. node {
93
99
ast:: StmtKind :: Expr ( ref expr) | ast:: StmtKind :: Semi ( ref expr) => {
94
- self . check_final_expr ( cx, expr, Some ( stmt. span ) ) ;
100
+ self . check_final_expr ( cx, expr, Some ( stmt. span ) , RetReplacement :: Empty ) ;
95
101
} ,
96
102
_ => ( ) ,
97
103
}
98
104
}
99
105
}
100
106
101
107
// Check a the final expression in a block if it's a return.
102
- fn check_final_expr ( & mut self , cx : & EarlyContext < ' _ > , expr : & ast:: Expr , span : Option < Span > ) {
108
+ fn check_final_expr (
109
+ & mut self ,
110
+ cx : & EarlyContext < ' _ > ,
111
+ expr : & ast:: Expr ,
112
+ span : Option < Span > ,
113
+ replacement : RetReplacement ,
114
+ ) {
103
115
match expr. node {
104
116
// simple return is always "bad"
105
- ast:: ExprKind :: Ret ( Some ( ref inner) ) => {
117
+ ast:: ExprKind :: Ret ( ref inner) => {
106
118
// allow `#[cfg(a)] return a; #[cfg(b)] return b;`
107
119
if !expr. attrs . iter ( ) . any ( attr_is_cfg) {
108
- self . emit_return_lint ( cx, span. expect ( "`else return` is not possible" ) , inner. span ) ;
120
+ self . emit_return_lint (
121
+ cx,
122
+ span. expect ( "`else return` is not possible" ) ,
123
+ inner. as_ref ( ) . map ( |i| i. span ) ,
124
+ replacement,
125
+ ) ;
109
126
}
110
127
} ,
111
128
// a whole block? check it!
@@ -117,32 +134,60 @@ impl Return {
117
134
// (except for unit type functions) so we don't match it
118
135
ast:: ExprKind :: If ( _, ref ifblock, Some ( ref elsexpr) ) => {
119
136
self . check_block_return ( cx, ifblock) ;
120
- self . check_final_expr ( cx, elsexpr, None ) ;
137
+ self . check_final_expr ( cx, elsexpr, None , RetReplacement :: Empty ) ;
121
138
} ,
122
139
// a match expr, check all arms
123
140
ast:: ExprKind :: Match ( _, ref arms) => {
124
141
for arm in arms {
125
- self . check_final_expr ( cx, & arm. body , Some ( arm. body . span ) ) ;
142
+ self . check_final_expr ( cx, & arm. body , Some ( arm. body . span ) , RetReplacement :: Unit ) ;
126
143
}
127
144
} ,
128
145
_ => ( ) ,
129
146
}
130
147
}
131
148
132
- fn emit_return_lint ( & mut self , cx : & EarlyContext < ' _ > , ret_span : Span , inner_span : Span ) {
133
- if in_external_macro ( cx. sess ( ) , inner_span) || in_macro_or_desugar ( inner_span) {
134
- return ;
149
+ fn emit_return_lint (
150
+ & mut self ,
151
+ cx : & EarlyContext < ' _ > ,
152
+ ret_span : Span ,
153
+ inner_span : Option < Span > ,
154
+ replacement : RetReplacement ,
155
+ ) {
156
+ match inner_span {
157
+ Some ( inner_span) => {
158
+ if in_external_macro ( cx. sess ( ) , inner_span) || in_macro_or_desugar ( inner_span) {
159
+ return ;
160
+ }
161
+
162
+ span_lint_and_then ( cx, NEEDLESS_RETURN , ret_span, "unneeded return statement" , |db| {
163
+ if let Some ( snippet) = snippet_opt ( cx, inner_span) {
164
+ db. span_suggestion ( ret_span, "remove `return`" , snippet, Applicability :: MachineApplicable ) ;
165
+ }
166
+ } )
167
+ } ,
168
+ None => match replacement {
169
+ RetReplacement :: Empty => {
170
+ span_lint_and_then ( cx, NEEDLESS_RETURN , ret_span, "unneeded return statement" , |db| {
171
+ db. span_suggestion (
172
+ ret_span,
173
+ "remove `return`" ,
174
+ String :: new ( ) ,
175
+ Applicability :: MachineApplicable ,
176
+ ) ;
177
+ } ) ;
178
+ } ,
179
+ RetReplacement :: Unit => {
180
+ span_lint_and_then ( cx, NEEDLESS_RETURN , ret_span, "unneeded return statement" , |db| {
181
+ db. span_suggestion (
182
+ ret_span,
183
+ "replace `return` with the unit type" ,
184
+ "()" . to_string ( ) ,
185
+ Applicability :: MachineApplicable ,
186
+ ) ;
187
+ } ) ;
188
+ } ,
189
+ } ,
135
190
}
136
- span_lint_and_then ( cx, NEEDLESS_RETURN , ret_span, "unneeded return statement" , |db| {
137
- if let Some ( snippet) = snippet_opt ( cx, inner_span) {
138
- db. span_suggestion (
139
- ret_span,
140
- "remove `return` as shown" ,
141
- snippet,
142
- Applicability :: MachineApplicable ,
143
- ) ;
144
- }
145
- } ) ;
146
191
}
147
192
148
193
// Check for "let x = EXPR; x"
@@ -195,7 +240,7 @@ impl EarlyLintPass for Return {
195
240
fn check_fn ( & mut self , cx : & EarlyContext < ' _ > , kind : FnKind < ' _ > , decl : & ast:: FnDecl , span : Span , _: ast:: NodeId ) {
196
241
match kind {
197
242
FnKind :: ItemFn ( .., block) | FnKind :: Method ( .., block) => self . check_block_return ( cx, block) ,
198
- FnKind :: Closure ( body) => self . check_final_expr ( cx, body, Some ( body. span ) ) ,
243
+ FnKind :: Closure ( body) => self . check_final_expr ( cx, body, Some ( body. span ) , RetReplacement :: Empty ) ,
199
244
}
200
245
if_chain ! {
201
246
if let ast:: FunctionRetTy :: Ty ( ref ty) = decl. output;
0 commit comments