+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{fn_def_id, in_macro, path_to_local_id};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
use rustc_span::source_map::Span;
use rustc_span::sym;
-use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then};
-
declare_clippy_lint! {
- /// **What it does:** Checks for `let`-bindings, which are subsequently
+ /// ### What it does
+ /// Checks for `let`-bindings, which are subsequently
/// returned.
///
- /// **Why is this bad?** It is just extraneous code. Remove it to make your code
+ /// ### Why is this bad?
+ /// It is just extraneous code. Remove it to make your code
/// more rusty.
///
- /// **Known problems:** None.
- ///
- /// **Example:**
+ /// ### Example
/// ```rust
/// fn foo() -> String {
/// let x = String::new();
}
declare_clippy_lint! {
- /// **What it does:** Checks for return statements at the end of a block.
+ /// ### What it does
+ /// Checks for return statements at the end of a block.
///
- /// **Why is this bad?** Removing the `return` and semicolon will make the code
+ /// ### Why is this bad?
+ /// Removing the `return` and semicolon will make the code
/// more rusty.
///
- /// **Known problems:** None.
- ///
- /// **Example:**
+ /// ### Example
/// ```rust
/// fn foo(x: usize) -> usize {
/// return x;
if let Some(stmt) = block.stmts.iter().last();
if let StmtKind::Local(local) = &stmt.kind;
if local.ty.is_none();
- if local.attrs.is_empty();
+ if cx.tcx.hir().attrs(local.hir_id).is_empty();
if let Some(initexpr) = &local.init;
- if let PatKind::Binding(.., ident, _) = local.pat.kind;
- if let ExprKind::Path(qpath) = &retexpr.kind;
- if match_qpath(qpath, &[&*ident.name.as_str()]);
+ if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
+ if path_to_local_id(retexpr, local_id);
if !last_statement_borrows(cx, initexpr);
if !in_external_macro(cx.sess(), initexpr.span);
if !in_external_macro(cx.sess(), retexpr.span);
err.span_label(local.span, "unnecessary `let` binding");
if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
- if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() {
+ if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
snippet.push_str(" as _");
}
err.multipart_suggestion(
_: HirId,
) {
match kind {
- FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty),
+ FnKind::Closure => {
+ // when returning without value in closure, replace this `return`
+ // with an empty block to prevent invalid suggestion (see #6501)
+ let replacement = if let ExprKind::Ret(None) = &body.value.kind {
+ RetReplacement::Block
+ } else {
+ RetReplacement::Empty
+ };
+ check_final_expr(cx, &body.value, Some(body.value.span), replacement);
+ },
FnKind::ItemFn(..) | FnKind::Method(..) => {
- if let ExprKind::Block(ref block, _) = body.value.kind {
+ if let ExprKind::Block(block, _) = body.value.kind {
check_block_return(cx, block);
}
},
check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
} else if let Some(stmt) = block.stmts.iter().last() {
match stmt.kind {
- StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
},
_ => (),
// simple return is always "bad"
ExprKind::Ret(ref inner) => {
// allow `#[cfg(a)] return a; #[cfg(b)] return b;`
- if !expr.attrs.iter().any(attr_is_cfg) {
+ let attrs = cx.tcx.hir().attrs(expr.hir_id);
+ if !attrs.iter().any(attr_is_cfg) {
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
if !borrows {
emit_return_lint(
}
},
// a whole block? check it!
- ExprKind::Block(ref block, _) => {
+ ExprKind::Block(block, _) => {
check_block_return(cx, block);
},
ExprKind::If(_, then, else_clause_opt) => {
- if let ExprKind::Block(ref ifblock, _) = then.kind {
+ if let ExprKind::Block(ifblock, _) = then.kind {
check_block_return(cx, ifblock);
}
if let Some(else_clause) = else_clause_opt {
// an if/if let expr, check both exprs
// note, if without else is going to be a type checking error anyways
// (except for unit type functions) so we don't match it
- ExprKind::Match(_, ref arms, source) => match source {
- MatchSource::Normal => {
- for arm in arms.iter() {
- check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
- }
- },
- MatchSource::IfLetDesugar {
- contains_else_clause: true,
- } => {
- if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind {
- check_block_return(cx, ifblock);
- }
- check_final_expr(cx, arms[1].body, None, RetReplacement::Empty);
- },
- _ => (),
+ ExprKind::Match(_, arms, MatchSource::Normal) => {
+ for arm in arms.iter() {
+ check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block);
+ }
},
+ ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
_ => (),
}
}
if let Some(snippet) = snippet_opt(cx, inner_span) {
diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
}
- })
+ });
},
None => match replacement {
RetReplacement::Empty => {
.fn_sig(def_id)
.output()
.skip_binder()
- .walk()
+ .walk(self.cx.tcx)
.any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
}