Skip to content

Commit f1a5d8e

Browse files
committed
Add semicolon-outside/inside-block lints
1 parent c4fbe54 commit f1a5d8e

10 files changed

+539
-0
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -4352,6 +4352,8 @@ Released 2018-09-13
43524352
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
43534353
[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
43544354
[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
4355+
[`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block
4356+
[`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block
43554357
[`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix
43564358
[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
43574359
[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse

clippy_lints/src/declared_lints.rs

+2
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
522522
crate::returns::NEEDLESS_RETURN_INFO,
523523
crate::same_name_method::SAME_NAME_METHOD_INFO,
524524
crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO,
525+
crate::semicolon_block::SEMICOLON_INSIDE_BLOCK_INFO,
526+
crate::semicolon_block::SEMICOLON_OUTSIDE_BLOCK_INFO,
525527
crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO,
526528
crate::serde_api::SERDE_API_MISUSE_INFO,
527529
crate::shadow::SHADOW_REUSE_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ mod return_self_not_must_use;
257257
mod returns;
258258
mod same_name_method;
259259
mod self_named_constructors;
260+
mod semicolon_block;
260261
mod semicolon_if_nothing_returned;
261262
mod serde_api;
262263
mod shadow;
@@ -921,6 +922,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
921922
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
922923
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
923924
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv)));
925+
store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock));
924926
// add lints here, do not remove this comment, it's used in `new_lint`
925927
}
926928

clippy_lints/src/semicolon_block.rs

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet_with_macro_callsite;
3+
use clippy_utils::{get_parent_expr_for_hir, get_parent_node};
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Block, Expr, ExprKind, Node, Stmt, StmtKind};
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
///
12+
/// For () returning expressions, check that the semicolon is inside the block.
13+
///
14+
/// ### Why is this bad?
15+
///
16+
/// For consistency it's best to have the semicolon inside/outside the block. Either way is fine and this lint suggests inside the block.
17+
/// Take a look at `semicolon_outside_block` for the other alternative.
18+
///
19+
/// ### Example
20+
///
21+
/// ```rust
22+
/// unsafe { f(x) };
23+
/// ```
24+
/// Use instead:
25+
/// ```rust
26+
/// unsafe { f(x); }
27+
/// ```
28+
#[clippy::version = "1.66.0"]
29+
pub SEMICOLON_INSIDE_BLOCK,
30+
restriction,
31+
"add a semicolon inside the block"
32+
}
33+
declare_clippy_lint! {
34+
/// ### What it does
35+
///
36+
/// For () returning expressions, check that the semicolon is outside the block.
37+
///
38+
/// ### Why is this bad?
39+
///
40+
/// For consistency it's best to have the semicolon inside/outside the block. Either way is fine and this lint suggests outside the block.
41+
/// Take a look at `semicolon_inside_block` for the other alternative.
42+
///
43+
/// ### Example
44+
///
45+
/// ```rust
46+
/// unsafe { f(x); }
47+
/// ```
48+
/// Use instead:
49+
/// ```rust
50+
/// unsafe { f(x) };
51+
/// ```
52+
#[clippy::version = "1.66.0"]
53+
pub SEMICOLON_OUTSIDE_BLOCK,
54+
restriction,
55+
"add a semicolon outside the block"
56+
}
57+
declare_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]);
58+
59+
impl LateLintPass<'_> for SemicolonBlock {
60+
fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) {
61+
semicolon_inside_block(cx, block);
62+
semicolon_outside_block(cx, block);
63+
}
64+
}
65+
66+
fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>) {
67+
if !block.span.from_expansion()
68+
&& let Some(tail) = block.expr
69+
&& let Some(block_expr @ Expr { kind: ExprKind::Block(_, _), ..}) = get_parent_expr_for_hir(cx, block.hir_id)
70+
&& let Some(Node::Stmt(Stmt { kind: StmtKind::Semi(_), span, .. })) = get_parent_node(cx.tcx, block_expr.hir_id)
71+
{
72+
let expr_snip = snippet_with_macro_callsite(cx, tail.span, "..");
73+
74+
let mut suggestion: String = snippet_with_macro_callsite(cx, block.span, "..").to_string();
75+
76+
if let Some((expr_offset, _)) = suggestion.rmatch_indices(&*expr_snip).next() {
77+
suggestion.insert(expr_offset + expr_snip.len(), ';');
78+
} else {
79+
return;
80+
}
81+
82+
span_lint_and_sugg(
83+
cx,
84+
SEMICOLON_INSIDE_BLOCK,
85+
*span,
86+
"consider moving the `;` inside the block for consistent formatting",
87+
"put the `;` here",
88+
suggestion,
89+
Applicability::MaybeIncorrect,
90+
);
91+
}
92+
}
93+
94+
fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>) {
95+
if !block.span.from_expansion()
96+
&& block.expr.is_none()
97+
&& let [.., Stmt { kind: StmtKind::Semi(expr), .. }] = block.stmts
98+
&& let Some(block_expr @ Expr { kind: ExprKind::Block(_, _), ..}) = get_parent_expr_for_hir(cx,block.hir_id)
99+
&& let Some(Node::Stmt(Stmt { kind: StmtKind::Expr(_), .. })) = get_parent_node(cx.tcx, block_expr.hir_id) {
100+
let expr_snip = snippet_with_macro_callsite(cx, expr.span, "..");
101+
102+
let mut suggestion: String = snippet_with_macro_callsite(cx, block.span, "..").to_string();
103+
104+
if let Some((expr_offset, _)) = suggestion.rmatch_indices(&*expr_snip).next()
105+
&& let Some(semi_offset) = suggestion[expr_offset + expr_snip.len()..].find(';') {
106+
suggestion.remove(expr_offset + expr_snip.len() + semi_offset);
107+
} else {
108+
return;
109+
}
110+
111+
suggestion.push(';');
112+
113+
span_lint_and_sugg(
114+
cx,
115+
SEMICOLON_OUTSIDE_BLOCK,
116+
block.span,
117+
"consider moving the `;` outside the block for consistent formatting",
118+
"put the `;` outside the block",
119+
suggestion,
120+
Applicability::MaybeIncorrect,
121+
);
122+
}
123+
}

tests/ui/semicolon_inside_block.fixed

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// run-rustfix
2+
#![allow(
3+
unused,
4+
clippy::unused_unit,
5+
clippy::unnecessary_operation,
6+
clippy::no_effect,
7+
clippy::single_element_loop
8+
)]
9+
#![warn(clippy::semicolon_inside_block)]
10+
11+
macro_rules! m {
12+
(()) => {
13+
()
14+
};
15+
(0) => {{
16+
0
17+
};};
18+
(1) => {{
19+
1;
20+
}};
21+
(2) => {{
22+
2;
23+
}};
24+
}
25+
26+
fn unit_fn_block() {
27+
()
28+
}
29+
30+
#[rustfmt::skip]
31+
fn main() {
32+
{ unit_fn_block() }
33+
unsafe { unit_fn_block() }
34+
35+
{
36+
unit_fn_block()
37+
}
38+
39+
{ unit_fn_block(); }
40+
unsafe { unit_fn_block(); }
41+
42+
{ unit_fn_block(); }
43+
unsafe { unit_fn_block(); }
44+
45+
{ unit_fn_block(); };
46+
unsafe { unit_fn_block(); };
47+
48+
{
49+
unit_fn_block();
50+
unit_fn_block();
51+
}
52+
{
53+
unit_fn_block();
54+
unit_fn_block();
55+
}
56+
{
57+
unit_fn_block();
58+
unit_fn_block();
59+
};
60+
61+
{ m!(()); }
62+
{ m!(()); }
63+
{ m!(()); };
64+
m!(0);
65+
m!(1);
66+
m!(2);
67+
68+
for _ in [()] {
69+
unit_fn_block();
70+
}
71+
for _ in [()] {
72+
unit_fn_block()
73+
}
74+
75+
let _d = || {
76+
unit_fn_block();
77+
};
78+
let _d = || {
79+
unit_fn_block()
80+
};
81+
82+
unit_fn_block()
83+
}

tests/ui/semicolon_inside_block.rs

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// run-rustfix
2+
#![allow(
3+
unused,
4+
clippy::unused_unit,
5+
clippy::unnecessary_operation,
6+
clippy::no_effect,
7+
clippy::single_element_loop
8+
)]
9+
#![warn(clippy::semicolon_inside_block)]
10+
11+
macro_rules! m {
12+
(()) => {
13+
()
14+
};
15+
(0) => {{
16+
0
17+
};};
18+
(1) => {{
19+
1;
20+
}};
21+
(2) => {{
22+
2;
23+
}};
24+
}
25+
26+
fn unit_fn_block() {
27+
()
28+
}
29+
30+
#[rustfmt::skip]
31+
fn main() {
32+
{ unit_fn_block() }
33+
unsafe { unit_fn_block() }
34+
35+
{
36+
unit_fn_block()
37+
}
38+
39+
{ unit_fn_block() };
40+
unsafe { unit_fn_block() };
41+
42+
{ unit_fn_block(); }
43+
unsafe { unit_fn_block(); }
44+
45+
{ unit_fn_block(); };
46+
unsafe { unit_fn_block(); };
47+
48+
{
49+
unit_fn_block();
50+
unit_fn_block()
51+
};
52+
{
53+
unit_fn_block();
54+
unit_fn_block();
55+
}
56+
{
57+
unit_fn_block();
58+
unit_fn_block();
59+
};
60+
61+
{ m!(()) };
62+
{ m!(()); }
63+
{ m!(()); };
64+
m!(0);
65+
m!(1);
66+
m!(2);
67+
68+
for _ in [()] {
69+
unit_fn_block();
70+
}
71+
for _ in [()] {
72+
unit_fn_block()
73+
}
74+
75+
let _d = || {
76+
unit_fn_block();
77+
};
78+
let _d = || {
79+
unit_fn_block()
80+
};
81+
82+
unit_fn_block()
83+
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error: consider moving the `;` inside the block for consistent formatting
2+
--> $DIR/semicolon_inside_block.rs:39:5
3+
|
4+
LL | { unit_fn_block() };
5+
| ^^^^^^^^^^^^^^^^^^^^ help: put the `;` here: `{ unit_fn_block(); }`
6+
|
7+
= note: `-D clippy::semicolon-inside-block` implied by `-D warnings`
8+
9+
error: consider moving the `;` inside the block for consistent formatting
10+
--> $DIR/semicolon_inside_block.rs:40:5
11+
|
12+
LL | unsafe { unit_fn_block() };
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: put the `;` here: `unsafe { unit_fn_block(); }`
14+
15+
error: consider moving the `;` inside the block for consistent formatting
16+
--> $DIR/semicolon_inside_block.rs:48:5
17+
|
18+
LL | / {
19+
LL | | unit_fn_block();
20+
LL | | unit_fn_block()
21+
LL | | };
22+
| |______^
23+
|
24+
help: put the `;` here
25+
|
26+
LL ~ {
27+
LL + unit_fn_block();
28+
LL + unit_fn_block();
29+
LL + }
30+
|
31+
32+
error: consider moving the `;` inside the block for consistent formatting
33+
--> $DIR/semicolon_inside_block.rs:61:5
34+
|
35+
LL | { m!(()) };
36+
| ^^^^^^^^^^^ help: put the `;` here: `{ m!(()); }`
37+
38+
error: aborting due to 4 previous errors
39+

0 commit comments

Comments
 (0)