mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-12-22 19:13:29 +00:00
99 lines
2.9 KiB
Rust
99 lines
2.9 KiB
Rust
use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt};
|
|
use rustc_ast::ast::{Item, ItemKind, VariantData};
|
|
use rustc_errors::Applicability;
|
|
use rustc_lexer::TokenKind;
|
|
use rustc_lint::{EarlyContext, EarlyLintPass};
|
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
|
use rustc_span::Span;
|
|
|
|
declare_clippy_lint! {
|
|
/// ### What it does
|
|
/// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
|
|
///
|
|
/// ### Why is this bad?
|
|
/// Empty brackets after a struct declaration can be omitted.
|
|
///
|
|
/// ### Example
|
|
/// ```rust
|
|
/// struct Cookie {}
|
|
/// ```
|
|
/// Use instead:
|
|
/// ```rust
|
|
/// struct Cookie;
|
|
/// ```
|
|
#[clippy::version = "1.62.0"]
|
|
pub EMPTY_STRUCTS_WITH_BRACKETS,
|
|
restriction,
|
|
"finds struct declarations with empty brackets"
|
|
}
|
|
declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]);
|
|
|
|
impl EarlyLintPass for EmptyStructsWithBrackets {
|
|
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
|
let span_after_ident = item.span.with_lo(item.ident.span.hi());
|
|
|
|
if let ItemKind::Struct(var_data, _) = &item.kind
|
|
&& has_brackets(var_data)
|
|
&& has_no_fields(cx, var_data, span_after_ident) {
|
|
span_lint_and_then(
|
|
cx,
|
|
EMPTY_STRUCTS_WITH_BRACKETS,
|
|
span_after_ident,
|
|
"found empty brackets on struct declaration",
|
|
|diagnostic| {
|
|
diagnostic.span_suggestion_hidden(
|
|
span_after_ident,
|
|
"remove the brackets",
|
|
";",
|
|
Applicability::MachineApplicable);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn has_no_ident_token(braces_span_str: &str) -> bool {
|
|
!rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident)
|
|
}
|
|
|
|
fn has_brackets(var_data: &VariantData) -> bool {
|
|
!matches!(var_data, VariantData::Unit(_))
|
|
}
|
|
|
|
fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool {
|
|
if !var_data.fields().is_empty() {
|
|
return false;
|
|
}
|
|
|
|
// there might still be field declarations hidden from the AST
|
|
// (conditionally compiled code using #[cfg(..)])
|
|
|
|
let Some(braces_span_str) = snippet_opt(cx, braces_span) else {
|
|
return false;
|
|
};
|
|
|
|
has_no_ident_token(braces_span_str.as_ref())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod unit_test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_has_no_ident_token() {
|
|
let input = "{ field: u8 }";
|
|
assert!(!has_no_ident_token(input));
|
|
|
|
let input = "(u8, String);";
|
|
assert!(!has_no_ident_token(input));
|
|
|
|
let input = " {
|
|
// test = 5
|
|
}
|
|
";
|
|
assert!(has_no_ident_token(input));
|
|
|
|
let input = " ();";
|
|
assert!(has_no_ident_token(input));
|
|
}
|
|
}
|