Skip to content

Commit 1cec8b3

Browse files
committed
Auto merge of #8594 - FoseFx:unit_like_struct_brackets, r=giraffate
add `empty_structs_with_brackets` <!-- Thank you for making Clippy better! We're collecting our changelog from pull request descriptions. If your PR only includes internal changes, you can just write `changelog: none`. Otherwise, please write a short comment explaining your change. Also, it's helpful for us that the lint name is put into brackets `[]` and backticks `` ` ` ``, e.g. ``[`lint_name`]``. If your PR fixes an issue, you can add "fixes #issue_number" into this PR description. This way the issue will be automatically closed when your PR is merged. If you added a new lint, here's a checklist for things that will be checked during review or continuous integration. - \[ ] Followed [lint naming conventions][lint_naming] - \[ ] Added passing UI tests (including committed `.stderr` file) - \[ ] `cargo test` passes locally - \[ ] Executed `cargo dev update_lints` - \[ ] Added lint documentation - \[ ] Run `cargo dev fmt` [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints Note that you can skip the above if you are just opening a WIP PR in order to get feedback. Delete this line and everything above before opening your PR. -- *Please write a short comment explaining your change (or "none" for internal only changes)* --> Closes #8591 I'm already sorry for the massive diff 😅 changelog: New lint [`empty_structs_with_brackets`]
2 parents 85b88be + 58833e5 commit 1cec8b3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+274
-101
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3263,6 +3263,7 @@ Released 2018-09-13
32633263
[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
32643264
[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
32653265
[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
3266+
[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets
32663267
[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
32673268
[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
32683269
[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt};
2+
use rustc_ast::ast::{Item, ItemKind, VariantData};
3+
use rustc_errors::Applicability;
4+
use rustc_lexer::TokenKind;
5+
use rustc_lint::{EarlyContext, EarlyLintPass};
6+
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use rustc_span::Span;
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
12+
///
13+
/// ### Why is this bad?
14+
/// Empty brackets after a struct declaration can be omitted.
15+
///
16+
/// ### Example
17+
/// ```rust
18+
/// struct Cookie {}
19+
/// ```
20+
/// Use instead:
21+
/// ```rust
22+
/// struct Cookie;
23+
/// ```
24+
#[clippy::version = "1.62.0"]
25+
pub EMPTY_STRUCTS_WITH_BRACKETS,
26+
restriction,
27+
"finds struct declarations with empty brackets"
28+
}
29+
declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]);
30+
31+
impl EarlyLintPass for EmptyStructsWithBrackets {
32+
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
33+
let span_after_ident = item.span.with_lo(item.ident.span.hi());
34+
35+
if let ItemKind::Struct(var_data, _) = &item.kind
36+
&& has_brackets(var_data)
37+
&& has_no_fields(cx, var_data, span_after_ident) {
38+
span_lint_and_then(
39+
cx,
40+
EMPTY_STRUCTS_WITH_BRACKETS,
41+
span_after_ident,
42+
"found empty brackets on struct declaration",
43+
|diagnostic| {
44+
diagnostic.span_suggestion_hidden(
45+
span_after_ident,
46+
"remove the brackets",
47+
";".to_string(),
48+
Applicability::MachineApplicable);
49+
},
50+
);
51+
}
52+
}
53+
}
54+
55+
fn has_no_ident_token(braces_span_str: &str) -> bool {
56+
!rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident)
57+
}
58+
59+
fn has_brackets(var_data: &VariantData) -> bool {
60+
!matches!(var_data, VariantData::Unit(_))
61+
}
62+
63+
fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool {
64+
if !var_data.fields().is_empty() {
65+
return false;
66+
}
67+
68+
// there might still be field declarations hidden from the AST
69+
// (conditionaly compiled code using #[cfg(..)])
70+
71+
let Some(braces_span_str) = snippet_opt(cx, braces_span) else {
72+
return false;
73+
};
74+
75+
has_no_ident_token(braces_span_str.as_ref())
76+
}
77+
78+
#[cfg(test)]
79+
mod unit_test {
80+
use super::*;
81+
82+
#[test]
83+
fn test_has_no_ident_token() {
84+
let input = "{ field: u8 }";
85+
assert!(!has_no_ident_token(input));
86+
87+
let input = "(u8, String);";
88+
assert!(!has_no_ident_token(input));
89+
90+
let input = " {
91+
// test = 5
92+
}
93+
";
94+
assert!(has_no_ident_token(input));
95+
96+
let input = " ();";
97+
assert!(has_no_ident_token(input));
98+
}
99+
}

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ store.register_lints(&[
129129
duration_subsec::DURATION_SUBSEC,
130130
else_if_without_else::ELSE_IF_WITHOUT_ELSE,
131131
empty_enum::EMPTY_ENUM,
132+
empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
132133
entry::MAP_ENTRY,
133134
enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
134135
enum_variants::ENUM_VARIANT_NAMES,

clippy_lints/src/lib.register_restriction.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
1616
LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
1717
LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
1818
LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
19+
LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
1920
LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
2021
LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
2122
LintId::of(exit::EXIT),

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ mod drop_forget_ref;
209209
mod duration_subsec;
210210
mod else_if_without_else;
211211
mod empty_enum;
212+
mod empty_structs_with_brackets;
212213
mod entry;
213214
mod enum_clike;
214215
mod enum_variants;
@@ -869,6 +870,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
869870
})
870871
});
871872
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
873+
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
872874
// add lints here, do not remove this comment, it's used in `new_lint`
873875
}
874876

clippy_lints/src/use_self.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ declare_clippy_lint! {
3434
///
3535
/// ### Example
3636
/// ```rust
37-
/// struct Foo {}
37+
/// struct Foo;
3838
/// impl Foo {
3939
/// fn new() -> Foo {
4040
/// Foo {}
@@ -43,7 +43,7 @@ declare_clippy_lint! {
4343
/// ```
4444
/// could be
4545
/// ```rust
46-
/// struct Foo {}
46+
/// struct Foo;
4747
/// impl Foo {
4848
/// fn new() -> Self {
4949
/// Self {}

tests/ui-toml/struct_excessive_bools/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ struct S {
44
a: bool,
55
}
66

7-
struct Foo {}
7+
struct Foo;
88

99
fn main() {}

tests/ui/case_sensitive_file_extension_comparisons.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use std::string::String;
44

5-
struct TestStruct {}
5+
struct TestStruct;
66

77
impl TestStruct {
88
fn ends_with(self, arg: &str) {}

tests/ui/crashes/ice-2774.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub struct Bar {
88
}
99

1010
#[derive(Eq, PartialEq, Debug, Hash)]
11-
pub struct Foo {}
11+
pub struct Foo;
1212

1313
#[allow(clippy::implicit_hasher)]
1414
// This should not cause a "cannot relate bound region" ICE.

tests/ui/crashes/ice-6179.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#![warn(clippy::use_self)]
55
#![allow(dead_code)]
66

7-
struct Foo {}
7+
struct Foo;
88

99
impl Foo {
1010
fn new() -> Self {

0 commit comments

Comments
 (0)