Skip to content

Commit f6449d6

Browse files
committed
Implement a ReturnVisitor
1 parent f328623 commit f6449d6

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

clippy_utils/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#![feature(rustc_private)]
1111
#![feature(assert_matches)]
1212
#![feature(unwrap_infallible)]
13+
#![feature(associated_type_defaults)]
1314
#![recursion_limit = "512"]
1415
#![allow(
1516
clippy::missing_errors_doc,
@@ -30,6 +31,7 @@
3031
// FIXME: switch to something more ergonomic here, once available.
3132
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
3233
extern crate rustc_ast;
34+
extern crate rustc_ast_ir;
3335
extern crate rustc_ast_pretty;
3436
extern crate rustc_attr;
3537
extern crate rustc_const_eval;
@@ -69,6 +71,7 @@ pub mod numeric_literal;
6971
pub mod paths;
7072
pub mod ptr;
7173
pub mod qualify_min_const_fn;
74+
mod returns;
7275
pub mod source;
7376
pub mod str_utils;
7477
pub mod sugg;
@@ -81,6 +84,7 @@ pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
8184
pub use self::hir_utils::{
8285
HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over,
8386
};
87+
pub use returns::{ReturnVisitor, visit_returns};
8488

8589
use core::mem;
8690
use core::ops::ControlFlow;

clippy_utils/src/returns.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use std::ops::ControlFlow;
2+
3+
use rustc_ast::visit::VisitorResult;
4+
use rustc_ast_ir::try_visit;
5+
use rustc_hir::intravisit::{self, Visitor};
6+
use rustc_hir::{Block, Expr, ExprKind};
7+
8+
pub enum ReturnType<'tcx> {
9+
/// An implicit return.
10+
///
11+
/// This is an expression that evaluates directly to a value, like a literal or operation.
12+
Implicit(&'tcx Expr<'tcx>),
13+
/// An explicit return.
14+
///
15+
/// This is the return expression of `return <expr>`.
16+
Explicit(&'tcx Expr<'tcx>),
17+
/// An explicit unit type return.
18+
///
19+
/// This is the return expression `return`.
20+
UnitReturnExplicit(&'tcx Expr<'tcx>),
21+
/// A `()` implicit return.
22+
///
23+
/// The expression is the `ExprKind::If` with no `else` block.
24+
///
25+
/// ```no_run
26+
/// fn example() -> () {
27+
/// if true {
28+
///
29+
/// } // no else!
30+
/// }
31+
/// ```
32+
MissingElseImplicit(&'tcx Expr<'tcx>),
33+
/// A diverging implict return.
34+
///
35+
/// ```no_run
36+
/// fn example() -> u8 {
37+
/// { todo!(); }
38+
/// }
39+
/// ```
40+
DivergingImplicit(&'tcx Block<'tcx>),
41+
}
42+
43+
pub trait ReturnVisitor {
44+
type Result: VisitorResult = ();
45+
46+
fn visit_return(&mut self, return_type: ReturnType<'_>) -> Self::Result;
47+
}
48+
49+
struct ExplicitReturnDriver<V>(V);
50+
51+
impl<V: ReturnVisitor> Visitor<'_> for ExplicitReturnDriver<V> {
52+
type Result = V::Result;
53+
type NestedFilter = intravisit::nested_filter::None;
54+
55+
fn visit_expr(&mut self, expr: &Expr<'_>) -> Self::Result {
56+
if let ExprKind::Ret(ret_val_expr) = expr.kind {
57+
if let Some(ret_val_expr) = ret_val_expr {
58+
try_visit!(self.0.visit_return(ReturnType::Explicit(ret_val_expr)));
59+
} else {
60+
try_visit!(self.0.visit_return(ReturnType::UnitReturnExplicit(expr)));
61+
}
62+
}
63+
64+
intravisit::walk_expr(self, expr)
65+
}
66+
}
67+
68+
fn visit_implicit_returns<V>(visitor: &mut V, expr: &Expr<'_>) -> V::Result
69+
where
70+
V: ReturnVisitor,
71+
{
72+
let cont = || V::Result::from_branch(ControlFlow::Continue(()));
73+
match expr.kind {
74+
ExprKind::Block(block, _) => {
75+
if let Some(expr) = block.expr {
76+
visit_implicit_returns(visitor, expr)
77+
} else {
78+
visitor.visit_return(ReturnType::DivergingImplicit(block))
79+
}
80+
},
81+
ExprKind::If(_, true_block, else_block) => {
82+
try_visit!(visit_implicit_returns(visitor, true_block));
83+
if let Some(expr) = else_block {
84+
visit_implicit_returns(visitor, expr)
85+
} else {
86+
visitor.visit_return(ReturnType::MissingElseImplicit(expr))
87+
}
88+
},
89+
ExprKind::Match(_, arms, _) => {
90+
for arm in arms {
91+
try_visit!(visit_implicit_returns(visitor, arm.body));
92+
}
93+
94+
cont()
95+
},
96+
97+
_ => visitor.visit_return(ReturnType::Implicit(expr)),
98+
}
99+
}
100+
101+
pub fn visit_returns<V>(visitor: V, expr: &Expr<'_>) -> V::Result
102+
where
103+
V: ReturnVisitor,
104+
{
105+
let mut explicit_driver = ExplicitReturnDriver(visitor);
106+
try_visit!(explicit_driver.visit_expr(expr));
107+
108+
visit_implicit_returns(&mut explicit_driver.0, expr)
109+
}

0 commit comments

Comments
 (0)