From 7192297c2878b22e44ca5bc58f5f7544b33cdf4a Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Sun, 27 Mar 2022 19:58:23 +0200 Subject: [PATCH] additional checks for conditionally compiled code --- clippy_lints/src/unit_like_struct_brackets.rs | 56 +++++++++++++++++-- tests/ui/unit_like_struct_brackets.fixed | 11 +++- tests/ui/unit_like_struct_brackets.rs | 11 +++- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/unit_like_struct_brackets.rs b/clippy_lints/src/unit_like_struct_brackets.rs index c4623df46bda..cb6e2a44afbe 100644 --- a/clippy_lints/src/unit_like_struct_brackets.rs +++ b/clippy_lints/src/unit_like_struct_brackets.rs @@ -1,8 +1,10 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{diagnostics::span_lint_and_sugg, 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 @@ -28,9 +30,9 @@ declare_lint_pass!(UnitLikeStructBrackets => [UNIT_LIKE_STRUCT_BRACKETS]); impl EarlyLintPass for UnitLikeStructBrackets { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::Struct(var_data, _) = &item.kind && has_no_fields(var_data) { - let span_after_ident = item.span.with_lo(item.ident.span.hi()); + let span_after_ident = item.span.with_lo(item.ident.span.hi()); + if let ItemKind::Struct(var_data, _) = &item.kind && has_no_fields(cx, var_data, span_after_ident) { span_lint_and_sugg( cx, UNIT_LIKE_STRUCT_BRACKETS, @@ -44,9 +46,51 @@ impl EarlyLintPass for UnitLikeStructBrackets { } } -fn has_no_fields(var_data: &VariantData) -> bool { +fn has_fields_in_hir(var_data: &VariantData) -> bool { match var_data { - VariantData::Struct(defs, _) | VariantData::Tuple(defs, _) => defs.is_empty(), - VariantData::Unit(_) => false, + VariantData::Struct(defs, _) | VariantData::Tuple(defs, _) => !defs.is_empty(), + VariantData::Unit(_) => true, + } +} + +fn has_no_ident_token(braces_span_str: &str) -> bool { + !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident) +} + +fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool { + if has_fields_in_hir(var_data) { + return false; + } + + // there might still be field declarations hidden from HIR + // (conditionaly 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)); } } diff --git a/tests/ui/unit_like_struct_brackets.fixed b/tests/ui/unit_like_struct_brackets.fixed index e6cf4a0c0b46..78764337ec0f 100644 --- a/tests/ui/unit_like_struct_brackets.fixed +++ b/tests/ui/unit_like_struct_brackets.fixed @@ -5,8 +5,17 @@ pub struct MyEmptyStruct; // should trigger lint struct MyEmptyTupleStruct; // should trigger lint +// should not trigger lint +struct MyCfgStruct { + #[cfg(feature = "thisisneverenabled")] + field: u8, +} + +// should not trigger lint +struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8); + +// should not trigger lint struct MyStruct { - // should not trigger lint field: u8, } struct MyTupleStruct(usize, String); // should not trigger lint diff --git a/tests/ui/unit_like_struct_brackets.rs b/tests/ui/unit_like_struct_brackets.rs index 306e4c207d85..c43f97adfe90 100644 --- a/tests/ui/unit_like_struct_brackets.rs +++ b/tests/ui/unit_like_struct_brackets.rs @@ -5,8 +5,17 @@ pub struct MyEmptyStruct {} // should trigger lint struct MyEmptyTupleStruct(); // should trigger lint +// should not trigger lint +struct MyCfgStruct { + #[cfg(feature = "thisisneverenabled")] + field: u8, +} + +// should not trigger lint +struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8); + +// should not trigger lint struct MyStruct { - // should not trigger lint field: u8, } struct MyTupleStruct(usize, String); // should not trigger lint