Skip to content

Commit d3d235d

Browse files
committed
Auto merge of rust-lang#10345 - J-ZhengLi:issue_10049, r=xFrednet
fix [`needless_return`] incorrect suggestion when returning if sequence fixes: rust-lang#10049 --- changelog: [`needless_return`]: fix incorrect suggestion on if sequence
2 parents e018a2c + 8b93eb8 commit d3d235d

File tree

4 files changed

+102
-46
lines changed

4 files changed

+102
-46
lines changed

clippy_lints/src/returns.rs

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
1414
use rustc_span::def_id::LocalDefId;
1515
use rustc_span::source_map::Span;
1616
use rustc_span::{BytePos, Pos};
17+
use std::borrow::Cow;
1718

1819
declare_clippy_lint! {
1920
/// ### What it does
@@ -69,31 +70,41 @@ declare_clippy_lint! {
6970
"using a return statement like `return expr;` where an expression would suffice"
7071
}
7172

72-
#[derive(PartialEq, Eq, Copy, Clone)]
73-
enum RetReplacement {
73+
#[derive(PartialEq, Eq, Clone)]
74+
enum RetReplacement<'tcx> {
7475
Empty,
7576
Block,
7677
Unit,
78+
IfSequence(Cow<'tcx, str>, Applicability),
79+
Expr(Cow<'tcx, str>, Applicability),
7780
}
7881

79-
impl RetReplacement {
82+
impl<'tcx> RetReplacement<'tcx> {
8083
fn sugg_help(self) -> &'static str {
8184
match self {
82-
Self::Empty => "remove `return`",
85+
Self::Empty | Self::Expr(..) => "remove `return`",
8386
Self::Block => "replace `return` with an empty block",
8487
Self::Unit => "replace `return` with a unit value",
88+
Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses",
89+
}
90+
}
91+
fn applicability(&self) -> Option<Applicability> {
92+
match self {
93+
Self::Expr(_, ap) | Self::IfSequence(_, ap) => Some(*ap),
94+
_ => None,
8595
}
8696
}
8797
}
8898

89-
impl ToString for RetReplacement {
99+
impl<'tcx> ToString for RetReplacement<'tcx> {
90100
fn to_string(&self) -> String {
91-
match *self {
92-
Self::Empty => "",
93-
Self::Block => "{}",
94-
Self::Unit => "()",
101+
match self {
102+
Self::Empty => String::new(),
103+
Self::Block => "{}".to_string(),
104+
Self::Unit => "()".to_string(),
105+
Self::IfSequence(inner, _) => format!("({inner})"),
106+
Self::Expr(inner, _) => inner.to_string(),
95107
}
96-
.to_string()
97108
}
98109
}
99110

@@ -204,34 +215,47 @@ fn check_final_expr<'tcx>(
204215
expr: &'tcx Expr<'tcx>,
205216
semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an
206217
* needless return */
207-
replacement: RetReplacement,
218+
replacement: RetReplacement<'tcx>,
208219
) {
209220
let peeled_drop_expr = expr.peel_drop_temps();
210221
match &peeled_drop_expr.kind {
211222
// simple return is always "bad"
212223
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-
}
224+
// check if expr return nothing
225+
let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
226+
extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
227+
} else {
228+
peeled_drop_expr.span
229+
};
230+
231+
let replacement = if let Some(inner_expr) = inner {
232+
// if desugar of `do yeet`, don't lint
233+
if let ExprKind::Call(path_expr, _) = inner_expr.kind
234+
&& let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind
235+
{
236+
return;
237+
}
238+
239+
let mut applicability = Applicability::MachineApplicable;
240+
let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability);
241+
if expr_contains_conjunctive_ifs(inner_expr) {
242+
RetReplacement::IfSequence(snippet, applicability)
243+
} else {
244+
RetReplacement::Expr(snippet, applicability)
245+
}
246+
} else {
247+
replacement
248+
};
249+
220250
if !cx.tcx.hir().attrs(expr.hir_id).is_empty() {
221251
return;
222252
}
223253
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
224254
if borrows {
225255
return;
226256
}
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-
};
233257

234-
emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement);
258+
emit_return_lint(cx, ret_span, semi_spans, replacement);
235259
},
236260
ExprKind::If(_, then, else_clause_opt) => {
237261
check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
@@ -253,29 +277,25 @@ fn check_final_expr<'tcx>(
253277
}
254278
}
255279

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-
) {
280+
fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool {
281+
fn contains_if(expr: &Expr<'_>, on_if: bool) -> bool {
282+
match expr.kind {
283+
ExprKind::If(..) => on_if,
284+
ExprKind::Binary(_, left, right) => contains_if(left, true) || contains_if(right, true),
285+
_ => false,
286+
}
287+
}
288+
289+
contains_if(expr, false)
290+
}
291+
292+
fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: RetReplacement<'_>) {
263293
if ret_span.from_expansion() {
264294
return;
265295
}
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-
};
296+
let applicability = replacement.applicability().unwrap_or(Applicability::MachineApplicable);
297+
let return_replacement = replacement.to_string();
298+
let sugg_help = replacement.sugg_help();
279299
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
280300
diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability);
281301
// for each parent statement, we need to remove the semicolon

tests/ui/needless_return.fixed

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

300+
mod issue10049 {
301+
fn single() -> u32 {
302+
if true { 1 } else { 2 }
303+
}
304+
305+
fn multiple(b1: bool, b2: bool, b3: bool) -> u32 {
306+
(if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 })
307+
}
308+
}
309+
300310
fn main() {}

tests/ui/needless_return.rs

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

310+
mod issue10049 {
311+
fn single() -> u32 {
312+
return if true { 1 } else { 2 };
313+
}
314+
315+
fn multiple(b1: bool, b2: bool, b3: bool) -> u32 {
316+
return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
317+
}
318+
}
319+
310320
fn main() {}

tests/ui/needless_return.stderr

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,5 +418,21 @@ 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:312:9
423+
|
424+
LL | return if true { 1 } else { 2 };
425+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
426+
|
427+
= help: remove `return`
428+
429+
error: unneeded `return` statement
430+
--> $DIR/needless_return.rs:316:9
431+
|
432+
LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
433+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
434+
|
435+
= help: remove `return` and wrap the sequence with parentheses
436+
437+
error: aborting due to 52 previous errors
422438

0 commit comments

Comments
 (0)