diff --git a/clippy_lints/src/empty_with_brackets.rs b/clippy_lints/src/empty_with_brackets.rs index e7230ebf8cba..f16320cd5874 100644 --- a/clippy_lints/src/empty_with_brackets.rs +++ b/clippy_lints/src/empty_with_brackets.rs @@ -1,16 +1,13 @@ -use clippy_utils::attrs::span_contains_cfg; -use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; -use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::Applicability; -use rustc_hir::def::CtorOf; -use rustc_hir::def::DefKind::Ctor; -use rustc_hir::def::Res::Def; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{IntoSpan, SpanRangeExt, walk_span_to_context}; +use core::mem; +use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node, Path, QPath, Variant, VariantData}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TyCtxt; +use rustc_hir::{Expr, ExprKind, Generics, Item, ItemKind, QPath, Variant, VariantData}; +use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_session::impl_lint_pass; -use rustc_span::Span; +use rustc_span::{DUMMY_SP, Span}; declare_clippy_lint! { /// ### What it does @@ -75,219 +72,242 @@ declare_clippy_lint! { "finds enum variants with empty brackets" } -#[derive(Debug)] -enum Usage { - Unused { redundant_use_sites: Vec }, - Used, - NoDefinition { redundant_use_sites: Vec }, +struct TupleDef { + did: LocalDefId, + lint_sp: Span, + edit_sp: Span, + needs_semi: bool, + lint: &'static Lint, + msg: &'static str, +} + +struct TupleUse { + did: LocalDefId, + /// The span of the call parenthesis, or `DUMMY_SP` if this use can't be changed. + edit_sp: Span, } #[derive(Default)] pub struct EmptyWithBrackets { - // Value holds `Usage::Used` if the empty tuple variant was used as a function - empty_tuple_enum_variants: FxIndexMap, + tuple_defs: Vec, + tuple_uses: Vec, + // Used to skip over constructor path expressions when they've already been seen as a + // call expression. + skip_next_expr: bool, } impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]); -impl LateLintPass<'_> for EmptyWithBrackets { +impl<'tcx> LateLintPass<'tcx> for EmptyWithBrackets { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - // FIXME: handle `struct $name {}` - if let ItemKind::Struct(ident, generics, var_data) = &item.kind - && !item.span.from_expansion() - && !ident.span.from_expansion() - && has_brackets(var_data) - && let span_after_ident = item.span.with_lo(generics.span.hi()) - && has_no_fields(cx, var_data, span_after_ident) - { - span_lint_and_then( + if let ItemKind::Struct(ident, generics, ref var_data) = item.kind { + self.check_def( cx, + var_data, + item.owner_id.def_id, + item.span, + ident.span, + Some(generics), 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::Unspecified, - ); - }, + "non-unit struct contains no fields", ); } } fn check_variant(&mut self, cx: &LateContext<'_>, variant: &Variant<'_>) { - // FIXME: handle `$name {}` - if !variant.span.from_expansion() - && !variant.ident.span.from_expansion() - && let span_after_ident = variant.span.with_lo(variant.ident.span.hi()) - && has_no_fields(cx, &variant.data, span_after_ident) - { - match variant.data { - VariantData::Struct { .. } => { - // Empty struct variants can be linted immediately - span_lint_and_then( - cx, - EMPTY_ENUM_VARIANTS_WITH_BRACKETS, - span_after_ident, - "enum variant has empty brackets", - |diagnostic| { - diagnostic.span_suggestion_hidden( - span_after_ident, - "remove the brackets", - "", - Applicability::MaybeIncorrect, - ); - }, - ); - }, - VariantData::Tuple(.., local_def_id) => { - // Don't lint reachable tuple enums - if cx.effective_visibilities.is_reachable(variant.def_id) { - return; - } - if let Some(entry) = self.empty_tuple_enum_variants.get_mut(&local_def_id) { - // empty_tuple_enum_variants contains Usage::NoDefinition if the variant was called before the - // definition was encountered. Now that there's a definition, convert it - // to Usage::Unused. - if let Usage::NoDefinition { redundant_use_sites } = entry { - *entry = Usage::Unused { - redundant_use_sites: redundant_use_sites.clone(), - }; - } - } else { - self.empty_tuple_enum_variants.insert( - local_def_id, - Usage::Unused { - redundant_use_sites: vec![], - }, - ); - } - }, - VariantData::Unit(..) => {}, - } - } + self.check_def( + cx, + &variant.data, + variant.def_id, + variant.span, + variant.ident.span, + None, + EMPTY_ENUM_VARIANTS_WITH_BRACKETS, + "non-unit variant contains no fields", + ); } - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let Some(def_id) = check_expr_for_enum_as_function(expr) { - if let Some(parentheses_span) = call_parentheses_span(cx.tcx, expr) { - // Do not count expressions from macro expansion as a redundant use site. - if expr.span.from_expansion() { - return; - } - match self.empty_tuple_enum_variants.get_mut(&def_id) { - Some( - &mut (Usage::Unused { - ref mut redundant_use_sites, - } - | Usage::NoDefinition { - ref mut redundant_use_sites, - }), - ) => { - redundant_use_sites.push(parentheses_span); - }, - None => { - // The variant isn't in the IndexMap which means its definition wasn't encountered yet. - self.empty_tuple_enum_variants.insert( - def_id, - Usage::NoDefinition { - redundant_use_sites: vec![parentheses_span], - }, - ); - }, - _ => {}, - } - } else { - // The parentheses are not redundant. - self.empty_tuple_enum_variants.insert(def_id, Usage::Used); - } + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + match e.kind { + ExprKind::Call(callee, []) + if let ExprKind::Path(QPath::Resolved(_, path)) = callee.kind + && let Res::Def(DefKind::Ctor(_, CtorKind::Fn), did) = path.res + && let Some(did) = did.as_local() + && let e_data = e.span.data() + && e_data.ctxt.is_root() => + { + // The next visited expression with be `callee`. Make sure we don't add it + // as a tuple use. + self.skip_next_expr = true; + let edit_sp = if let Some(callee_sp) = walk_span_to_context(callee.span, e_data.ctxt) + && let edit_range = (callee_sp.hi()..e_data.hi) + && let Some(edit_range) = edit_range.map_range(cx, |_, src, range| { + let src = src + .get(range.clone())? + .trim_start() + .strip_prefix('(')? + .trim_start() + .strip_prefix(')')?; + // Any trailing closing parens are from the call expression being wrapped in parens. + let len = src.len(); + src.trim_start_matches(|c: char| c == ')' || c.is_whitespace()) + .is_empty() + .then_some(range.start..range.end - len) + }) { + edit_range.into_span() + } else { + DUMMY_SP + }; + self.tuple_uses.push(TupleUse { + did: cx.tcx.local_parent(did), + edit_sp, + }); + }, + + ExprKind::Path(QPath::Resolved(_, path)) + if let Res::Def(DefKind::Ctor(_, CtorKind::Fn), did) = path.res + && let Some(did) = did.as_local() + && !mem::replace(&mut self.skip_next_expr, false) + && cx.tcx.fn_sig(did).skip_binder().skip_binder().inputs_and_output.len() == 1 => + { + self.tuple_uses.push(TupleUse { + did: cx.tcx.local_parent(did), + edit_sp: DUMMY_SP, + }); + }, + + _ => {}, } } - fn check_crate_post(&mut self, cx: &LateContext<'_>) { - for (local_def_id, usage) in &self.empty_tuple_enum_variants { - // Ignore all variants with Usage::Used or Usage::NoDefinition - let Usage::Unused { redundant_use_sites } = usage else { - continue; - }; - // Attempt to fetch the Variant from LocalDefId. - let Node::Variant(variant) = cx.tcx.hir_node( - cx.tcx - .local_def_id_to_hir_id(cx.tcx.parent(local_def_id.to_def_id()).expect_local()), - ) else { - continue; - }; - // Span of the parentheses in variant definition - let span = variant.span.with_lo(variant.ident.span.hi()); - span_lint_hir_and_then( - cx, - EMPTY_ENUM_VARIANTS_WITH_BRACKETS, - variant.hir_id, - span, - "enum variant has empty brackets", - |diagnostic| { - if redundant_use_sites.is_empty() { - // If there's no redundant use sites, the definition is the only place to modify. - diagnostic.span_suggestion_hidden( - span, - "remove the brackets", - "", - Applicability::MaybeIncorrect, - ); - } else { - let mut parentheses_spans: Vec<_> = - redundant_use_sites.iter().map(|span| (*span, String::new())).collect(); - parentheses_spans.push((span, String::new())); - diagnostic.multipart_suggestion( - "remove the brackets", - parentheses_spans, - Applicability::MaybeIncorrect, - ); - } - }, - ); + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + if self.tuple_defs.is_empty() { + return; } - } -} + self.tuple_uses.sort_unstable_by_key(|x| x.did.local_def_index); -fn has_brackets(var_data: &VariantData<'_>) -> bool { - !matches!(var_data, VariantData::Unit(..)) -} + let mut replacements = Vec::with_capacity(16); + 'def_loop: for def in &self.tuple_defs { + replacements.clear(); + replacements.push(( + def.edit_sp, + if def.needs_semi { + String::from(";") + } else { + String::new() + }, + )); -fn has_no_fields(cx: &LateContext<'_>, var_data: &VariantData<'_>, braces_span: Span) -> bool { - var_data.fields().is_empty() && - // there might still be field declarations hidden from the AST - // (conditionally compiled code using #[cfg(..)]) - !span_contains_cfg(cx, braces_span) -} + let start = self + .tuple_uses + .partition_point(|x| x.did.local_def_index < def.did.local_def_index); + for u in &self.tuple_uses[start..] { + if u.did != def.did { + break; + } + if u.edit_sp == DUMMY_SP { + continue 'def_loop; + } + replacements.push((u.edit_sp, String::new())); + } -// If expression HIR ID and callee HIR ID are same, returns the span of the parentheses, else, -// returns None. -fn call_parentheses_span(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { - if let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) - && let ExprKind::Call(callee, ..) = parent.kind - && callee.hir_id == expr.hir_id - { - Some(parent.span.with_lo(expr.span.hi())) - } else { - None + span_lint_and_then(cx, def.lint, def.lint_sp, def.msg, |diag| { + diag.multipart_suggestion_with_style( + "remove the parenthesis", + replacements.clone(), + Applicability::MachineApplicable, + SuggestionStyle::HideCodeAlways, + ); + }); + } } } -// Returns the LocalDefId of the variant being called as a function if it exists. -fn check_expr_for_enum_as_function(expr: &Expr<'_>) -> Option { - if let ExprKind::Path(QPath::Resolved( - _, - Path { - res: Def(Ctor(CtorOf::Variant, _), def_id), - .. - }, - )) = expr.kind - { - def_id.as_local() - } else { - None +impl EmptyWithBrackets { + #[expect(clippy::too_many_arguments)] + fn check_def( + &mut self, + cx: &LateContext<'_>, + data: &VariantData<'_>, + did: LocalDefId, + item_sp: Span, + name_sp: Span, + struct_generics: Option<&Generics<'_>>, + lint: &'static Lint, + msg: &'static str, + ) { + // Start by normalizing the various variant forms into: + // * Does the replacement need a semicolon + // * Which brace characters to use + // * The span that should only contain braces and whitespace. + let (needs_semi, start_char, end_char, start_sp, end_pos, ctxt) = match *data { + VariantData::Struct { fields: [], .. } => { + if let Some(g) = struct_generics { + // `struct Name where {}` + let data = item_sp.data(); + (true, '{', '}', g.where_clause_span, data.hi, data.ctxt) + } else { + // `Variant {}` + let data = item_sp.data(); + (false, '{', '}', name_sp, data.hi, data.ctxt) + } + }, + VariantData::Tuple([], _, _) => { + if let Some(g) = struct_generics { + // `struct Name() where;` + let data = g.where_clause_span.data(); + (false, '(', ')', g.span, data.lo, data.ctxt) + } else { + // `Variant()` + let data = item_sp.data(); + (false, '(', ')', name_sp, data.hi, data.ctxt) + } + }, + VariantData::Struct { .. } | VariantData::Tuple(..) | VariantData::Unit(..) => return, + }; + + let start_data = start_sp.data(); + if start_data.ctxt == ctxt + && !ctxt.in_external_macro(cx.tcx.sess.source_map()) + && let Some(lint_range) = (start_data.hi..end_pos).clone().map_range(cx, |_, src, range| { + // Check that the source text only contains whitespace and the braces. + // Anything else (e.g. comments, cfgs, macro vars, etc.) should stop this + // lint from triggering. + let src = src.get(range.clone())?; + let src2 = src.trim_start(); + let start = range.start + (src.len() - src2.len()); + let src3 = src2.trim_end(); + let end = range.end - (src2.len() - src3.len()); + src3.strip_prefix(start_char)? + .strip_suffix(end_char)? + .trim_start() + .is_empty() + .then_some(start..end) + }) + { + // Don't edit the out any trailing whitespace to avoid problems with + // where clauses. + let edit_sp = Span::new(start_data.hi, lint_range.end, ctxt, None); + let lint_sp = Span::new(lint_range.start, lint_range.end, ctxt, None); + if start_char == '{' { + span_lint_and_then(cx, lint, lint_sp, msg, |diagnostic| { + diagnostic.span_suggestion_hidden( + edit_sp, + "remove the braces", + if needs_semi { String::from(";") } else { String::new() }, + Applicability::MaybeIncorrect, + ); + }); + } else if !cx.effective_visibilities.is_exported(did) { + self.tuple_defs.push(TupleDef { + did, + lint_sp, + edit_sp, + needs_semi, + lint, + msg, + }); + } + } } } diff --git a/tests/ui/empty_enum_variants_with_brackets.fixed b/tests/ui/empty_enum_variants_with_brackets.fixed index abdf6ca5cb61..4d1e62796175 100644 --- a/tests/ui/empty_enum_variants_with_brackets.fixed +++ b/tests/ui/empty_enum_variants_with_brackets.fixed @@ -1,34 +1,61 @@ -#![warn(clippy::empty_enum_variants_with_brackets)] +//@aux-build:proc_macros.rs +#![deny(clippy::empty_enum_variants_with_brackets)] #![allow(dead_code)] +extern crate proc_macros; +use proc_macros::{external, with_span}; + pub enum PublicTestEnum { - NonEmptyBraces { x: i32, y: i32 }, // No error - NonEmptyParentheses(i32, i32), // No error - EmptyBraces, - //~^ empty_enum_variants_with_brackets - EmptyParentheses(), // No error as enum is pub + NonEmptyBraces { x: i32, y: i32 }, + NonEmptyParentheses(i32, i32), + EmptyBraces, //~ empty_enum_variants_with_brackets + EmptyParentheses(), } enum TestEnum { - NonEmptyBraces { x: i32, y: i32 }, // No error - NonEmptyParentheses(i32, i32), // No error - EmptyBraces, - //~^ empty_enum_variants_with_brackets - EmptyParentheses, - //~^ empty_enum_variants_with_brackets - AnotherEnum, // No error + NonEmptyBraces { x: i32, y: i32 }, + NonEmptyParentheses(i32, i32), + EmptyBraces, //~ empty_enum_variants_with_brackets + EmptyParentheses, //~ empty_enum_variants_with_brackets + AnotherEnum, +} + +enum TestEnumWithFeatures { + NonEmptyBraces { + #[cfg(feature = "thisisneverenabled")] + x: i32, + }, + NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), +} + +external! { + enum External { + Foo {}, + } +} + +with_span! { + span + enum ProcMacro { + Foo(), + } +} + +macro_rules! m { + ($($ty:ty),*) => { + enum Macro { + Foo($($ty),*), + } + } } mod issue12551 { + // Check if the variant is passed as an argument. enum EvenOdd { - // Used as functions -> no error Even(), Odd(), - // Not used as a function - Unknown, - //~^ empty_enum_variants_with_brackets + Unknown, //~ empty_enum_variants_with_brackets } - fn even_odd(x: i32) -> EvenOdd { (x % 2 == 0).then(EvenOdd::Even).unwrap_or_else(EvenOdd::Odd) } @@ -38,68 +65,30 @@ mod issue12551 { .then(NaturalOrNot::Natural) .unwrap_or_else(NaturalOrNot::NotNatural) } - enum NaturalOrNot { - // Used as functions -> no error Natural(), NotNatural(), - // Not used as a function - Unknown, - //~^ empty_enum_variants_with_brackets + Unknown, //~ empty_enum_variants_with_brackets } + // Check that call sites are fixed. enum RedundantParenthesesFunctionCall { - // Used as a function call but with redundant parentheses - Parentheses, - //~^ empty_enum_variants_with_brackets - // Not used as a function + Parentheses, //~ empty_enum_variants_with_brackets NoParentheses, } - - #[allow(clippy::no_effect)] fn redundant_parentheses_function_call() { - // The parentheses in the below line are redundant. - RedundantParenthesesFunctionCall::Parentheses; - RedundantParenthesesFunctionCall::NoParentheses; + let _ = RedundantParenthesesFunctionCall::Parentheses; + let _ = RedundantParenthesesFunctionCall::NoParentheses; } - // Same test as above but with usage of the enum occurring before the definition. - #[allow(clippy::no_effect)] fn redundant_parentheses_function_call_2() { - // The parentheses in the below line are redundant. - RedundantParenthesesFunctionCall2::Parentheses; - RedundantParenthesesFunctionCall2::NoParentheses; + let _ = RedundantParenthesesFunctionCall2::Parentheses; + let _ = RedundantParenthesesFunctionCall2::NoParentheses; } - enum RedundantParenthesesFunctionCall2 { - // Used as a function call but with redundant parentheses - Parentheses, - //~^ empty_enum_variants_with_brackets - // Not used as a function + Parentheses, //~ empty_enum_variants_with_brackets NoParentheses, } } -enum TestEnumWithFeatures { - NonEmptyBraces { - #[cfg(feature = "thisisneverenabled")] - x: i32, - }, // No error - NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error -} - -#[derive(Clone)] -enum Foo { - Variant1(i32), - Variant2, - Variant3, //~ ERROR: enum variant has empty brackets -} - -#[derive(Clone)] -pub enum PubFoo { - Variant1(i32), - Variant2, - Variant3(), -} - fn main() {} diff --git a/tests/ui/empty_enum_variants_with_brackets.rs b/tests/ui/empty_enum_variants_with_brackets.rs index 63a5a8e9143e..16451562e4b7 100644 --- a/tests/ui/empty_enum_variants_with_brackets.rs +++ b/tests/ui/empty_enum_variants_with_brackets.rs @@ -1,34 +1,61 @@ -#![warn(clippy::empty_enum_variants_with_brackets)] +//@aux-build:proc_macros.rs +#![deny(clippy::empty_enum_variants_with_brackets)] #![allow(dead_code)] +extern crate proc_macros; +use proc_macros::{external, with_span}; + pub enum PublicTestEnum { - NonEmptyBraces { x: i32, y: i32 }, // No error - NonEmptyParentheses(i32, i32), // No error - EmptyBraces {}, - //~^ empty_enum_variants_with_brackets - EmptyParentheses(), // No error as enum is pub + NonEmptyBraces { x: i32, y: i32 }, + NonEmptyParentheses(i32, i32), + EmptyBraces {}, //~ empty_enum_variants_with_brackets + EmptyParentheses(), } enum TestEnum { - NonEmptyBraces { x: i32, y: i32 }, // No error - NonEmptyParentheses(i32, i32), // No error - EmptyBraces {}, - //~^ empty_enum_variants_with_brackets - EmptyParentheses(), - //~^ empty_enum_variants_with_brackets - AnotherEnum, // No error + NonEmptyBraces { x: i32, y: i32 }, + NonEmptyParentheses(i32, i32), + EmptyBraces {}, //~ empty_enum_variants_with_brackets + EmptyParentheses(), //~ empty_enum_variants_with_brackets + AnotherEnum, +} + +enum TestEnumWithFeatures { + NonEmptyBraces { + #[cfg(feature = "thisisneverenabled")] + x: i32, + }, + NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), +} + +external! { + enum External { + Foo {}, + } +} + +with_span! { + span + enum ProcMacro { + Foo(), + } +} + +macro_rules! m { + ($($ty:ty),*) => { + enum Macro { + Foo($($ty),*), + } + } } mod issue12551 { + // Check if the variant is passed as an argument. enum EvenOdd { - // Used as functions -> no error Even(), Odd(), - // Not used as a function - Unknown(), - //~^ empty_enum_variants_with_brackets + Unknown(), //~ empty_enum_variants_with_brackets } - fn even_odd(x: i32) -> EvenOdd { (x % 2 == 0).then(EvenOdd::Even).unwrap_or_else(EvenOdd::Odd) } @@ -38,68 +65,30 @@ mod issue12551 { .then(NaturalOrNot::Natural) .unwrap_or_else(NaturalOrNot::NotNatural) } - enum NaturalOrNot { - // Used as functions -> no error Natural(), NotNatural(), - // Not used as a function - Unknown(), - //~^ empty_enum_variants_with_brackets + Unknown(), //~ empty_enum_variants_with_brackets } + // Check that call sites are fixed. enum RedundantParenthesesFunctionCall { - // Used as a function call but with redundant parentheses - Parentheses(), - //~^ empty_enum_variants_with_brackets - // Not used as a function + Parentheses(), //~ empty_enum_variants_with_brackets NoParentheses, } - - #[allow(clippy::no_effect)] fn redundant_parentheses_function_call() { - // The parentheses in the below line are redundant. - RedundantParenthesesFunctionCall::Parentheses(); - RedundantParenthesesFunctionCall::NoParentheses; + let _ = RedundantParenthesesFunctionCall::Parentheses(); + let _ = RedundantParenthesesFunctionCall::NoParentheses; } - // Same test as above but with usage of the enum occurring before the definition. - #[allow(clippy::no_effect)] fn redundant_parentheses_function_call_2() { - // The parentheses in the below line are redundant. - RedundantParenthesesFunctionCall2::Parentheses(); - RedundantParenthesesFunctionCall2::NoParentheses; + let _ = RedundantParenthesesFunctionCall2::Parentheses(); + let _ = RedundantParenthesesFunctionCall2::NoParentheses; } - enum RedundantParenthesesFunctionCall2 { - // Used as a function call but with redundant parentheses - Parentheses(), - //~^ empty_enum_variants_with_brackets - // Not used as a function + Parentheses(), //~ empty_enum_variants_with_brackets NoParentheses, } } -enum TestEnumWithFeatures { - NonEmptyBraces { - #[cfg(feature = "thisisneverenabled")] - x: i32, - }, // No error - NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error -} - -#[derive(Clone)] -enum Foo { - Variant1(i32), - Variant2, - Variant3(), //~ ERROR: enum variant has empty brackets -} - -#[derive(Clone)] -pub enum PubFoo { - Variant1(i32), - Variant2, - Variant3(), -} - fn main() {} diff --git a/tests/ui/empty_enum_variants_with_brackets.stderr b/tests/ui/empty_enum_variants_with_brackets.stderr index 7fe85e829a35..3afe8c722fb3 100644 --- a/tests/ui/empty_enum_variants_with_brackets.stderr +++ b/tests/ui/empty_enum_variants_with_brackets.stderr @@ -1,82 +1,63 @@ -error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:7:16 +error: non-unit variant contains no fields + --> tests/ui/empty_enum_variants_with_brackets.rs:11:17 | LL | EmptyBraces {}, - | ^^^ + | ^^ | - = note: `-D clippy::empty-enum-variants-with-brackets` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::empty_enum_variants_with_brackets)]` - = help: remove the brackets +note: the lint level is defined here + --> tests/ui/empty_enum_variants_with_brackets.rs:2:9 + | +LL | #![deny(clippy::empty_enum_variants_with_brackets)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: remove the braces -error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:15:16 +error: non-unit variant contains no fields + --> tests/ui/empty_enum_variants_with_brackets.rs:18:17 | LL | EmptyBraces {}, - | ^^^ + | ^^ | - = help: remove the brackets + = help: remove the braces -error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:17:21 +error: non-unit variant contains no fields + --> tests/ui/empty_enum_variants_with_brackets.rs:19:21 | LL | EmptyParentheses(), | ^^ | - = help: remove the brackets + = help: remove the parenthesis -error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:28:16 +error: non-unit variant contains no fields + --> tests/ui/empty_enum_variants_with_brackets.rs:57:16 | LL | Unknown(), | ^^ | - = help: remove the brackets + = help: remove the parenthesis -error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:47:16 +error: non-unit variant contains no fields + --> tests/ui/empty_enum_variants_with_brackets.rs:71:16 | LL | Unknown(), | ^^ | - = help: remove the brackets + = help: remove the parenthesis -error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:53:20 +error: non-unit variant contains no fields + --> tests/ui/empty_enum_variants_with_brackets.rs:76:20 | LL | Parentheses(), | ^^ | -help: remove the brackets - | -LL ~ Parentheses, -LL | -... -LL | // The parentheses in the below line are redundant. -LL ~ RedundantParenthesesFunctionCall::Parentheses; - | + = help: remove the parenthesis -error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:76:20 +error: non-unit variant contains no fields + --> tests/ui/empty_enum_variants_with_brackets.rs:89:20 | LL | Parentheses(), | ^^ | -help: remove the brackets - | -LL ~ RedundantParenthesesFunctionCall2::Parentheses; -LL | RedundantParenthesesFunctionCall2::NoParentheses; -... -LL | // Used as a function call but with redundant parentheses -LL ~ Parentheses, - | - -error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:95:13 - | -LL | Variant3(), - | ^^ - | - = help: remove the brackets + = help: remove the parenthesis -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/empty_structs_with_brackets.fixed b/tests/ui/empty_structs_with_brackets.fixed index 22386245ffe6..5f3683386098 100644 --- a/tests/ui/empty_structs_with_brackets.fixed +++ b/tests/ui/empty_structs_with_brackets.fixed @@ -1,48 +1,75 @@ -#![warn(clippy::empty_structs_with_brackets)] +//@aux-build:proc_macros.rs +#![deny(clippy::empty_structs_with_brackets)] #![allow(dead_code)] -pub struct MyEmptyStruct; // should trigger lint -//~^ empty_structs_with_brackets -struct MyEmptyTupleStruct; // should trigger lint -//~^ empty_structs_with_brackets +extern crate proc_macros; +use proc_macros::{external, with_span}; + +pub struct MyEmptyStruct; //~ empty_structs_with_brackets +struct MyEmptyTupleStruct; //~ empty_structs_with_brackets + +#[rustfmt::skip] +struct WithWhitespace; //~ empty_structs_with_brackets + +struct WithComment { + // Some long explanation here +} -// 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 { field: u8, } -struct MyTupleStruct(usize, String); // should not trigger lint -struct MySingleTupleStruct(usize); // should not trigger lint -struct MyUnitLikeStruct; // should not trigger lint +struct MyTupleStruct(usize, String); +struct MySingleTupleStruct(usize); +struct MyUnitLikeStruct; + +external! { + struct External {} +} + +with_span! { + span + struct ProcMacro {} +} -macro_rules! empty_struct { - ($s:ident) => { - struct $s {} - }; +macro_rules! m { + ($($name:ident: $ty:ty),*) => { + struct Macro { $($name: $ty),* } + } } +m! {} -empty_struct!(FromMacro); +mod used { + // Check if the constructor is passed as an argument. + struct S1(); + struct S2; //~ empty_structs_with_brackets + fn f1(x: i32) -> Option { + (x == 1).then(S1) + } + + // Check that call sites are fixed. + struct S3; //~ empty_structs_with_brackets + struct S4(); + fn f2() { + let _ = S3; + let _ = S4; + } +} fn main() {} +#[rustfmt::skip] mod issue15349 { - trait Bar {} - impl Bar for [u8; 7] {} - - struct Foo; - //~^ empty_structs_with_brackets - impl Foo - where - [u8; N]: Bar<[(); N]>, - { - fn foo() {} - } + struct S1; //~ empty_structs_with_brackets + struct S2 where usize: Copy; //~ empty_structs_with_brackets + struct S3 where; //~ empty_structs_with_brackets + struct S4; //~ empty_structs_with_brackets + struct S5 where usize: Copy; //~ empty_structs_with_brackets + struct S6 where usize: Copy; //~ empty_structs_with_brackets } diff --git a/tests/ui/empty_structs_with_brackets.rs b/tests/ui/empty_structs_with_brackets.rs index 5cb54b661349..f4197f1d81ff 100644 --- a/tests/ui/empty_structs_with_brackets.rs +++ b/tests/ui/empty_structs_with_brackets.rs @@ -1,48 +1,75 @@ -#![warn(clippy::empty_structs_with_brackets)] +//@aux-build:proc_macros.rs +#![deny(clippy::empty_structs_with_brackets)] #![allow(dead_code)] -pub struct MyEmptyStruct {} // should trigger lint -//~^ empty_structs_with_brackets -struct MyEmptyTupleStruct(); // should trigger lint -//~^ empty_structs_with_brackets +extern crate proc_macros; +use proc_macros::{external, with_span}; + +pub struct MyEmptyStruct {} //~ empty_structs_with_brackets +struct MyEmptyTupleStruct(); //~ empty_structs_with_brackets + +#[rustfmt::skip] +struct WithWhitespace { } //~ empty_structs_with_brackets + +struct WithComment { + // Some long explanation here +} -// 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 { field: u8, } -struct MyTupleStruct(usize, String); // should not trigger lint -struct MySingleTupleStruct(usize); // should not trigger lint -struct MyUnitLikeStruct; // should not trigger lint +struct MyTupleStruct(usize, String); +struct MySingleTupleStruct(usize); +struct MyUnitLikeStruct; + +external! { + struct External {} +} + +with_span! { + span + struct ProcMacro {} +} -macro_rules! empty_struct { - ($s:ident) => { - struct $s {} - }; +macro_rules! m { + ($($name:ident: $ty:ty),*) => { + struct Macro { $($name: $ty),* } + } } +m! {} -empty_struct!(FromMacro); +mod used { + // Check if the constructor is passed as an argument. + struct S1(); + struct S2(); //~ empty_structs_with_brackets + fn f1(x: i32) -> Option { + (x == 1).then(S1) + } + + // Check that call sites are fixed. + struct S3(); //~ empty_structs_with_brackets + struct S4(); + fn f2() { + let _ = S3(); + let _ = S4; + } +} fn main() {} +#[rustfmt::skip] mod issue15349 { - trait Bar {} - impl Bar for [u8; 7] {} - - struct Foo {} - //~^ empty_structs_with_brackets - impl Foo - where - [u8; N]: Bar<[(); N]>, - { - fn foo() {} - } + struct S1 {} //~ empty_structs_with_brackets + struct S2 where usize: Copy {} //~ empty_structs_with_brackets + struct S3 where {} //~ empty_structs_with_brackets + struct S4(); //~ empty_structs_with_brackets + struct S5() where usize: Copy; //~ empty_structs_with_brackets + struct S6() where usize: Copy; //~ empty_structs_with_brackets } diff --git a/tests/ui/empty_structs_with_brackets.stderr b/tests/ui/empty_structs_with_brackets.stderr index f662bb9423e3..c8b7d2d24fda 100644 --- a/tests/ui/empty_structs_with_brackets.stderr +++ b/tests/ui/empty_structs_with_brackets.stderr @@ -1,28 +1,95 @@ -error: found empty brackets on struct declaration - --> tests/ui/empty_structs_with_brackets.rs:4:25 +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:8:26 | -LL | pub struct MyEmptyStruct {} // should trigger lint - | ^^^ +LL | pub struct MyEmptyStruct {} + | ^^ | - = note: `-D clippy::empty-structs-with-brackets` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::empty_structs_with_brackets)]` - = help: remove the brackets +note: the lint level is defined here + --> tests/ui/empty_structs_with_brackets.rs:2:9 + | +LL | #![deny(clippy::empty_structs_with_brackets)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: remove the braces + +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:12:23 + | +LL | struct WithWhitespace { } + | ^^^^ + | + = help: remove the braces + +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:69:31 + | +LL | struct S1 {} + | ^^ + | + = help: remove the braces + +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:70:49 + | +LL | struct S2 where usize: Copy {} + | ^^ + | + = help: remove the braces + +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:71:21 + | +LL | struct S3 where {} + | ^^ + | + = help: remove the braces + +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:9:26 + | +LL | struct MyEmptyTupleStruct(); + | ^^ + | + = help: remove the parenthesis + +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:51:14 + | +LL | struct S2(); + | ^^ + | + = help: remove the parenthesis + +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:57:14 + | +LL | struct S3(); + | ^^ + | + = help: remove the parenthesis + +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:72:30 + | +LL | struct S4(); + | ^^ + | + = help: remove the parenthesis -error: found empty brackets on struct declaration - --> tests/ui/empty_structs_with_brackets.rs:6:26 +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:73:30 | -LL | struct MyEmptyTupleStruct(); // should trigger lint - | ^^^ +LL | struct S5() where usize: Copy; + | ^^ | - = help: remove the brackets + = help: remove the parenthesis -error: found empty brackets on struct declaration - --> tests/ui/empty_structs_with_brackets.rs:40:31 +error: non-unit struct contains no fields + --> tests/ui/empty_structs_with_brackets.rs:74:14 | -LL | struct Foo {} - | ^^^ +LL | struct S6() where usize: Copy; + | ^^ | - = help: remove the brackets + = help: remove the parenthesis -error: aborting due to 3 previous errors +error: aborting due to 11 previous errors