1 use if_chain::if_chain;
3 use rustc_ast::visit::FnKind;
4 use rustc_errors::Applicability;
5 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
6 use rustc_middle::lint::in_external_macro;
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
8 use rustc_span::source_map::Span;
9 use rustc_span::BytePos;
11 use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then};
13 declare_clippy_lint! {
14 /// **What it does:** Checks for return statements at the end of a block.
16 /// **Why is this bad?** Removing the `return` and semicolon will make the code
19 /// **Known problems:** If the computation returning the value borrows a local
20 /// variable, removing the `return` may run afoul of the borrow checker.
24 /// fn foo(x: usize) -> usize {
30 /// fn foo(x: usize) -> usize {
36 "using a return statement like `return expr;` where an expression would suffice"
39 declare_clippy_lint! {
40 /// **What it does:** Checks for unit (`()`) expressions that can be removed.
42 /// **Why is this bad?** Such expressions add no value, but can make the code
43 /// less readable. Depending on formatting they can make a `break` or `return`
44 /// statement look like a function call.
46 /// **Known problems:** The lint currently misses unit return types in types,
47 /// e.g., the `F` in `fn generic_unit<F: Fn() -> ()>(f: F) { .. }`.
51 /// fn return_unit() -> () {
57 "needless unit expression"
60 #[derive(PartialEq, Eq, Copy, Clone)]
66 declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]);
69 // Check the final stmt or expr in a block for unnecessary return.
70 fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
71 if let Some(stmt) = block.stmts.last() {
73 ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
74 self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
81 // Check the final expression in a block if it's a return.
84 cx: &EarlyContext<'_>,
87 replacement: RetReplacement,
90 // simple return is always "bad"
91 ast::ExprKind::Ret(ref inner) => {
92 // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
93 if !expr.attrs.iter().any(attr_is_cfg) {
94 Self::emit_return_lint(
96 span.expect("`else return` is not possible"),
97 inner.as_ref().map(|i| i.span),
102 // a whole block? check it!
103 ast::ExprKind::Block(ref block, _) => {
104 self.check_block_return(cx, block);
106 // an if/if let expr, check both exprs
107 // note, if without else is going to be a type checking error anyways
108 // (except for unit type functions) so we don't match it
109 ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
110 self.check_block_return(cx, ifblock);
111 self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty);
113 // a match expr, check all arms
114 ast::ExprKind::Match(_, ref arms) => {
116 self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
123 fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
125 Some(inner_span) => {
126 if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() {
130 span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
131 if let Some(snippet) = snippet_opt(cx, inner_span) {
132 diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
136 None => match replacement {
137 RetReplacement::Empty => {
142 "unneeded `return` statement",
145 Applicability::MachineApplicable,
148 RetReplacement::Block => {
153 "unneeded `return` statement",
154 "replace `return` with an empty block",
156 Applicability::MachineApplicable,
164 impl EarlyLintPass for Return {
165 fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
167 FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block),
168 FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty),
169 FnKind::Fn(.., None) => {},
172 if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
173 if let ast::TyKind::Tup(ref vals) = ty.kind;
174 if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
176 lint_unneeded_unit_return(cx, ty, span);
181 fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
183 if let Some(ref stmt) = block.stmts.last();
184 if let ast::StmtKind::Expr(ref expr) = stmt.kind;
185 if is_unit_expr(expr) && !stmt.span.from_expansion();
192 "unneeded unit expression",
193 "remove the final `()`",
195 Applicability::MachineApplicable,
201 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
203 ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
204 if is_unit_expr(expr) && !expr.span.from_expansion() {
212 Applicability::MachineApplicable,
220 fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) {
221 let segments = &poly.trait_ref.path.segments;
224 if segments.len() == 1;
225 if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str());
226 if let Some(args) = &segments[0].args;
227 if let ast::GenericArgs::Parenthesized(generic_args) = &**args;
228 if let ast::FnRetTy::Ty(ty) = &generic_args.output;
229 if ty.kind.is_unit();
231 lint_unneeded_unit_return(cx, ty, generic_args.span);
237 fn attr_is_cfg(attr: &ast::Attribute) -> bool {
238 attr.meta_item_list().is_some() && attr.has_name(sym!(cfg))
243 fn get_def(span: Span) -> Option<Span> {
244 if span.from_expansion() {
245 Some(span.ctxt().outer_expn_data().def_site)
251 // is this expr a `()` unit?
252 fn is_unit_expr(expr: &ast::Expr) -> bool {
253 if let ast::ExprKind::Tup(ref vals) = expr.kind {
260 fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
261 let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
264 .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
266 #[allow(clippy::cast_possible_truncation)]
267 ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
268 Applicability::MachineApplicable,
272 (ty.span, Applicability::MaybeIncorrect)
278 "unneeded unit return type",
279 "remove the `-> ()`",