Skip to content

Commit 917be2d

Browse files
committed
Make empty_enum_variants_with_brackets to support empty variant with braces
1 parent bd0a5e4 commit 917be2d

File tree

3 files changed

+135
-92
lines changed

3 files changed

+135
-92
lines changed

clippy_lints/src/empty_with_brackets.rs

Lines changed: 83 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_utils::attrs::span_contains_cfg;
22
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
3-
use rustc_data_structures::fx::FxIndexMap;
3+
use rustc_data_structures::fx::{FxIndexMap, IndexEntry};
44
use rustc_errors::Applicability;
55
use rustc_hir::def::DefKind::Ctor;
66
use rustc_hir::def::Res::Def;
@@ -118,80 +118,51 @@ impl LateLintPass<'_> for EmptyWithBrackets {
118118
}
119119

120120
fn check_variant(&mut self, cx: &LateContext<'_>, variant: &Variant<'_>) {
121-
// FIXME: handle `$name {}`
122121
if !variant.span.from_expansion()
123122
&& !variant.ident.span.from_expansion()
124123
&& let span_after_ident = variant.span.with_lo(variant.ident.span.hi())
125124
&& has_no_fields(cx, &variant.data, span_after_ident)
126125
{
127126
match variant.data {
128127
VariantData::Struct { .. } => {
129-
// Empty struct variants can be linted immediately
130-
span_lint_and_then(
131-
cx,
132-
EMPTY_ENUM_VARIANTS_WITH_BRACKETS,
133-
span_after_ident,
134-
"enum variant has empty brackets",
135-
|diagnostic| {
136-
diagnostic.span_suggestion_hidden(
137-
span_after_ident,
138-
"remove the brackets",
139-
"",
140-
Applicability::MaybeIncorrect,
141-
);
142-
},
143-
);
128+
self.add_enum_variant(cx, variant, variant.def_id);
144129
},
145130
VariantData::Tuple(.., local_def_id) => {
146-
// Don't lint reachable tuple enums
147-
if cx.effective_visibilities.is_reachable(variant.def_id) {
148-
return;
149-
}
150-
if let Some(entry) = self.empty_tuple_enum_variants.get_mut(&local_def_id) {
151-
// empty_tuple_enum_variants contains Usage::NoDefinition if the variant was called before the
152-
// definition was encountered. Now that there's a definition, convert it
153-
// to Usage::Unused.
154-
if let Usage::NoDefinition { redundant_use_sites } = entry {
155-
*entry = Usage::Unused {
156-
redundant_use_sites: redundant_use_sites.clone(),
157-
};
158-
}
159-
} else {
160-
self.empty_tuple_enum_variants.insert(
161-
local_def_id,
162-
Usage::Unused {
163-
redundant_use_sites: vec![],
164-
},
165-
);
166-
}
131+
self.add_enum_variant(cx, variant, local_def_id);
167132
},
168133
VariantData::Unit(..) => {},
169134
}
170135
}
171136
}
172137

173138
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
174-
if let Some(def_id) = check_expr_for_enum_as_function(expr) {
175-
if let Some(parentheses_span) = call_parentheses_span(cx.tcx, expr) {
139+
if let Some((def_id, mut span)) = check_expr_for_enum_as_function(cx, expr) {
140+
if span.is_empty()
141+
&& let Some(parentheses_span) = call_parentheses_span(cx.tcx, expr)
142+
{
143+
span = parentheses_span;
144+
}
145+
146+
if span.is_empty() {
147+
// The parentheses are not redundant.
148+
self.empty_tuple_enum_variants.insert(def_id, Usage::Used);
149+
} else {
176150
// Do not count expressions from macro expansion as a redundant use site.
177151
if expr.span.from_expansion() {
178152
return;
179153
}
180-
self.update_enum_variant_usage(def_id, parentheses_span);
181-
} else {
182-
// The parentheses are not redundant.
183-
self.empty_tuple_enum_variants.insert(def_id, Usage::Used);
154+
self.update_enum_variant_usage(def_id, span);
184155
}
185156
}
186157
}
187158

188159
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
189-
if let Some((def_id, parentheses_span)) = check_pat_for_enum_as_function(cx, pat) {
160+
if let Some((def_id, span)) = check_pat_for_enum_as_function(cx, pat) {
190161
if pat.span.from_expansion() {
191162
return;
192163
}
193164

194-
self.update_enum_variant_usage(def_id, parentheses_span);
165+
self.update_enum_variant_usage(def_id, span);
195166
}
196167
}
197168

@@ -201,13 +172,19 @@ impl LateLintPass<'_> for EmptyWithBrackets {
201172
let Usage::Unused { redundant_use_sites } = usage else {
202173
continue;
203174
};
175+
204176
// Attempt to fetch the Variant from LocalDefId.
205-
let Node::Variant(variant) = cx.tcx.hir_node(
177+
let variant = if let Node::Variant(variant) = cx.tcx.hir_node(cx.tcx.local_def_id_to_hir_id(local_def_id)) {
178+
variant
179+
} else if let Node::Variant(variant) = cx.tcx.hir_node(
206180
cx.tcx
207181
.local_def_id_to_hir_id(cx.tcx.parent(local_def_id.to_def_id()).expect_local()),
208-
) else {
182+
) {
183+
variant
184+
} else {
209185
continue;
210186
};
187+
211188
// Span of the parentheses in variant definition
212189
let span = variant.span.with_lo(variant.ident.span.hi());
213190
span_lint_hir_and_then(
@@ -242,28 +219,43 @@ impl LateLintPass<'_> for EmptyWithBrackets {
242219
}
243220

244221
impl EmptyWithBrackets {
222+
fn add_enum_variant(&mut self, cx: &LateContext<'_>, variant: &Variant<'_>, local_def_id: LocalDefId) {
223+
// Don't lint reachable tuple enums
224+
if cx.effective_visibilities.is_reachable(variant.def_id) {
225+
return;
226+
}
227+
228+
self.empty_tuple_enum_variants
229+
.entry(local_def_id)
230+
.and_modify(|entry| {
231+
// empty_tuple_enum_variants contains Usage::NoDefinition if the variant was called before
232+
// the definition was encountered. Now that there's a
233+
// definition, convert it to Usage::Unused.
234+
if let Usage::NoDefinition { redundant_use_sites } = entry {
235+
*entry = Usage::Unused {
236+
redundant_use_sites: redundant_use_sites.clone(),
237+
};
238+
}
239+
})
240+
.or_insert_with(|| Usage::Unused {
241+
redundant_use_sites: vec![],
242+
});
243+
}
244+
245245
fn update_enum_variant_usage(&mut self, def_id: LocalDefId, parentheses_span: Span) {
246-
match self.empty_tuple_enum_variants.get_mut(&def_id) {
247-
Some(
248-
&mut (Usage::Unused {
249-
ref mut redundant_use_sites,
246+
match self.empty_tuple_enum_variants.entry(def_id) {
247+
IndexEntry::Occupied(mut e) => {
248+
if let Usage::Unused { redundant_use_sites } | Usage::NoDefinition { redundant_use_sites } = e.get_mut()
249+
{
250+
redundant_use_sites.push(parentheses_span);
250251
}
251-
| Usage::NoDefinition {
252-
ref mut redundant_use_sites,
253-
}),
254-
) => {
255-
redundant_use_sites.push(parentheses_span);
256252
},
257-
None => {
253+
IndexEntry::Vacant(e) => {
258254
// The variant isn't in the IndexMap which means its definition wasn't encountered yet.
259-
self.empty_tuple_enum_variants.insert(
260-
def_id,
261-
Usage::NoDefinition {
262-
redundant_use_sites: vec![parentheses_span],
263-
},
264-
);
255+
e.insert(Usage::NoDefinition {
256+
redundant_use_sites: vec![parentheses_span],
257+
});
265258
},
266-
_ => {},
267259
}
268260
}
269261
}
@@ -293,18 +285,27 @@ fn call_parentheses_span(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Span> {
293285
}
294286

295287
// Returns the LocalDefId of the variant being called as a function if it exists.
296-
fn check_expr_for_enum_as_function(expr: &Expr<'_>) -> Option<LocalDefId> {
297-
if let ExprKind::Path(QPath::Resolved(
298-
_,
299-
Path {
300-
res: Def(Ctor(CtorOf::Variant, _), def_id),
301-
..
288+
fn check_expr_for_enum_as_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(LocalDefId, Span)> {
289+
match expr.kind {
290+
ExprKind::Path(QPath::Resolved(
291+
_,
292+
Path {
293+
res: Def(Ctor(CtorOf::Variant, _), def_id),
294+
span,
295+
..
296+
},
297+
)) => def_id.as_local().map(|id| (id, span.with_lo(expr.span.hi()))),
298+
ExprKind::Struct(qpath, ..)
299+
if let Def(DefKind::Variant, mut def_id) = cx.typeck_results().qpath_res(qpath, expr.hir_id) =>
300+
{
301+
let ty = cx.tcx.type_of(def_id).instantiate_identity();
302+
if let ty::FnDef(ctor_def_id, _) = ty.kind() {
303+
def_id = *ctor_def_id;
304+
}
305+
306+
def_id.as_local().map(|id| (id, qpath.span().with_lo(expr.span.hi())))
302307
},
303-
)) = expr.kind
304-
{
305-
def_id.as_local()
306-
} else {
307-
None
308+
_ => None,
308309
}
309310
}
310311

@@ -316,10 +317,13 @@ fn check_pat_for_enum_as_function(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option
316317
def_id.as_local().map(|id| (id, qpath.span().with_lo(pat.span.hi())))
317318
},
318319
PatKind::Struct(qpath, ..)
319-
if let Def(DefKind::Variant, def_id) = cx.typeck_results().qpath_res(&qpath, pat.hir_id)
320-
&& let ty = cx.tcx.type_of(def_id).instantiate_identity()
321-
&& let ty::FnDef(def_id, _) = ty.kind() =>
320+
if let Def(DefKind::Variant, mut def_id) = cx.typeck_results().qpath_res(&qpath, pat.hir_id) =>
322321
{
322+
let ty = cx.tcx.type_of(def_id).instantiate_identity();
323+
if let ty::FnDef(ctor_def_id, _) = ty.kind() {
324+
def_id = *ctor_def_id;
325+
}
326+
323327
def_id.as_local().map(|id| (id, qpath.span().with_lo(pat.span.hi())))
324328
},
325329
_ => None,

tests/ui/empty_enum_variants_with_brackets.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
pub enum PublicTestEnum {
66
NonEmptyBraces { x: i32, y: i32 }, // No error
77
NonEmptyParentheses(i32, i32), // No error
8-
EmptyBraces {},
9-
//~^ empty_enum_variants_with_brackets
10-
EmptyParentheses(), // No error as enum is pub
8+
EmptyBraces {}, // No error as enum is pub
9+
EmptyParentheses(), // No error as enum is pub
1110
}
1211

1312
enum TestEnum {
@@ -115,4 +114,21 @@ fn issue16157() {
115114
<E>::V {} = E::V();
116115
}
117116

117+
fn variant_with_braces() {
118+
enum E {
119+
V(),
120+
//~^ empty_enum_variants_with_brackets
121+
}
122+
E::V() = E::V();
123+
E::V() = E::V {};
124+
<E>::V {} = <E>::V {};
125+
126+
enum F {
127+
U {},
128+
//~^ empty_enum_variants_with_brackets
129+
}
130+
F::U {} = F::U {};
131+
<F>::U {} = F::U {};
132+
}
133+
118134
fn main() {}

tests/ui/empty_enum_variants_with_brackets.stderr

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: enum variant has empty brackets
2-
--> tests/ui/empty_enum_variants_with_brackets.rs:8:16
2+
--> tests/ui/empty_enum_variants_with_brackets.rs:16:16
33
|
44
LL | EmptyBraces {},
55
| ^^^
@@ -8,14 +8,6 @@ LL | EmptyBraces {},
88
= help: to override `-D warnings` add `#[allow(clippy::empty_enum_variants_with_brackets)]`
99
= help: remove the brackets
1010

11-
error: enum variant has empty brackets
12-
--> tests/ui/empty_enum_variants_with_brackets.rs:16:16
13-
|
14-
LL | EmptyBraces {},
15-
| ^^^
16-
|
17-
= help: remove the brackets
18-
1911
error: enum variant has empty brackets
2012
--> tests/ui/empty_enum_variants_with_brackets.rs:18:21
2113
|
@@ -96,5 +88,36 @@ LL ~ <E>::V = E::V;
9688
LL ~ <E>::V = E::V;
9789
|
9890

99-
error: aborting due to 9 previous errors
91+
error: enum variant has empty brackets
92+
--> tests/ui/empty_enum_variants_with_brackets.rs:120:10
93+
|
94+
LL | V(),
95+
| ^^
96+
|
97+
help: remove the brackets
98+
|
99+
LL ~ V,
100+
LL |
101+
LL | }
102+
LL ~ E::V = E::V;
103+
LL ~ E::V = E::V;
104+
LL ~ <E>::V = <E>::V;
105+
|
106+
107+
error: enum variant has empty brackets
108+
--> tests/ui/empty_enum_variants_with_brackets.rs:128:10
109+
|
110+
LL | U {},
111+
| ^^^
112+
|
113+
help: remove the brackets
114+
|
115+
LL ~ U,
116+
LL |
117+
LL | }
118+
LL ~ F::U = F::U;
119+
LL ~ <F>::U = F::U;
120+
|
121+
122+
error: aborting due to 10 previous errors
100123

0 commit comments

Comments
 (0)