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 {

tests/ui/crashes/ice-6792.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ trait Trait {
77
fn broken() -> Self::Ty;
88
}
99

10-
struct Foo {}
10+
struct Foo;
1111

1212
impl Trait for Foo {
1313
type Ty = Foo;

tests/ui/crashes/needless_lifetimes_impl_trait.rs

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

44
trait Foo {}
55

6-
struct Bar {}
6+
struct Bar;
77

88
struct Baz<'a> {
99
bar: &'a Bar,

tests/ui/crashes/regressions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ pub fn foo(bar: *const u8) {
66

77
// Regression test for https://github.com/rust-lang/rust-clippy/issues/4917
88
/// <foo
9-
struct A {}
9+
struct A;
1010

1111
fn main() {}

tests/ui/default_numeric_fallback_f64.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ mod enum_ctor {
134134
}
135135

136136
mod method_calls {
137-
struct StructForMethodCallTest {}
137+
struct StructForMethodCallTest;
138138

139139
impl StructForMethodCallTest {
140140
fn concrete_arg(&self, f: f64) {}

tests/ui/default_numeric_fallback_f64.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ mod enum_ctor {
134134
}
135135

136136
mod method_calls {
137-
struct StructForMethodCallTest {}
137+
struct StructForMethodCallTest;
138138

139139
impl StructForMethodCallTest {
140140
fn concrete_arg(&self, f: f64) {}

tests/ui/default_numeric_fallback_i32.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ mod enum_ctor {
133133
}
134134

135135
mod method_calls {
136-
struct StructForMethodCallTest {}
136+
struct StructForMethodCallTest;
137137

138138
impl StructForMethodCallTest {
139139
fn concrete_arg(&self, x: i32) {}

tests/ui/default_numeric_fallback_i32.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ mod enum_ctor {
133133
}
134134

135135
mod method_calls {
136-
struct StructForMethodCallTest {}
136+
struct StructForMethodCallTest;
137137

138138
impl StructForMethodCallTest {
139139
fn concrete_arg(&self, x: i32) {}

tests/ui/drop_forget_copy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::mem::{drop, forget};
55
use std::vec::Vec;
66

77
#[derive(Copy, Clone)]
8-
struct SomeStruct {}
8+
struct SomeStruct;
99

1010
struct AnotherStruct {
1111
x: u8,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// run-rustfix
2+
#![warn(clippy::empty_structs_with_brackets)]
3+
#![allow(dead_code)]
4+
5+
pub struct MyEmptyStruct; // should trigger lint
6+
struct MyEmptyTupleStruct; // should trigger lint
7+
8+
// should not trigger lint
9+
struct MyCfgStruct {
10+
#[cfg(feature = "thisisneverenabled")]
11+
field: u8,
12+
}
13+
14+
// should not trigger lint
15+
struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8);
16+
17+
// should not trigger lint
18+
struct MyStruct {
19+
field: u8,
20+
}
21+
struct MyTupleStruct(usize, String); // should not trigger lint
22+
struct MySingleTupleStruct(usize); // should not trigger lint
23+
struct MyUnitLikeStruct; // should not trigger lint
24+
25+
fn main() {}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// run-rustfix
2+
#![warn(clippy::empty_structs_with_brackets)]
3+
#![allow(dead_code)]
4+
5+
pub struct MyEmptyStruct {} // should trigger lint
6+
struct MyEmptyTupleStruct(); // should trigger lint
7+
8+
// should not trigger lint
9+
struct MyCfgStruct {
10+
#[cfg(feature = "thisisneverenabled")]
11+
field: u8,
12+
}
13+
14+
// should not trigger lint
15+
struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8);
16+
17+
// should not trigger lint
18+
struct MyStruct {
19+
field: u8,
20+
}
21+
struct MyTupleStruct(usize, String); // should not trigger lint
22+
struct MySingleTupleStruct(usize); // should not trigger lint
23+
struct MyUnitLikeStruct; // should not trigger lint
24+
25+
fn main() {}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: found empty brackets on struct declaration
2+
--> $DIR/empty_structs_with_brackets.rs:5:25
3+
|
4+
LL | pub struct MyEmptyStruct {} // should trigger lint
5+
| ^^^
6+
|
7+
= note: `-D clippy::empty-structs-with-brackets` implied by `-D warnings`
8+
= help: remove the brackets
9+
10+
error: found empty brackets on struct declaration
11+
--> $DIR/empty_structs_with_brackets.rs:6:26
12+
|
13+
LL | struct MyEmptyTupleStruct(); // should trigger lint
14+
| ^^^
15+
|
16+
= help: remove the brackets
17+
18+
error: aborting due to 2 previous errors
19+

tests/ui/fn_params_excessive_bools.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fn h(_: bool, _: bool, _: bool) {}
2020
fn e(_: S, _: S, _: Box<S>, _: Vec<u32>) {}
2121
fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
2222

23-
struct S {}
23+
struct S;
2424
trait Trait {
2525
fn f(_: bool, _: bool, _: bool, _: bool);
2626
fn g(_: bool, _: bool, _: bool, _: Vec<u32>);

tests/ui/implicit_clone.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ where
3030
}
3131

3232
#[derive(Copy, Clone)]
33-
struct Kitten {}
33+
struct Kitten;
3434
impl Kitten {
3535
// badly named method
3636
fn to_vec(self) -> Kitten {
@@ -44,7 +44,7 @@ impl Borrow<BorrowedKitten> for Kitten {
4444
}
4545
}
4646

47-
struct BorrowedKitten {}
47+
struct BorrowedKitten;
4848
impl ToOwned for BorrowedKitten {
4949
type Owned = Kitten;
5050
fn to_owned(&self) -> Kitten {

tests/ui/iter_nth_zero.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![warn(clippy::iter_nth_zero)]
44
use std::collections::HashSet;
55

6-
struct Foo {}
6+
struct Foo;
77

88
impl Foo {
99
fn nth(&self, index: usize) -> usize {

tests/ui/iter_nth_zero.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![warn(clippy::iter_nth_zero)]
44
use std::collections::HashSet;
55

6-
struct Foo {}
6+
struct Foo;
77

88
impl Foo {
99
fn nth(&self, index: usize) -> usize {

tests/ui/large_types_passed_by_value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub trait PubLargeTypeDevourer {
3737
fn devoure_array_in_public(&self, array: [u8; 6666]);
3838
}
3939

40-
struct S {}
40+
struct S;
4141
impl LargeTypeDevourer for S {
4242
fn devoure_array(&self, array: [u8; 6666]) {
4343
todo!();

tests/ui/let_and_return.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ mod no_lint_if_stmt_borrows {
8888
ret
8989
}
9090

91-
struct Bar {}
91+
struct Bar;
9292

9393
impl Bar {
9494
fn new() -> Self {

0 commit comments

Comments
 (0)