Skip to content

Commit d0a8c69

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

10 files changed

+547
-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

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

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)