]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / empty_structs_with_brackets.rs
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 ";",
48 Applicability::Unspecified);
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 // (conditionally 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 }