Skip to content

Add configuration for semicolon_block lints #10656

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions book/src/lint_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Please use that command to update the file and do not edit it by hand.
| [msrv](#msrv) | `None` |
| [cognitive-complexity-threshold](#cognitive-complexity-threshold) | `25` |
| [disallowed-names](#disallowed-names) | `["foo", "baz", "quux"]` |
| [semicolon-inside-block-ignore-singleline](#semicolon-inside-block-ignore-singleline) | `false` |
| [semicolon-outside-block-ignore-multiline](#semicolon-outside-block-ignore-multiline) | `false` |
| [doc-valid-idents](#doc-valid-idents) | `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` |
| [too-many-arguments-threshold](#too-many-arguments-threshold) | `7` |
| [type-complexity-threshold](#type-complexity-threshold) | `250` |
Expand Down Expand Up @@ -202,6 +204,22 @@ default configuration of Clippy. By default, any configuration will replace the
* [disallowed_names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names)


### semicolon-inside-block-ignore-singleline
Whether to lint only if it's multiline.

**Default Value:** `false` (`bool`)

* [semicolon_inside_block](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block)


### semicolon-outside-block-ignore-multiline
Whether to lint only if it's singleline.

**Default Value:** `false` (`bool`)

* [semicolon_outside_block](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block)


### doc-valid-idents
The list of words this lint should not consider as identifiers needing ticks. The value
`".."` can be used as part of the list to indicate, that the configured values should be appended to the
Expand Down
9 changes: 8 additions & 1 deletion clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock));
let semicolon_inside_block_ignore_singleline = conf.semicolon_inside_block_ignore_singleline;
let semicolon_outside_block_ignore_multiline = conf.semicolon_outside_block_ignore_multiline;
store.register_late_pass(move |_| {
Box::new(semicolon_block::SemicolonBlock::new(
semicolon_inside_block_ignore_singleline,
semicolon_outside_block_ignore_multiline,
))
});
store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck));
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
Expand Down
125 changes: 82 additions & 43 deletions clippy_lints/src/semicolon_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;

declare_clippy_lint! {
Expand Down Expand Up @@ -64,7 +64,78 @@ declare_clippy_lint! {
restriction,
"add a semicolon outside the block"
}
declare_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]);
impl_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]);

#[derive(Copy, Clone)]
pub struct SemicolonBlock {
semicolon_inside_block_ignore_singleline: bool,
semicolon_outside_block_ignore_multiline: bool,
}

impl SemicolonBlock {
pub fn new(semicolon_inside_block_ignore_singleline: bool, semicolon_outside_block_ignore_multiline: bool) -> Self {
Self {
semicolon_inside_block_ignore_singleline,
semicolon_outside_block_ignore_multiline,
}
}

fn semicolon_inside_block(self, cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) {
let insert_span = tail.span.source_callsite().shrink_to_hi();
let remove_span = semi_span.with_lo(block.span.hi());

if self.semicolon_inside_block_ignore_singleline && get_line(cx, remove_span) == get_line(cx, insert_span) {
return;
}

span_lint_and_then(
cx,
SEMICOLON_INSIDE_BLOCK,
semi_span,
"consider moving the `;` inside the block for consistent formatting",
|diag| {
multispan_sugg_with_applicability(
diag,
"put the `;` here",
Applicability::MachineApplicable,
[(remove_span, String::new()), (insert_span, ";".to_owned())],
);
},
);
}

fn semicolon_outside_block(
self,
cx: &LateContext<'_>,
block: &Block<'_>,
tail_stmt_expr: &Expr<'_>,
semi_span: Span,
) {
let insert_span = block.span.with_lo(block.span.hi());
// account for macro calls
let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span);
let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi());

if self.semicolon_outside_block_ignore_multiline && get_line(cx, remove_span) != get_line(cx, insert_span) {
return;
}

span_lint_and_then(
cx,
SEMICOLON_OUTSIDE_BLOCK,
block.span,
"consider moving the `;` outside the block for consistent formatting",
|diag| {
multispan_sugg_with_applicability(
diag,
"put the `;` here",
Applicability::MachineApplicable,
[(remove_span, String::new()), (insert_span, ";".to_owned())],
);
},
);
}
}

impl LateLintPass<'_> for SemicolonBlock {
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
Expand All @@ -83,55 +154,23 @@ impl LateLintPass<'_> for SemicolonBlock {
span,
..
} = stmt else { return };
semicolon_outside_block(cx, block, expr, span);
self.semicolon_outside_block(cx, block, expr, span);
},
StmtKind::Semi(Expr {
kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _),
..
}) if !block.span.from_expansion() => semicolon_inside_block(cx, block, tail, stmt.span),
}) if !block.span.from_expansion() => {
self.semicolon_inside_block(cx, block, tail, stmt.span);
},
_ => (),
}
}
}

fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) {
let insert_span = tail.span.source_callsite().shrink_to_hi();
let remove_span = semi_span.with_lo(block.span.hi());

span_lint_and_then(
cx,
SEMICOLON_INSIDE_BLOCK,
semi_span,
"consider moving the `;` inside the block for consistent formatting",
|diag| {
multispan_sugg_with_applicability(
diag,
"put the `;` here",
Applicability::MachineApplicable,
[(remove_span, String::new()), (insert_span, ";".to_owned())],
);
},
);
}

fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>, semi_span: Span) {
let insert_span = block.span.with_lo(block.span.hi());
// account for macro calls
let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span);
let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi());
fn get_line(cx: &LateContext<'_>, span: Span) -> Option<usize> {
if let Ok(line) = cx.sess().source_map().lookup_line(span.lo()) {
return Some(line.line);
}

span_lint_and_then(
cx,
SEMICOLON_OUTSIDE_BLOCK,
block.span,
"consider moving the `;` outside the block for consistent formatting",
|diag| {
multispan_sugg_with_applicability(
diag,
"put the `;` here",
Applicability::MachineApplicable,
[(remove_span, String::new()), (insert_span, ";".to_owned())],
);
},
);
None
}
8 changes: 8 additions & 0 deletions clippy_lints/src/utils/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,14 @@ define_Conf! {
/// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
/// default configuration of Clippy. By default, any configuration will replace the default value.
(disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
/// Lint: SEMICOLON_INSIDE_BLOCK.
///
/// Whether to lint only if it's multiline.
(semicolon_inside_block_ignore_singleline: bool = false),
/// Lint: SEMICOLON_OUTSIDE_BLOCK.
///
/// Whether to lint only if it's singleline.
(semicolon_outside_block_ignore_multiline: bool = false),
/// Lint: DOC_MARKDOWN.
///
/// The list of words this lint should not consider as identifiers needing ticks. The value
Expand Down
86 changes: 86 additions & 0 deletions tests/ui-toml/semicolon_block/both.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//@run-rustfix
#![allow(
unused,
clippy::unused_unit,
clippy::unnecessary_operation,
clippy::no_effect,
clippy::single_element_loop
)]
#![warn(clippy::semicolon_inside_block)]
#![warn(clippy::semicolon_outside_block)]

macro_rules! m {
(()) => {
()
};
(0) => {{
0
};};
(1) => {{
1;
}};
(2) => {{
2;
}};
}

fn unit_fn_block() {
()
}

#[rustfmt::skip]
fn main() {
{ unit_fn_block() }
unsafe { unit_fn_block() }

{
unit_fn_block()
}

{ unit_fn_block() };
unsafe { unit_fn_block() };

{ unit_fn_block() };
unsafe { unit_fn_block() };

{ unit_fn_block(); };
unsafe { unit_fn_block(); };

{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
};

{ m!(()) };
{ m!(()) };
{ m!(()); };
m!(0);
m!(1);
m!(2);

for _ in [()] {
unit_fn_block();
}
for _ in [()] {
unit_fn_block()
}

let _d = || {
unit_fn_block();
};
let _d = || {
unit_fn_block()
};

{ unit_fn_block(); };

unit_fn_block()
}
86 changes: 86 additions & 0 deletions tests/ui-toml/semicolon_block/both.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//@run-rustfix
#![allow(
unused,
clippy::unused_unit,
clippy::unnecessary_operation,
clippy::no_effect,
clippy::single_element_loop
)]
#![warn(clippy::semicolon_inside_block)]
#![warn(clippy::semicolon_outside_block)]

macro_rules! m {
(()) => {
()
};
(0) => {{
0
};};
(1) => {{
1;
}};
(2) => {{
2;
}};
}

fn unit_fn_block() {
()
}

#[rustfmt::skip]
fn main() {
{ unit_fn_block() }
unsafe { unit_fn_block() }

{
unit_fn_block()
}

{ unit_fn_block() };
unsafe { unit_fn_block() };

{ unit_fn_block(); }
unsafe { unit_fn_block(); }

{ unit_fn_block(); };
unsafe { unit_fn_block(); };

{
unit_fn_block();
unit_fn_block()
};
{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
};

{ m!(()) };
{ m!(()); }
{ m!(()); };
m!(0);
m!(1);
m!(2);

for _ in [()] {
unit_fn_block();
}
for _ in [()] {
unit_fn_block()
}

let _d = || {
unit_fn_block();
};
let _d = || {
unit_fn_block()
};

{ unit_fn_block(); };

unit_fn_block()
}
Loading