Skip to content

Commit 8e96ade

Browse files
committed
fix [needless_return] incorrect suggestion when returning if sequence
1 parent 298f139 commit 8e96ade

File tree

4 files changed

+72
-42
lines changed

4 files changed

+72
-42
lines changed

clippy_lints/src/returns.rs

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -69,31 +69,35 @@ declare_clippy_lint! {
6969
"using a return statement like `return expr;` where an expression would suffice"
7070
}
7171

72-
#[derive(PartialEq, Eq, Copy, Clone)]
72+
#[derive(PartialEq, Eq, Clone)]
7373
enum RetReplacement {
7474
Empty,
7575
Block,
7676
Unit,
77+
IfSequence(String),
78+
Expr(String),
7779
}
7880

7981
impl RetReplacement {
8082
fn sugg_help(self) -> &'static str {
8183
match self {
82-
Self::Empty => "remove `return`",
84+
Self::Empty | Self::Expr(_) => "remove `return`",
8385
Self::Block => "replace `return` with an empty block",
8486
Self::Unit => "replace `return` with a unit value",
87+
Self::IfSequence(_) => "remove `return` and wrap the sequence with parentheses",
8588
}
8689
}
8790
}
8891

8992
impl ToString for RetReplacement {
9093
fn to_string(&self) -> String {
91-
match *self {
92-
Self::Empty => "",
93-
Self::Block => "{}",
94-
Self::Unit => "()",
94+
match self {
95+
Self::Empty => String::new(),
96+
Self::Block => "{}".to_string(),
97+
Self::Unit => "()".to_string(),
98+
Self::IfSequence(inner) => format!("({inner})"),
99+
Self::Expr(inner) => inner.clone(),
95100
}
96-
.to_string()
97101
}
98102
}
99103

@@ -210,28 +214,46 @@ fn check_final_expr<'tcx>(
210214
match &peeled_drop_expr.kind {
211215
// simple return is always "bad"
212216
ExprKind::Ret(ref inner) => {
213-
// if desugar of `do yeet`, don't lint
214-
if let Some(inner_expr) = inner
215-
&& let ExprKind::Call(path_expr, _) = inner_expr.kind
216-
&& let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind
217-
{
218-
return;
219-
}
217+
// check if expr return nothing
218+
let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
219+
extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
220+
} else {
221+
peeled_drop_expr.span
222+
};
223+
224+
let replacement = if let Some(inner_expr) = inner {
225+
// if desugar of `do yeet`, don't lint
226+
if let ExprKind::Call(path_expr, _) = inner_expr.kind
227+
&& let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind
228+
{
229+
return;
230+
}
231+
232+
let (snippet, _) = snippet_with_context(
233+
cx,
234+
inner_expr.span,
235+
ret_span.ctxt(),
236+
"..",
237+
&mut Applicability::MachineApplicable,
238+
);
239+
if expr_contains_if(inner_expr) {
240+
RetReplacement::IfSequence(snippet.to_string())
241+
} else {
242+
RetReplacement::Expr(snippet.to_string())
243+
}
244+
} else {
245+
replacement
246+
};
247+
220248
if !cx.tcx.hir().attrs(expr.hir_id).is_empty() {
221249
return;
222250
}
223251
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
224252
if borrows {
225253
return;
226254
}
227-
// check if expr return nothing
228-
let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
229-
extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
230-
} else {
231-
peeled_drop_expr.span
232-
};
233255

234-
emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement);
256+
emit_return_lint(cx, ret_span, semi_spans, replacement);
235257
},
236258
ExprKind::If(_, then, else_clause_opt) => {
237259
check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
@@ -253,29 +275,21 @@ fn check_final_expr<'tcx>(
253275
}
254276
}
255277

256-
fn emit_return_lint(
257-
cx: &LateContext<'_>,
258-
ret_span: Span,
259-
semi_spans: Vec<Span>,
260-
inner_span: Option<Span>,
261-
replacement: RetReplacement,
262-
) {
278+
fn expr_contains_if<'tcx>(expr: &'tcx Expr<'tcx>) -> bool {
279+
match expr.kind {
280+
ExprKind::If(..) => true,
281+
ExprKind::Binary(_, left, right) => expr_contains_if(left) || expr_contains_if(right),
282+
_ => false,
283+
}
284+
}
285+
286+
fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: RetReplacement) {
263287
if ret_span.from_expansion() {
264288
return;
265289
}
266-
let mut applicability = Applicability::MachineApplicable;
267-
let return_replacement = inner_span.map_or_else(
268-
|| replacement.to_string(),
269-
|inner_span| {
270-
let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
271-
snippet.to_string()
272-
},
273-
);
274-
let sugg_help = if inner_span.is_some() {
275-
"remove `return`"
276-
} else {
277-
replacement.sugg_help()
278-
};
290+
let applicability = Applicability::MachineApplicable;
291+
let return_replacement = replacement.to_string();
292+
let sugg_help = replacement.sugg_help();
279293
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
280294
diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability);
281295
// for each parent statement, we need to remove the semicolon

tests/ui/needless_return.fixed

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,4 +297,8 @@ fn issue10051() -> Result<String, String> {
297297
}
298298
}
299299

300+
fn issue10049(b1: bool, b2: bool, b3: bool) -> u32 {
301+
(if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 })
302+
}
303+
300304
fn main() {}

tests/ui/needless_return.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,4 +307,8 @@ fn issue10051() -> Result<String, String> {
307307
}
308308
}
309309

310+
fn issue10049(b1: bool, b2: bool, b3: bool) -> u32 {
311+
return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
312+
}
313+
310314
fn main() {}

tests/ui/needless_return.stderr

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,5 +418,13 @@ LL | return Err(format!("err!"));
418418
|
419419
= help: remove `return`
420420

421-
error: aborting due to 50 previous errors
421+
error: unneeded `return` statement
422+
--> $DIR/needless_return.rs:311:5
423+
|
424+
LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
425+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
426+
|
427+
= help: remove `return` and wrap the sequence with parentheses
428+
429+
error: aborting due to 51 previous errors
422430

0 commit comments

Comments
 (0)