Skip to content

Commit 6222a73

Browse files
committed
Move hir::Item::ident into hir::ItemKind.
`hir::Item` has an `ident` field. - It's always non-empty for these item kinds: `ExternCrate`, `Static`, `Const`, `Fn`, `Macro`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`, Trait`, TraitAalis`. - It's always empty for these item kinds: `ForeignMod`, `GlobalAsm`, `Impl`. - For `Use`, it is non-empty for `UseKind::Single` and empty for `UseKind::{Glob,ListStem}`. All of this is quite non-obvious; the only documentation is a single comment saying "The name might be a dummy name in case of anonymous items". Some sites that handle items check for an empty ident, some don't. This is a very C-like way of doing things, but this is Rust, we have sum types, we can do this properly and never forget to check for the exceptional case and never YOLO possibly empty identifiers (or possibly dummy spans) around and hope that things will work out. The commit is large but it's mostly obvious plumbing work. Some notable things. - A similar transformation makes sense for `ast::Item`, but this is already a big change. That can be done later. - Lots of assertions are added to item lowering to ensure that identifiers are empty/non-empty as expected. These will be removable when `ast::Item` is done later. - `ItemKind::Use` doesn't get an `Ident`, but `UseKind::Single` does. - `lower_use_tree` is significantly simpler. No more confusing `&mut Ident` to deal with. - `ItemKind::ident` is a new method, it returns an `Option<Ident>`. It's used with `unwrap` in a few places; sometimes it's hard to tell exactly which item kinds might occur. None of these unwraps fail on the test suite. It's conceivable that some might fail on alternative input. We can deal with those if/when they happen. - In `trait_path` the `find_map`/`if let` is replaced with a loop, and things end up much clearer that way. - `named_span` no longer checks for an empty name; instead the call site now checks for a missing identifier if necessary. - `maybe_inline_local` doesn't need the `glob` argument, it can be computed in-function from the `renamed` argument. - `arbitrary_source_item_ordering::check_mod` had a big `if` statement that was just getting the ident from the item kinds that had one. It could be mostly replaced by a single call to the new `ItemKind::ident` method. - `ItemKind` grows from 56 to 64 bytes, but `Item` stays the same size, and that's what matters, because `ItemKind` only occurs within `Item`.
1 parent 2b431f7 commit 6222a73

34 files changed

+138
-144
lines changed

clippy_lints/src/arbitrary_source_item_ordering.rs

+31-55
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use clippy_config::types::{
55
};
66
use clippy_utils::diagnostics::span_lint_and_note;
77
use rustc_hir::{
8-
AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, UseKind,
8+
AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind,
99
Variant, VariantData,
1010
};
1111
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -178,8 +178,8 @@ impl ArbitrarySourceItemOrdering {
178178
/// Produces a linting warning for incorrectly ordered item members.
179179
fn lint_member_name<T: LintContext>(
180180
cx: &T,
181-
ident: &rustc_span::symbol::Ident,
182-
before_ident: &rustc_span::symbol::Ident,
181+
ident: &rustc_span::Ident,
182+
before_ident: &rustc_span::Ident,
183183
) {
184184
span_lint_and_note(
185185
cx,
@@ -192,21 +192,21 @@ impl ArbitrarySourceItemOrdering {
192192
}
193193

194194
fn lint_member_item<T: LintContext>(cx: &T, item: &Item<'_>, before_item: &Item<'_>) {
195-
let span = if item.ident.as_str().is_empty() {
196-
&item.span
195+
let span = if let Some(ident) = item.kind.ident() {
196+
ident.span
197197
} else {
198-
&item.ident.span
198+
item.span
199199
};
200200

201-
let (before_span, note) = if before_item.ident.as_str().is_empty() {
201+
let (before_span, note) = if let Some(ident) = before_item.kind.ident() {
202202
(
203-
&before_item.span,
204-
"should be placed before the following item".to_owned(),
203+
ident.span,
204+
format!("should be placed before `{}`", ident.as_str(),),
205205
)
206206
} else {
207207
(
208-
&before_item.ident.span,
209-
format!("should be placed before `{}`", before_item.ident.as_str(),),
208+
before_item.span,
209+
"should be placed before the following item".to_owned(),
210210
)
211211
};
212212

@@ -218,9 +218,9 @@ impl ArbitrarySourceItemOrdering {
218218
span_lint_and_note(
219219
cx,
220220
ARBITRARY_SOURCE_ITEM_ORDERING,
221-
*span,
221+
span,
222222
"incorrect ordering of items (must be alphabetically ordered)",
223-
Some(*before_span),
223+
Some(before_span),
224224
note,
225225
);
226226
}
@@ -244,7 +244,7 @@ impl ArbitrarySourceItemOrdering {
244244
impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
245245
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
246246
match &item.kind {
247-
ItemKind::Enum(enum_def, _generics) if self.enable_ordering_for_enum => {
247+
ItemKind::Enum(_, enum_def, _generics) if self.enable_ordering_for_enum => {
248248
let mut cur_v: Option<&Variant<'_>> = None;
249249
for variant in enum_def.variants {
250250
if variant.span.in_external_macro(cx.sess().source_map()) {
@@ -259,7 +259,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
259259
cur_v = Some(variant);
260260
}
261261
},
262-
ItemKind::Struct(VariantData::Struct { fields, .. }, _generics) if self.enable_ordering_for_struct => {
262+
ItemKind::Struct(_, VariantData::Struct { fields, .. }, _generics) if self.enable_ordering_for_struct => {
263263
let mut cur_f: Option<&FieldDef<'_>> = None;
264264
for field in *fields {
265265
if field.span.in_external_macro(cx.sess().source_map()) {
@@ -274,7 +274,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
274274
cur_f = Some(field);
275275
}
276276
},
277-
ItemKind::Trait(is_auto, _safety, _generics, _generic_bounds, item_ref)
277+
ItemKind::Trait(is_auto, _safety, _ident, _generics, _generic_bounds, item_ref)
278278
if self.enable_ordering_for_trait && *is_auto == IsAuto::No =>
279279
{
280280
let mut cur_t: Option<&TraitItemRef> = None;
@@ -351,50 +351,24 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
351351
continue;
352352
}
353353

354-
// The following exceptions (skipping with `continue;`) may not be
355-
// complete, edge cases have not been explored further than what
356-
// appears in the existing code base.
357-
if item.ident.name == rustc_span::symbol::kw::Empty {
358-
if let ItemKind::Impl(_) = item.kind {
359-
// Sorting trait impls for unnamed types makes no sense.
360-
if get_item_name(item).is_empty() {
361-
continue;
362-
}
363-
} else if let ItemKind::ForeignMod { .. } = item.kind {
364-
continue;
365-
} else if let ItemKind::GlobalAsm { .. } = item.kind {
366-
continue;
367-
} else if let ItemKind::Use(path, use_kind) = item.kind {
368-
if path.segments.is_empty() {
369-
// Use statements that contain braces get caught here.
370-
// They will still be linted internally.
371-
continue;
372-
} else if path.segments.len() >= 2
373-
&& (path.segments[0].ident.name == rustc_span::sym::std
374-
|| path.segments[0].ident.name == rustc_span::sym::core)
375-
&& path.segments[1].ident.name == rustc_span::sym::prelude
376-
{
377-
// Filters the autogenerated prelude use statement.
378-
// e.g. `use std::prelude::rustc_2021`
379-
} else if use_kind == UseKind::Glob {
380-
// Filters glob kinds of uses.
381-
// e.g. `use std::sync::*`
382-
} else {
383-
// This can be used for debugging.
384-
// println!("Unknown autogenerated use statement: {:?}", item);
385-
}
386-
continue;
387-
}
388-
}
354+
let ident = if let Some(ident) = item.kind.ident() {
355+
ident
356+
} else if let ItemKind::Impl(_) = item.kind
357+
&& !get_item_name(item).is_empty()
358+
{
359+
rustc_span::Ident::empty() // FIXME: a bit strange, is there a better way to do it?
360+
} else {
361+
continue;
362+
};
389363

390-
if item.ident.name.as_str().starts_with('_') {
364+
if ident.name.as_str().starts_with('_') {
391365
// Filters out unnamed macro-like impls for various derives,
392366
// e.g. serde::Serialize or num_derive::FromPrimitive.
393367
continue;
394368
}
395369

396-
if item.ident.name == rustc_span::sym::std && item.span.is_dummy() {
397-
if let ItemKind::ExternCrate(None) = item.kind {
370+
if ident.name == rustc_span::sym::std && item.span.is_dummy() {
371+
if let ItemKind::ExternCrate(None, _) = item.kind {
398372
// Filters the auto-included Rust standard library.
399373
continue;
400374
}
@@ -525,6 +499,8 @@ fn get_item_name(item: &Item<'_>) -> String {
525499
String::new()
526500
}
527501
},
528-
_ => item.ident.name.as_str().to_owned(),
502+
// FIXME: `Ident::empty` for anonymous items is a bit strange, is there
503+
// a better way to do it?
504+
_ => item.kind.ident().unwrap_or(rustc_span::Ident::empty()).name.as_str().to_owned(),
529505
}
530506
}

clippy_lints/src/attrs/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ mod utils;
1616
use clippy_config::Conf;
1717
use clippy_utils::msrvs::{self, Msrv, MsrvStack};
1818
use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind};
19-
use rustc_hir::{ImplItem, Item, TraitItem};
19+
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
2020
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
2121
use rustc_session::impl_lint_pass;
2222
use rustc_span::sym;
@@ -466,8 +466,8 @@ impl Attributes {
466466
impl<'tcx> LateLintPass<'tcx> for Attributes {
467467
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
468468
let attrs = cx.tcx.hir_attrs(item.hir_id());
469-
if is_relevant_item(cx, item) {
470-
inline_always::check(cx, item.span, item.ident.name, attrs);
469+
if let ItemKind::Fn { ident, .. } = item.kind && is_relevant_item(cx, item) {
470+
inline_always::check(cx, item.span, ident.name, attrs);
471471
}
472472
repr_attributes::check(cx, item.span, attrs, self.msrv);
473473
}

clippy_lints/src/disallowed_types.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
9999

100100
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
101101
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
102-
if let ItemKind::Use(path, UseKind::Single) = &item.kind {
102+
if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind {
103103
for res in &path.res {
104104
self.check_res_emit(cx, res, item.span);
105105
}

clippy_lints/src/enum_clike.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
3838
if cx.tcx.data_layout.pointer_size.bits() != 64 {
3939
return;
4040
}
41-
if let ItemKind::Enum(def, _) = &item.kind {
41+
if let ItemKind::Enum(_, def, _) = &item.kind {
4242
for var in def.variants {
4343
if let Some(anon_const) = &var.disr_expr {
4444
let def_id = cx.tcx.hir_body_owner_def_id(anon_const.body);

clippy_lints/src/error_impl_error.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]);
3737
impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
3838
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
3939
match item.kind {
40-
ItemKind::TyAlias(..)
41-
if item.ident.name == sym::Error
40+
ItemKind::TyAlias(ident, ..)
41+
if ident.name == sym::Error
4242
&& is_visible_outside_module(cx, item.owner_id.def_id)
4343
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
4444
&& let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error)
@@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
4747
span_lint(
4848
cx,
4949
ERROR_IMPL_ERROR,
50-
item.ident.span,
50+
ident.span,
5151
"exported type alias named `Error` that implements `Error`",
5252
);
5353
},

clippy_lints/src/escape.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
9191
}
9292

9393
// find `self` ty for this trait if relevant
94-
if let ItemKind::Trait(_, _, _, _, items) = item.kind {
94+
if let ItemKind::Trait(_, _, _, _, _, items) = item.kind {
9595
for trait_item in items {
9696
if trait_item.id.owner_id.def_id == fn_def_id {
9797
// be sure we have `self` parameter in this function

clippy_lints/src/excessive_bools.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ fn check_fn_decl(cx: &LateContext<'_>, decl: &FnDecl<'_>, sp: Span, max: u64) {
122122

123123
impl<'tcx> LateLintPass<'tcx> for ExcessiveBools {
124124
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
125-
if let ItemKind::Struct(variant_data, _) = &item.kind
125+
if let ItemKind::Struct(_, variant_data, _) = &item.kind
126126
&& variant_data.fields().len() as u64 > self.max_struct_bools
127127
&& has_n_bools(
128128
variant_data.fields().iter().map(|field| field.ty),

clippy_lints/src/exhaustive_items.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ impl LateLintPass<'_> for ExhaustiveItems {
7676
"exported enums should not be exhaustive",
7777
[].as_slice(),
7878
),
79-
ItemKind::Struct(v, ..) => (
79+
ItemKind::Struct(_, v, ..) => (
8080
EXHAUSTIVE_STRUCTS,
8181
"exported structs should not be exhaustive",
8282
v.fields(),

clippy_lints/src/functions/result.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
103103
.did()
104104
.as_local()
105105
&& let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_def_id)
106-
&& let hir::ItemKind::Enum(ref def, _) = item.kind
106+
&& let hir::ItemKind::Enum(_, ref def, _) = item.kind
107107
{
108108
let variants_size = AdtVariantInfo::new(cx, *adt, subst);
109109
if let Some((first_variant, variants)) = variants_size.split_first()

clippy_lints/src/item_name_repetitions.rs

+21-17
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashSet;
88
use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData};
99
use rustc_lint::{LateContext, LateLintPass};
1010
use rustc_session::impl_lint_pass;
11-
use rustc_span::Span;
11+
use rustc_span::{Ident, Span};
1212
use rustc_span::symbol::Symbol;
1313

1414
declare_clippy_lint! {
@@ -196,16 +196,16 @@ fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
196196
prefixes.iter().all(|p| p == &"" || p == &"_")
197197
}
198198

199-
fn check_fields(cx: &LateContext<'_>, threshold: u64, item: &Item<'_>, fields: &[FieldDef<'_>]) {
199+
fn check_fields(cx: &LateContext<'_>, threshold: u64, ident: Ident, span: Span, fields: &[FieldDef<'_>]) {
200200
if (fields.len() as u64) < threshold {
201201
return;
202202
}
203203

204-
check_struct_name_repetition(cx, item, fields);
204+
check_struct_name_repetition(cx, ident, fields);
205205

206206
// if the SyntaxContext of the identifiers of the fields and struct differ dont lint them.
207207
// this prevents linting in macros in which the location of the field identifier names differ
208-
if !fields.iter().all(|field| item.ident.span.eq_ctxt(field.ident.span)) {
208+
if !fields.iter().all(|field| ident.span.eq_ctxt(field.ident.span)) {
209209
return;
210210
}
211211

@@ -256,19 +256,19 @@ fn check_fields(cx: &LateContext<'_>, threshold: u64, item: &Item<'_>, fields: &
256256
span_lint_and_help(
257257
cx,
258258
STRUCT_FIELD_NAMES,
259-
item.span,
259+
span,
260260
format!("all fields have the same {what}fix: `{value}`"),
261261
None,
262262
format!("remove the {what}fixes"),
263263
);
264264
}
265265
}
266266

267-
fn check_struct_name_repetition(cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) {
268-
let snake_name = to_snake_case(item.ident.name.as_str());
267+
fn check_struct_name_repetition(cx: &LateContext<'_>, ident: Ident, fields: &[FieldDef<'_>]) {
268+
let snake_name = to_snake_case(ident.name.as_str());
269269
let item_name_words: Vec<&str> = snake_name.split('_').collect();
270270
for field in fields {
271-
if field.ident.span.eq_ctxt(item.ident.span) {
271+
if field.ident.span.eq_ctxt(ident.span) {
272272
//consider linting only if the field identifier has the same SyntaxContext as the item(struct)
273273
let field_words: Vec<&str> = field.ident.name.as_str().split('_').collect();
274274
if field_words.len() >= item_name_words.len() {
@@ -397,19 +397,23 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
397397
}
398398

399399
impl LateLintPass<'_> for ItemNameRepetitions {
400-
fn check_item_post(&mut self, _cx: &LateContext<'_>, _item: &Item<'_>) {
400+
fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
401+
let Some(_ident) = item.kind.ident() else { return };
402+
401403
let last = self.modules.pop();
402404
assert!(last.is_some());
403405
}
404406

405407
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
406-
let item_name = item.ident.name.as_str();
408+
let Some(ident) = item.kind.ident() else { return };
409+
410+
let item_name = ident.name.as_str();
407411
let item_camel = to_camel_case(item_name);
408412
if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
409413
if let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules {
410414
// constants don't have surrounding modules
411415
if !mod_camel.is_empty() {
412-
if mod_name == &item.ident.name
416+
if mod_name == &ident.name
413417
&& let ItemKind::Mod(..) = item.kind
414418
&& (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public())
415419
{
@@ -438,7 +442,7 @@ impl LateLintPass<'_> for ItemNameRepetitions {
438442
Some(c) if is_word_beginning(c) => span_lint(
439443
cx,
440444
MODULE_NAME_REPETITIONS,
441-
item.ident.span,
445+
ident.span,
442446
"item name starts with its containing module's name",
443447
),
444448
_ => (),
@@ -450,7 +454,7 @@ impl LateLintPass<'_> for ItemNameRepetitions {
450454
span_lint(
451455
cx,
452456
MODULE_NAME_REPETITIONS,
453-
item.ident.span,
457+
ident.span,
454458
"item name ends with its containing module's name",
455459
);
456460
}
@@ -462,13 +466,13 @@ impl LateLintPass<'_> for ItemNameRepetitions {
462466
&& span_is_local(item.span)
463467
{
464468
match item.kind {
465-
ItemKind::Enum(def, _) => check_variant(cx, self.enum_threshold, &def, item_name, item.span),
466-
ItemKind::Struct(VariantData::Struct { fields, .. }, _) => {
467-
check_fields(cx, self.struct_threshold, item, fields);
469+
ItemKind::Enum(_, def, _) => check_variant(cx, self.enum_threshold, &def, item_name, item.span),
470+
ItemKind::Struct(_, VariantData::Struct { fields, .. }, _) => {
471+
check_fields(cx, self.struct_threshold, ident, item.span, fields);
468472
},
469473
_ => (),
470474
}
471475
}
472-
self.modules.push((item.ident.name, item_camel, item.owner_id));
476+
self.modules.push((ident.name, item_camel, item.owner_id));
473477
}
474478
}

0 commit comments

Comments
 (0)