]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/implicit_return.rs
bump version to 1.79.0+dfsg1-1~bpo12+pve2
[rustc.git] / src / tools / clippy / clippy_lints / src / implicit_return.rs
CommitLineData
add651ee
FG
1use clippy_utils::diagnostics::span_lint_hir_and_then;
2use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
3use clippy_utils::visitors::for_each_expr;
4use clippy_utils::{get_async_fn_body, is_async_fn};
2b03887a 5use core::ops::ControlFlow;
f20569fa 6use rustc_errors::Applicability;
2b03887a 7use rustc_hir::intravisit::FnKind;
17df50a5
XL
8use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
9use rustc_lint::{LateContext, LateLintPass, LintContext};
10use rustc_middle::lint::in_external_macro;
4b012472 11use rustc_session::declare_lint_pass;
9ffffee4 12use rustc_span::def_id::LocalDefId;
17df50a5 13use rustc_span::{Span, SyntaxContext};
f20569fa
XL
14
15declare_clippy_lint! {
94222f64
XL
16 /// ### What it does
17 /// Checks for missing return statements at the end of a block.
f20569fa 18 ///
94222f64
XL
19 /// ### Why is this bad?
20 /// Actually omitting the return keyword is idiomatic Rust code. Programmers
f20569fa
XL
21 /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
22 /// the last returning statement because the only difference is a missing `;`. Especially in bigger
23 /// code with multiple return paths having a `return` keyword makes it easier to find the
24 /// corresponding statements.
25 ///
94222f64 26 /// ### Example
ed00b5ec 27 /// ```no_run
f20569fa
XL
28 /// fn foo(x: usize) -> usize {
29 /// x
30 /// }
31 /// ```
32 /// add return
ed00b5ec 33 /// ```no_run
f20569fa
XL
34 /// fn foo(x: usize) -> usize {
35 /// return x;
36 /// }
37 /// ```
a2a8927a 38 #[clippy::version = "1.33.0"]
f20569fa
XL
39 pub IMPLICIT_RETURN,
40 restriction,
41 "use a return statement like `return expr` instead of an expression"
42}
43
44declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
45
064997fb 46fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
17df50a5
XL
47 let mut app = Applicability::MachineApplicable;
48 let snip = snippet_with_applicability(cx, span, "..", &mut app);
064997fb 49 span_lint_hir_and_then(
17df50a5
XL
50 cx,
51 IMPLICIT_RETURN,
064997fb 52 emission_place,
17df50a5
XL
53 span,
54 "missing `return` statement",
064997fb 55 |diag| {
2b03887a 56 diag.span_suggestion(span, "add `return` as shown", format!("return {snip}"), app);
064997fb 57 },
17df50a5
XL
58 );
59}
60
064997fb 61fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, expr_span: Span) {
17df50a5
XL
62 let mut app = Applicability::MachineApplicable;
63 let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
064997fb 64 span_lint_hir_and_then(
17df50a5
XL
65 cx,
66 IMPLICIT_RETURN,
064997fb 67 emission_place,
17df50a5
XL
68 break_span,
69 "missing `return` statement",
064997fb
FG
70 |diag| {
71 diag.span_suggestion(
72 break_span,
73 "change `break` to `return` as shown",
2b03887a 74 format!("return {snip}"),
064997fb
FG
75 app,
76 );
77 },
17df50a5
XL
78 );
79}
80
81#[derive(Clone, Copy, PartialEq, Eq)]
82enum LintLocation {
83 /// The lint was applied to a parent expression.
84 Parent,
85 /// The lint was applied to this expression, a child, or not applied.
86 Inner,
87}
88impl LintLocation {
89 fn still_parent(self, b: bool) -> Self {
90 if b { self } else { Self::Inner }
91 }
92
93 fn is_parent(self) -> bool {
94 self == Self::Parent
95 }
96}
97
98// Gets the call site if the span is in a child context. Otherwise returns `None`.
99fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> {
100 (span.ctxt() != ctxt).then(|| walk_span_to_context(span, ctxt).unwrap_or(span))
f20569fa
XL
101}
102
17df50a5 103fn lint_implicit_returns(
5099ac24
FG
104 cx: &LateContext<'_>,
105 expr: &Expr<'_>,
17df50a5
XL
106 // The context of the function body.
107 ctxt: SyntaxContext,
108 // Whether the expression is from a macro expansion.
109 call_site_span: Option<Span>,
110) -> LintLocation {
f20569fa 111 match expr.kind {
17df50a5
XL
112 ExprKind::Block(
113 Block {
114 expr: Some(block_expr), ..
115 },
116 _,
117 ) => lint_implicit_returns(
118 cx,
119 block_expr,
120 ctxt,
121 call_site_span.or_else(|| get_call_site(block_expr.span, ctxt)),
122 )
123 .still_parent(call_site_span.is_some()),
124
125 ExprKind::If(_, then_expr, Some(else_expr)) => {
126 // Both `then_expr` or `else_expr` are required to be blocks in the same context as the `if`. Don't
127 // bother checking.
128 let res = lint_implicit_returns(cx, then_expr, ctxt, call_site_span).still_parent(call_site_span.is_some());
129 if res.is_parent() {
130 // The return was added as a parent of this if expression.
131 return res;
f20569fa 132 }
17df50a5 133 lint_implicit_returns(cx, else_expr, ctxt, call_site_span).still_parent(call_site_span.is_some())
f20569fa 134 },
f20569fa 135
17df50a5
XL
136 ExprKind::Match(_, arms, _) => {
137 for arm in arms {
138 let res = lint_implicit_returns(
139 cx,
140 arm.body,
141 ctxt,
142 call_site_span.or_else(|| get_call_site(arm.body.span, ctxt)),
143 )
144 .still_parent(call_site_span.is_some());
145 if res.is_parent() {
146 // The return was added as a parent of this match expression.
147 return res;
148 }
f20569fa 149 }
17df50a5 150 LintLocation::Inner
f20569fa 151 },
17df50a5
XL
152
153 ExprKind::Loop(block, ..) => {
154 let mut add_return = false;
2b03887a 155 let _: Option<!> = for_each_expr(block, |e| {
a2a8927a
XL
156 if let ExprKind::Break(dest, sub_expr) = e.kind {
157 if dest.target_id.ok() == Some(expr.hir_id) {
158 if call_site_span.is_none() && e.span.ctxt() == ctxt {
159 // At this point sub_expr can be `None` in async functions which either diverge, or return
160 // the unit type.
161 if let Some(sub_expr) = sub_expr {
064997fb 162 lint_break(cx, e.hir_id, e.span, sub_expr.span);
a2a8927a
XL
163 }
164 } else {
165 // the break expression is from a macro call, add a return to the loop
166 add_return = true;
17df50a5 167 }
17df50a5
XL
168 }
169 }
2b03887a
FG
170 ControlFlow::Continue(())
171 });
17df50a5 172 if add_return {
923072b8 173 #[expect(clippy::option_if_let_else)]
17df50a5 174 if let Some(span) = call_site_span {
064997fb 175 lint_return(cx, expr.hir_id, span);
17df50a5
XL
176 LintLocation::Parent
177 } else {
064997fb 178 lint_return(cx, expr.hir_id, expr.span);
17df50a5 179 LintLocation::Inner
f20569fa
XL
180 }
181 } else {
17df50a5 182 LintLocation::Inner
f20569fa
XL
183 }
184 },
17df50a5
XL
185
186 // If expressions without an else clause, and blocks without a final expression can only be the final expression
187 // if they are divergent, or return the unit type.
188 ExprKind::If(_, _, None) | ExprKind::Block(Block { expr: None, .. }, _) | ExprKind::Ret(_) => {
189 LintLocation::Inner
190 },
191
192 // Any divergent expression doesn't need a return statement.
193 ExprKind::MethodCall(..)
194 | ExprKind::Call(..)
195 | ExprKind::Binary(..)
196 | ExprKind::Unary(..)
197 | ExprKind::Index(..)
198 if cx.typeck_results().expr_ty(expr).is_never() =>
199 {
200 LintLocation::Inner
201 },
202
203 _ =>
204 {
923072b8 205 #[expect(clippy::option_if_let_else)]
17df50a5 206 if let Some(span) = call_site_span {
064997fb 207 lint_return(cx, expr.hir_id, span);
17df50a5
XL
208 LintLocation::Parent
209 } else {
064997fb 210 lint_return(cx, expr.hir_id, expr.span);
17df50a5 211 LintLocation::Inner
f20569fa
XL
212 }
213 },
f20569fa
XL
214 }
215}
216
217impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
218 fn check_fn(
219 &mut self,
220 cx: &LateContext<'tcx>,
17df50a5
XL
221 kind: FnKind<'tcx>,
222 decl: &'tcx FnDecl<'_>,
f20569fa
XL
223 body: &'tcx Body<'_>,
224 span: Span,
9ffffee4 225 _: LocalDefId,
f20569fa 226 ) {
17df50a5 227 if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_)))
c0240ec0 228 || !span.eq_ctxt(body.value.span)
17df50a5
XL
229 || in_external_macro(cx.sess(), span)
230 {
f20569fa
XL
231 return;
232 }
17df50a5 233
f2b60f7d 234 let res_ty = cx.typeck_results().expr_ty(body.value);
17df50a5 235 if res_ty.is_unit() || res_ty.is_never() {
f20569fa
XL
236 return;
237 }
17df50a5
XL
238
239 let expr = if is_async_fn(kind) {
240 match get_async_fn_body(cx.tcx, body) {
241 Some(e) => e,
242 None => return,
243 }
244 } else {
f2b60f7d 245 body.value
17df50a5
XL
246 };
247 lint_implicit_returns(cx, expr, expr.span.ctxt(), None);
f20569fa
XL
248 }
249}