Skip to content

Commit ddd328b

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

File tree

4 files changed

+144
-76
lines changed

4 files changed

+144
-76
lines changed

clippy_lints/src/empty_with_brackets.rs

Lines changed: 78 additions & 75 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,55 @@ 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(variant.def_id);
144129
},
145130
VariantData::Tuple(.., local_def_id) => {
146131
// Don't lint reachable tuple enums
147132
if cx.effective_visibilities.is_reachable(variant.def_id) {
148133
return;
149134
}
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-
}
135+
self.add_enum_variant(local_def_id);
167136
},
168137
VariantData::Unit(..) => {},
169138
}
170139
}
171140
}
172141

173142
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) {
143+
if let Some((def_id, mut span)) = check_expr_for_enum_as_function(cx, expr) {
144+
if span.is_empty()
145+
&& let Some(parentheses_span) = call_parentheses_span(cx.tcx, expr)
146+
{
147+
span = parentheses_span;
148+
}
149+
150+
if span.is_empty() {
151+
// The parentheses are not redundant.
152+
self.empty_tuple_enum_variants.insert(def_id, Usage::Used);
153+
} else {
176154
// Do not count expressions from macro expansion as a redundant use site.
177155
if expr.span.from_expansion() {
178156
return;
179157
}
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);
158+
self.update_enum_variant_usage(def_id, span);
184159
}
185160
}
186161
}
187162

188163
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) {
164+
if let Some((def_id, span)) = check_pat_for_enum_as_function(cx, pat) {
190165
if pat.span.from_expansion() {
191166
return;
192167
}
193168

194-
self.update_enum_variant_usage(def_id, parentheses_span);
169+
self.update_enum_variant_usage(def_id, span);
195170
}
196171
}
197172

@@ -201,13 +176,19 @@ impl LateLintPass<'_> for EmptyWithBrackets {
201176
let Usage::Unused { redundant_use_sites } = usage else {
202177
continue;
203178
};
179+
204180
// Attempt to fetch the Variant from LocalDefId.
205-
let Node::Variant(variant) = cx.tcx.hir_node(
181+
let variant = if let Node::Variant(variant) = cx.tcx.hir_node(cx.tcx.local_def_id_to_hir_id(local_def_id)) {
182+
variant
183+
} else if let Node::Variant(variant) = cx.tcx.hir_node(
206184
cx.tcx
207185
.local_def_id_to_hir_id(cx.tcx.parent(local_def_id.to_def_id()).expect_local()),
208-
) else {
186+
) {
187+
variant
188+
} else {
209189
continue;
210190
};
191+
211192
// Span of the parentheses in variant definition
212193
let span = variant.span.with_lo(variant.ident.span.hi());
213194
span_lint_hir_and_then(
@@ -242,28 +223,38 @@ impl LateLintPass<'_> for EmptyWithBrackets {
242223
}
243224

244225
impl EmptyWithBrackets {
226+
fn add_enum_variant(&mut self, local_def_id: LocalDefId) {
227+
self.empty_tuple_enum_variants
228+
.entry(local_def_id)
229+
.and_modify(|entry| {
230+
// empty_tuple_enum_variants contains Usage::NoDefinition if the variant was called before
231+
// the definition was encountered. Now that there's a
232+
// definition, convert it to Usage::Unused.
233+
if let Usage::NoDefinition { redundant_use_sites } = entry {
234+
*entry = Usage::Unused {
235+
redundant_use_sites: redundant_use_sites.clone(),
236+
};
237+
}
238+
})
239+
.or_insert_with(|| Usage::Unused {
240+
redundant_use_sites: vec![],
241+
});
242+
}
243+
245244
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,
245+
match self.empty_tuple_enum_variants.entry(def_id) {
246+
IndexEntry::Occupied(mut e) => {
247+
if let Usage::Unused { redundant_use_sites } | Usage::NoDefinition { redundant_use_sites } = e.get_mut()
248+
{
249+
redundant_use_sites.push(parentheses_span);
250250
}
251-
| Usage::NoDefinition {
252-
ref mut redundant_use_sites,
253-
}),
254-
) => {
255-
redundant_use_sites.push(parentheses_span);
256251
},
257-
None => {
252+
IndexEntry::Vacant(e) => {
258253
// 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-
);
254+
e.insert(Usage::NoDefinition {
255+
redundant_use_sites: vec![parentheses_span],
256+
});
265257
},
266-
_ => {},
267258
}
268259
}
269260
}
@@ -293,18 +284,27 @@ fn call_parentheses_span(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Span> {
293284
}
294285

295286
// 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-
..
287+
fn check_expr_for_enum_as_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(LocalDefId, Span)> {
288+
match expr.kind {
289+
ExprKind::Path(QPath::Resolved(
290+
_,
291+
Path {
292+
res: Def(Ctor(CtorOf::Variant, _), def_id),
293+
span,
294+
..
295+
},
296+
)) => def_id.as_local().map(|id| (id, span.with_lo(expr.span.hi()))),
297+
ExprKind::Struct(qpath, ..)
298+
if let Def(DefKind::Variant, mut def_id) = cx.typeck_results().qpath_res(qpath, expr.hir_id) =>
299+
{
300+
let ty = cx.tcx.type_of(def_id).instantiate_identity();
301+
if let ty::FnDef(ctor_def_id, _) = ty.kind() {
302+
def_id = *ctor_def_id;
303+
}
304+
305+
def_id.as_local().map(|id| (id, qpath.span().with_lo(expr.span.hi())))
302306
},
303-
)) = expr.kind
304-
{
305-
def_id.as_local()
306-
} else {
307-
None
307+
_ => None,
308308
}
309309
}
310310

@@ -316,10 +316,13 @@ fn check_pat_for_enum_as_function(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option
316316
def_id.as_local().map(|id| (id, qpath.span().with_lo(pat.span.hi())))
317317
},
318318
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() =>
319+
if let Def(DefKind::Variant, mut def_id) = cx.typeck_results().qpath_res(&qpath, pat.hir_id) =>
322320
{
321+
let ty = cx.tcx.type_of(def_id).instantiate_identity();
322+
if let ty::FnDef(ctor_def_id, _) = ty.kind() {
323+
def_id = *ctor_def_id;
324+
}
325+
323326
def_id.as_local().map(|id| (id, qpath.span().with_lo(pat.span.hi())))
324327
},
325328
_ => None,

tests/ui/empty_enum_variants_with_brackets.fixed

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,21 @@ fn issue16157() {
115115
<E>::V = E::V;
116116
}
117117

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

tests/ui/empty_enum_variants_with_brackets.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,21 @@ fn issue16157() {
115115
<E>::V {} = E::V();
116116
}
117117

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

tests/ui/empty_enum_variants_with_brackets.stderr

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,36 @@ LL ~ <E>::V = E::V;
9696
LL ~ <E>::V = E::V;
9797
|
9898

99-
error: aborting due to 9 previous errors
99+
error: enum variant has empty brackets
100+
--> tests/ui/empty_enum_variants_with_brackets.rs:120:10
101+
|
102+
LL | V(),
103+
| ^^
104+
|
105+
help: remove the brackets
106+
|
107+
LL ~ V,
108+
LL |
109+
LL | }
110+
LL ~ E::V = E::V;
111+
LL ~ E::V = E::V;
112+
LL ~ <E>::V = <E>::V;
113+
|
114+
115+
error: enum variant has empty brackets
116+
--> tests/ui/empty_enum_variants_with_brackets.rs:128:10
117+
|
118+
LL | U {},
119+
| ^^^
120+
|
121+
help: remove the brackets
122+
|
123+
LL ~ U,
124+
LL |
125+
LL | }
126+
LL ~ F::U = F::U;
127+
LL ~ <F>::U = F::U;
128+
|
129+
130+
error: aborting due to 11 previous errors
100131

0 commit comments

Comments
 (0)