Skip to content

Commit f1c076b

Browse files
committed
Refactored lint to use check_fn
1 parent ccbb200 commit f1c076b

File tree

1 file changed

+106
-81
lines changed

1 file changed

+106
-81
lines changed

Diff for: clippy_lints/src/semicolon_outside_block.rs

+106-81
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use clippy_utils::in_macro;
44
use clippy_utils::source::snippet_with_macro_callsite;
55
use if_chain::if_chain;
66
use rustc_errors::Applicability;
7+
use rustc_hir::intravisit::FnKind;
78
use rustc_hir::ExprKind;
8-
use rustc_hir::{Block, BodyOwnerKind, StmtKind};
9+
use rustc_hir::{Block, Body, Expr, FnDecl, HirId, StmtKind};
910
use rustc_lint::{LateContext, LateLintPass};
1011
use rustc_session::{declare_lint_pass, declare_tool_lint};
1112
use rustc_span::BytePos;
@@ -36,94 +37,118 @@ declare_clippy_lint! {
3637

3738
declare_lint_pass!(SemicolonOutsideBlock => [SEMICOLON_OUTSIDE_BLOCK]);
3839

39-
/// Checks if an ExprKind is of an illegal variant (aka blocks that we don't want)
40-
/// to lint on as it's illegal or unnecessary to put a semicolon afterwards.
41-
/// This macro then inserts a `return` statement to return out of the check_block
42-
/// method.
43-
macro_rules! check_expr_return {
44-
($l:expr) => {
45-
if let ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::DropTemps(..) | ExprKind::Match(..) = $l {
46-
return;
47-
}
48-
};
49-
}
50-
5140
impl LateLintPass<'_> for SemicolonOutsideBlock {
52-
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
53-
if_chain! {
54-
if !in_macro(block.span);
55-
if block.expr.is_none();
56-
if let Some(last) = block.stmts.last();
57-
if let StmtKind::Semi(expr) = last.kind;
58-
let t_expr = cx.typeck_results().expr_ty(expr);
59-
if t_expr.is_unit();
60-
then {
61-
// make sure that the block does not belong to a function by iterating over the parents
62-
for (hir_id, _) in cx.tcx.hir().parent_iter(block.hir_id) {
63-
if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(hir_id) {
64-
// if we're in a body, check if it's an fn or a closure
65-
if cx.tcx.hir().body_owner_kind(hir_id).is_fn_or_closure() {
66-
let item_body = cx.tcx.hir().body(body_id);
67-
if let ExprKind::Block(fn_block, _) = item_body.value.kind {
68-
// check for an illegal statement in the list of statements...
69-
for stmt in fn_block.stmts {
70-
if let StmtKind::Expr(pot_ille_expr) = stmt.kind {
71-
check_expr_return!(pot_ille_expr.kind);
72-
}
73-
}
74-
75-
//.. the potential last statement ..
76-
if let Some(last_expr) = fn_block.expr {
77-
check_expr_return!(last_expr.kind);
78-
}
41+
fn check_fn(
42+
&mut self,
43+
cx: &LateContext<'tcx>,
44+
fn_kind: FnKind<'tcx>,
45+
_: &'tcx FnDecl<'_>,
46+
body: &'tcx Body<'_>,
47+
_: Span,
48+
_: HirId,
49+
) {
50+
if let ExprKind::Block(block) = body.value.kind {
51+
// also check this block if we're inside a closure
52+
if matches!(fn_kind, FnKind::Closure) {
53+
check_block(cx, block);
54+
}
7955

80-
// .. or if this is the body of an fn
81-
if fn_block.hir_id == block.hir_id &&
82-
!matches!(cx.tcx.hir().body_owner_kind(hir_id), BodyOwnerKind::Closure) {
83-
return
84-
}
85-
}
86-
}
87-
}
56+
// iterate over the statements and check if we find a potential
57+
// block to check
58+
for stmt in block.stmts {
59+
match stmt.kind {
60+
StmtKind::Expr(Expr {
61+
kind: ExprKind::Block(bl, ..),
62+
..
63+
})
64+
| StmtKind::Semi(Expr {
65+
kind: ExprKind::Block(bl, ..),
66+
..
67+
}) => check_block(cx, bl),
68+
_ => (),
8869
}
70+
}
8971

90-
// filter out other blocks and the desugared for loop
91-
check_expr_return!(expr.kind);
92-
93-
// make sure we're also having the semicolon at the end of the expression...
94-
let expr_w_sem = expand_span_to_semicolon(cx, expr.span);
95-
let expr_snip = snippet_with_macro_callsite(cx, expr_w_sem, "..");
96-
let mut expr_sugg = expr_snip.to_string();
97-
expr_sugg.pop();
72+
// check if the potential trailing expr is a block and check it if necessary
73+
if let Some(Expr {
74+
kind: ExprKind::Block(bl, ..),
75+
..
76+
}) = block.expr
77+
{
78+
check_block(cx, bl);
79+
}
80+
}
81+
}
82+
}
9883

99-
// and the block
100-
let block_w_sem = expand_span_to_semicolon(cx, block.span);
101-
let mut block_snip: String = snippet_with_macro_callsite(cx, block_w_sem, "..").to_string();
102-
if block_snip.ends_with('\n') {
103-
block_snip.pop();
104-
}
84+
/// Checks for a block if it's a target of this lint and spans a suggestion
85+
/// if applicable. This method also recurses through all other statements that
86+
/// are blocks.
87+
fn check_block(cx: &LateContext<'_>, block: &Block<'_>) {
88+
// check all subblocks
89+
for stmt in block.stmts {
90+
match stmt.kind {
91+
StmtKind::Expr(Expr {
92+
kind: ExprKind::Block(bl, ..),
93+
..
94+
})
95+
| StmtKind::Semi(Expr {
96+
kind: ExprKind::Block(bl, ..),
97+
..
98+
}) => check_block(cx, bl),
99+
_ => (),
100+
}
101+
}
102+
// check the potential trailing expression
103+
if let Some(Expr {
104+
kind: ExprKind::Block(bl, ..),
105+
..
106+
}) = block.expr
107+
{
108+
check_block(cx, bl);
109+
}
105110

106-
// retrieve the suggestion
107-
let suggestion = if block_snip.ends_with(';') {
108-
block_snip.replace(expr_snip.as_ref(), &format!("{}", expr_sugg.as_str()))
109-
} else {
110-
format!("{};", block_snip.replace(expr_snip.as_ref(), &format!("{}", expr_sugg.as_str())))
111-
};
111+
if_chain! {
112+
if !in_macro(block.span);
113+
if block.expr.is_none();
114+
if let Some(last) = block.stmts.last();
115+
if let StmtKind::Semi(expr) = last.kind;
116+
let t_expr = cx.typeck_results().expr_ty(expr);
117+
if t_expr.is_unit();
118+
then {
119+
// make sure we're also having the semicolon at the end of the expression...
120+
let expr_w_sem = expand_span_to_semicolon(cx, expr.span);
121+
let expr_snip = snippet_with_macro_callsite(cx, expr_w_sem, "..");
122+
let mut expr_sugg = expr_snip.to_string();
123+
expr_sugg.pop();
112124

113-
span_lint_and_sugg(
114-
cx,
115-
SEMICOLON_OUTSIDE_BLOCK,
116-
if block_snip.ends_with(';') {
117-
block_w_sem
118-
} else {
119-
block.span
120-
},
121-
"consider moving the `;` outside the block for consistent formatting",
122-
"put the `;` outside the block",
123-
suggestion,
124-
Applicability::MaybeIncorrect,
125-
);
125+
// and the block
126+
let block_w_sem = expand_span_to_semicolon(cx, block.span);
127+
let mut block_snip: String = snippet_with_macro_callsite(cx, block_w_sem, "..").to_string();
128+
if block_snip.ends_with('\n') {
129+
block_snip.pop();
126130
}
131+
132+
// retrieve the suggestion
133+
let suggestion = if block_snip.ends_with(';') {
134+
block_snip.replace(expr_snip.as_ref(), &format!("{}", expr_sugg.as_str()))
135+
} else {
136+
format!("{};", block_snip.replace(expr_snip.as_ref(), &format!("{}", expr_sugg.as_str())))
137+
};
138+
139+
span_lint_and_sugg(
140+
cx,
141+
SEMICOLON_OUTSIDE_BLOCK,
142+
if block_snip.ends_with(';') {
143+
block_w_sem
144+
} else {
145+
block.span
146+
},
147+
"consider moving the `;` outside the block for consistent formatting",
148+
"put the `;` outside the block",
149+
suggestion,
150+
Applicability::MaybeIncorrect,
151+
);
127152
}
128153
}
129154
}

0 commit comments

Comments
 (0)