1use crate::de::{
2 Deserialize, DeserializeOrDefault, DiagCtxt, FromDefault, TomlValue, create_value_list_msg, find_closest_match,
3};
4use clippy_utils::paths::{PathNS, find_crates, lookup_path};
5use core::fmt::{self, Display};
6use itertools::Itertools as _;
7use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8use rustc_errors::{Applicability, Diag};
9use rustc_hir::PrimTy;
10use rustc_hir::def::DefKind;
11use rustc_hir::def_id::DefIdMap;
12use rustc_middle::ty::TyCtxt;
13use rustc_session::Session;
14use rustc_span::{Span, Spanned, Symbol};
15use std::collections::HashMap;
16
17macro_rules! concat_expr {
18 ($($e:expr)*) => {
19 concat!($($e),*)
20 }
21}
22
23macro_rules! name_or_lit {
24 ($name:ident) => {
25 stringify!($name)
26 };
27 ($name:ident $lit:literal) => {
28 $lit
29 };
30}
31
32macro_rules! conf_enum {
33 (
34 $(#[$attrs:meta])*
35 $vis:vis $name:ident {$(
36 $(#[$var_attrs:meta])*
37 $var_name:ident $(($var_lit:literal))?,
38 )*}
39 ) => {
40 $(#[$attrs])*
41 #[derive(Clone, Copy)]
42 $vis enum $name {$(
43 $(#[$var_attrs])*
44 $var_name,
45 )*}
46 impl $name {
47 const NAMES: &[&'static str] = &[$(name_or_lit!($var_name $($var_lit)?)),*];
48 #[allow(dead_code)]
49 const COUNT: usize = {
50 enum __ITEMS__ { $($var_name,)* __COUNT__ }
51 __ITEMS__::__COUNT__ as usize
52 };
53
54 pub fn name(self) -> &'static str {
55 Self::NAMES[self as usize]
56 }
57 #[allow(clippy::should_implement_trait)]
58 pub fn from_str(s: &str) -> Option<Self> {
59 match s {
60 $(name_or_lit!($var_name $($var_lit)?) => Some(Self::$var_name),)*
61 _ => None,
62 }
63 }
64 }
65 impl FromDefault<$name> for $name {
66 fn from_default(default: $name) -> Self {
67 default
68 }
69 fn display_default(default: $name) -> impl Display {
70 String::display_default(default.name())
71 }
72 }
73 impl Deserialize for $name {
74 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
75 let Some(s) = value.get_ref().as_str() else {
76 dcx.span_err(value.span(), "expected a string");
77 return None;
78 };
79 let x = Self::from_str(s);
80 if x.is_none() {
81 let sp = dcx.make_sp(value.span());
82 let mut diag = dcx.inner.struct_span_err(
83 sp,
84 concat_expr!("expected one of: " $("`" name_or_lit!($var_name $($var_lit)?) "`")", "*),
85 );
86 if let Some(sugg) = find_closest_match(s, Self::NAMES) {
87 diag.span_suggestion(sp, "did you mean", sugg, Applicability::MaybeIncorrect);
88 }
89 diag.note(create_value_list_msg(dcx, Self::NAMES));
90 diag.emit();
91 }
92 x
93 }
94 }
95 };
96}
97pub struct Rename {
98 pub path: String,
99 pub rename: String,
100}
101
102impl Deserialize for Rename {
103 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
104 if let Some(table) = value.as_ref().as_table() {
105 deserialize_table!(dcx, table,
106 path("path"): String,
107 rename("rename"): String,
108 );
109 let Some(path) = path else {
110 dcx.span_err(value.span().clone(), "missing required field `path`");
111 return None;
112 };
113 let Some(rename) = rename else {
114 dcx.span_err(value.span().clone(), "missing required field `rename`");
115 return None;
116 };
117 Some(Rename { path, rename })
118 } else {
119 dcx.span_err(value.span(), "expected a table");
120 None
121 }
122 }
123}
124
125pub type DisallowedPathWithoutReplacement = DisallowedPath<false>;
126
127pub struct DisallowedPath<const REPLACEMENT_ALLOWED: bool = true> {
128 path: Spanned<String>,
129 reason: Option<String>,
130 replacement: Option<String>,
131 allow_invalid: bool,
137}
138
139impl<const REPLACEMENT_ALLOWED: bool> DisallowedPath<REPLACEMENT_ALLOWED> {
140 pub fn path(&self) -> &str {
141 &self.path.node
142 }
143
144 pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) {
145 move |diag| {
146 if let Some(replacement) = &self.replacement {
147 diag.span_suggestion(
148 span,
149 self.reason.as_ref().map_or_else(|| String::from("use"), Clone::clone),
150 replacement,
151 Applicability::MachineApplicable,
152 );
153 } else if let Some(reason) = &self.reason {
154 diag.note(reason.clone());
155 }
156 }
157 }
158}
159
160impl Deserialize for DisallowedPath<false> {
161 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
162 if let Some(s) = value.as_ref().as_str() {
163 Some(DisallowedPath {
164 path: Spanned {
165 node: s.into(),
166 span: dcx.make_sp(value.span()),
167 },
168 reason: None,
169 replacement: None,
170 allow_invalid: false,
171 })
172 } else if let Some(table) = value.as_ref().as_table() {
173 deserialize_table!(dcx, table,
174 path("path"): Spanned<String>,
175 reason("reason"): String,
176 allow_invalid("allow-invalid"): bool,
177 );
178 let Some(path) = path else {
179 dcx.span_err(value.span(), "missing required field `path`");
180 return None;
181 };
182 Some(DisallowedPath {
183 path,
184 reason,
185 replacement: None,
186 allow_invalid: allow_invalid.unwrap_or(false),
187 })
188 } else {
189 dcx.span_err(value.span(), "expected either a string or an inline table");
190 None
191 }
192 }
193}
194
195impl Deserialize for DisallowedPath<true> {
196 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
197 if let Some(s) = value.as_ref().as_str() {
198 Some(DisallowedPath {
199 path: Spanned {
200 node: s.into(),
201 span: dcx.make_sp(value.span()),
202 },
203 reason: None,
204 replacement: None,
205 allow_invalid: false,
206 })
207 } else if let Some(table) = value.as_ref().as_table() {
208 deserialize_table!(dcx, table,
209 path("path"): Spanned<String>,
210 reason("reason"): String,
211 replacement("replacement"): String,
212 allow_invalid("allow-invalid"): bool,
213 );
214 let Some(path) = path else {
215 dcx.span_err(value.span(), "missing required field `path`");
216 return None;
217 };
218 Some(DisallowedPath {
219 path,
220 reason,
221 replacement,
222 allow_invalid: allow_invalid.unwrap_or(false),
223 })
224 } else {
225 dcx.span_err(value.span(), "expected either a string or an inline table");
226 None
227 }
228 }
229}
230
231#[expect(clippy::type_complexity)]
233pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
234 tcx: TyCtxt<'_>,
235 disallowed_paths: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
236 ns: PathNS,
237 def_kind_predicate: impl Fn(DefKind) -> bool,
238 predicate_description: &str,
239 allow_prim_tys: bool,
240) -> (
241 DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)>,
242 FxHashMap<PrimTy, (&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)>,
243) {
244 let mut def_ids: DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> = DefIdMap::default();
245 let mut prim_tys: FxHashMap<PrimTy, (&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> =
246 FxHashMap::default();
247 for disallowed_path in disallowed_paths {
248 let path = &*disallowed_path.path.node;
249 let sym_path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect();
250 let mut resolutions = lookup_path(tcx, ns, &sym_path);
251 resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id)));
252
253 let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice()
254 && let Some(prim) = PrimTy::from_name(name)
255 {
256 (allow_prim_tys.then_some(prim), true)
257 } else {
258 (None, false)
259 };
260
261 if resolutions.is_empty()
262 && prim_ty.is_none()
263 && !disallowed_path.allow_invalid
264 && (sym_path.len() < 2 || !find_crates(tcx, sym_path[0]).is_empty())
267 {
268 let found_def_ids = lookup_path(tcx, PathNS::Arbitrary, &sym_path);
270 let message = if let Some(&def_id) = found_def_ids.first() {
271 let (article, description) = tcx.article_and_description(def_id);
272 format!("expected a {predicate_description}, found {article} {description}")
273 } else if found_prim_ty {
274 format!("expected a {predicate_description}, found a primitive type")
275 } else {
276 format!("`{path}` does not refer to a reachable {predicate_description}")
277 };
278 tcx.sess
279 .dcx()
280 .struct_span_warn(disallowed_path.path.span, message)
281 .with_help("add `allow-invalid = true` to the entry to suppress this warning")
282 .emit();
283 }
284
285 for def_id in resolutions {
286 def_ids.insert(def_id, (path, disallowed_path));
287 }
288 if let Some(ty) = prim_ty {
289 prim_tys.insert(ty, (path, disallowed_path));
290 }
291 }
292
293 (def_ids, prim_tys)
294}
295
296conf_enum! {
297 #[derive(PartialEq, Eq)]
298 pub MatchLintBehaviour {
299 AllTypes,
300 WellKnownTypes,
301 Never,
302 }
303}
304
305enum BraceKind {
306 Brace,
307 Bracket,
308 Paren,
309}
310
311impl Deserialize for BraceKind {
312 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
313 let msg = if let Some(s) = value.as_ref().as_str() {
314 match s {
315 "{" | "{}" => return Some(BraceKind::Brace),
316 "[" | "[]" => return Some(BraceKind::Bracket),
317 "(" | "()" => return Some(BraceKind::Paren),
318 _ => "unknown value",
319 }
320 } else {
321 "expected a string"
322 };
323 let mut diag = dcx.inner.struct_span_err(dcx.make_sp(value.span()), msg);
324 diag.note("possible values: `()`, `[]`, `{}`");
325 diag.emit();
326 None
327 }
328}
329
330pub struct MacroMatcher {
331 pub name: String,
332 pub braces: (char, char),
333}
334
335impl Deserialize for MacroMatcher {
336 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
337 if let Some(table) = value.as_ref().as_table() {
338 deserialize_table!(dcx, table,
339 name("name"): String,
340 brace("brace"): BraceKind,
341 );
342 let Some(name) = name else {
343 dcx.span_err(value.span(), "missing required field `name`");
344 return None;
345 };
346 let Some(brace) = brace else {
347 dcx.span_err(value.span(), "missing required field `brace`");
348 return None;
349 };
350 Some(MacroMatcher {
351 name,
352 braces: match brace {
353 BraceKind::Brace => ('{', '}'),
354 BraceKind::Bracket => ('[', ']'),
355 BraceKind::Paren => ('(', ')'),
356 },
357 })
358 } else {
359 dcx.span_err(value.span(), "expected an inline table");
360 None
361 }
362 }
363}
364
365conf_enum! {
366 pub PubUnderscoreFieldsBehaviour {
367 PubliclyExported,
368 AllPubFields,
369 }
370}
371
372conf_enum! {
373 #[derive(Debug, PartialEq, Eq, Hash)]
375 pub SourceItemOrderingCategory {
376 Enum("enum"),
377 Impl("impl"),
378 Module("module"),
379 Struct("struct"),
380 Trait("trait"),
381 }
382}
383
384pub struct SourceItemOrdering(Vec<SourceItemOrderingCategory>);
389
390impl SourceItemOrdering {
391 pub fn contains(&self, category: SourceItemOrderingCategory) -> bool {
392 self.0.contains(&category)
393 }
394}
395
396impl fmt::Debug for SourceItemOrdering {
397 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398 self.0.fmt(f)
399 }
400}
401
402impl Deserialize for SourceItemOrdering {
403 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
404 let items = Vec::<SourceItemOrderingCategory>::deserialize(dcx, value)?;
405 let mut items_set = FxHashSet::default();
406
407 for item in &items {
408 if items_set.contains(item) {
409 dcx.span_err(
410 value.span(),
411 format!(
412 "The category \"{}\" was enabled more than once in the source ordering configuration.",
413 item.name()
414 ),
415 );
416 return None;
417 }
418 items_set.insert(item);
419 }
420 Some(SourceItemOrdering(items))
421 }
422}
423impl FromDefault<()> for SourceItemOrdering {
424 fn from_default((): ()) -> Self {
425 Self(vec![
426 SourceItemOrderingCategory::Enum,
427 SourceItemOrderingCategory::Impl,
428 SourceItemOrderingCategory::Module,
429 SourceItemOrderingCategory::Struct,
430 SourceItemOrderingCategory::Trait,
431 ])
432 }
433 fn display_default((): ()) -> impl Display {
434 r#"["enum", "impl", "module", "struct", "trait"]"#
435 }
436}
437impl DeserializeOrDefault<()> for SourceItemOrdering {
438 fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>, default: ()) -> Self {
439 Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default))
440 }
441}
442
443conf_enum! {
444 #[derive(Debug, PartialEq, Eq, Hash)]
445 pub SourceItemOrderingModuleItemKind {
446 ExternCrate("extern_crate"),
447 Mod("mod"),
448 ForeignMod("foreign_mod"),
449 Use("use"),
450 Macro("macro"),
451 GlobalAsm("global_asm"),
452 Static("static"),
453 Const("const"),
454 TyAlias("ty_alias"),
455 Enum("enum"),
456 Struct("struct"),
457 Union("union"),
458 Trait("trait"),
459 TraitAlias("trait_alias"),
460 Impl("impl"),
461 Fn("fn"),
462 }
463}
464
465impl SourceItemOrderingModuleItemKind {
466 pub fn all_variants() -> Vec<Self> {
467 #[allow(clippy::enum_glob_use)] use SourceItemOrderingModuleItemKind::*;
469 vec![
470 ExternCrate,
471 Mod,
472 ForeignMod,
473 Use,
474 Macro,
475 GlobalAsm,
476 Static,
477 Const,
478 TyAlias,
479 Enum,
480 Struct,
481 Union,
482 Trait,
483 TraitAlias,
484 Impl,
485 Fn,
486 ]
487 }
488}
489
490#[derive(Clone)]
495pub struct SourceItemOrderingModuleItemGroupings {
496 groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)>,
497 lut: HashMap<SourceItemOrderingModuleItemKind, usize>,
498 back_lut: HashMap<SourceItemOrderingModuleItemKind, String>,
499}
500
501impl fmt::Debug for SourceItemOrderingModuleItemGroupings {
502 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503 self.groups.fmt(f)
504 }
505}
506
507impl SourceItemOrderingModuleItemGroupings {
508 fn build_lut(
509 groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)],
510 ) -> HashMap<SourceItemOrderingModuleItemKind, usize> {
511 let mut lut = HashMap::new();
512 for (group_index, (_, items)) in groups.iter().enumerate() {
513 for &item in items {
514 lut.insert(item, group_index);
515 }
516 }
517 lut
518 }
519
520 fn build_back_lut(
521 groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)],
522 ) -> HashMap<SourceItemOrderingModuleItemKind, String> {
523 let mut lut = HashMap::new();
524 for (group_name, items) in groups {
525 for &item in items {
526 lut.insert(item, group_name.clone());
527 }
528 }
529 lut
530 }
531
532 pub fn grouping_name_of(&self, item: SourceItemOrderingModuleItemKind) -> Option<&String> {
533 self.back_lut.get(&item)
534 }
535
536 pub fn grouping_names(&self) -> Vec<String> {
537 self.groups.iter().map(|(name, _)| name.clone()).collect()
538 }
539
540 pub fn is_grouping(&self, grouping: &str) -> bool {
541 self.groups.iter().any(|(g, _)| g == grouping)
542 }
543
544 pub fn module_level_order_of(&self, item: SourceItemOrderingModuleItemKind) -> Option<usize> {
545 self.lut.get(&item).copied()
546 }
547}
548
549impl Deserialize for SourceItemOrderingModuleItemGroupings {
550 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
551 let Some(values) = value.as_ref().as_array() else {
552 dcx.span_err(value.span(), "expected an array");
553 return None;
554 };
555 let mut groups = Vec::with_capacity(values.len());
556 for value in values {
557 if let Some(values) = value.as_ref().as_array()
558 && let [value1, value2] = &**values
559 {
560 groups.push((
561 String::deserialize(dcx, value1)?,
562 Vec::<SourceItemOrderingModuleItemKind>::deserialize(dcx, value2)?,
563 ));
564 } else {
565 dcx.span_err(value.span(), "expected an array of length two");
566 return None;
567 }
568 }
569
570 let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum();
571 let lut = Self::build_lut(&groups);
572 let back_lut = Self::build_back_lut(&groups);
573
574 let mut expected_items = SourceItemOrderingModuleItemKind::all_variants();
575 for item in lut.keys() {
576 expected_items.retain(|i| i != item);
577 }
578
579 let all_items = SourceItemOrderingModuleItemKind::all_variants();
580 if expected_items.is_empty() && items_total == all_items.len() {
581 let Some(use_group_index) = lut.get(&SourceItemOrderingModuleItemKind::Use) else {
582 dcx.span_err(value.span(), "Error in internal LUT.");
583 return None;
584 };
585 let Some((_, use_group_items)) = groups.get(*use_group_index) else {
586 dcx.span_err(value.span(), "Error in internal LUT.");
587 return None;
588 };
589 if use_group_items.len() > 1 {
590 dcx.span_err(
591 value.span(),
592 "The group containing the \"use\" item kind may not contain any other item kinds. \
593 The \"use\" items will (generally) be sorted by rustfmt already. \
594 Therefore it makes no sense to implement linting rules that may conflict with rustfmt.",
595 );
596 return None;
597 }
598 Some(Self { groups, lut, back_lut })
599 } else if items_total != all_items.len() {
600 dcx.span_err(value.span(),
601 format!(
602 "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \
603 The module item kinds are: {all_items:?}"
604 )
605 );
606 None
607 } else {
608 dcx.span_err(value.span(),
609 format!(
610 "Not all module item kinds were part of the configured source ordering rule. \
611 All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
612 The module item kinds are: {all_items:?}"
613 )
614 );
615 None
616 }
617 }
618}
619impl FromDefault<()> for SourceItemOrderingModuleItemGroupings {
620 fn from_default((): ()) -> Self {
621 Self {
622 groups: vec![
623 (
624 "modules".into(),
625 vec![
626 SourceItemOrderingModuleItemKind::ExternCrate,
627 SourceItemOrderingModuleItemKind::Mod,
628 SourceItemOrderingModuleItemKind::ForeignMod,
629 ],
630 ),
631 ("use".into(), vec![SourceItemOrderingModuleItemKind::Use]),
632 ("macros".into(), vec![SourceItemOrderingModuleItemKind::Macro]),
633 ("global_asm".into(), vec![SourceItemOrderingModuleItemKind::GlobalAsm]),
634 (
635 "UPPER_SNAKE_CASE".into(),
636 vec![
637 SourceItemOrderingModuleItemKind::Static,
638 SourceItemOrderingModuleItemKind::Const,
639 ],
640 ),
641 (
642 "PascalCase".into(),
643 vec![
644 SourceItemOrderingModuleItemKind::TyAlias,
645 SourceItemOrderingModuleItemKind::Enum,
646 SourceItemOrderingModuleItemKind::Struct,
647 SourceItemOrderingModuleItemKind::Union,
648 SourceItemOrderingModuleItemKind::Trait,
649 SourceItemOrderingModuleItemKind::TraitAlias,
650 SourceItemOrderingModuleItemKind::Impl,
651 ],
652 ),
653 ("lower_snake_case".into(), vec![SourceItemOrderingModuleItemKind::Fn]),
654 ],
655 lut: HashMap::from_iter([
656 (SourceItemOrderingModuleItemKind::ExternCrate, 0),
657 (SourceItemOrderingModuleItemKind::Mod, 0),
658 (SourceItemOrderingModuleItemKind::ForeignMod, 0),
659 (SourceItemOrderingModuleItemKind::Use, 1),
660 (SourceItemOrderingModuleItemKind::Macro, 2),
661 (SourceItemOrderingModuleItemKind::GlobalAsm, 3),
662 (SourceItemOrderingModuleItemKind::Static, 4),
663 (SourceItemOrderingModuleItemKind::Const, 4),
664 (SourceItemOrderingModuleItemKind::TyAlias, 5),
665 (SourceItemOrderingModuleItemKind::Enum, 5),
666 (SourceItemOrderingModuleItemKind::Struct, 5),
667 (SourceItemOrderingModuleItemKind::Union, 5),
668 (SourceItemOrderingModuleItemKind::Trait, 5),
669 (SourceItemOrderingModuleItemKind::TraitAlias, 5),
670 (SourceItemOrderingModuleItemKind::Impl, 5),
671 (SourceItemOrderingModuleItemKind::Fn, 6),
672 ]),
673 back_lut: HashMap::from_iter([
674 (SourceItemOrderingModuleItemKind::ExternCrate, "modules".into()),
675 (SourceItemOrderingModuleItemKind::Mod, "modules".into()),
676 (SourceItemOrderingModuleItemKind::ForeignMod, "modules".into()),
677 (SourceItemOrderingModuleItemKind::Use, "use".into()),
678 (SourceItemOrderingModuleItemKind::Macro, "macros".into()),
679 (SourceItemOrderingModuleItemKind::GlobalAsm, "global_asm".into()),
680 (SourceItemOrderingModuleItemKind::Static, "UPPER_SNAKE_CASE".into()),
681 (SourceItemOrderingModuleItemKind::Const, "UPPER_SNAKE_CASE".into()),
682 (SourceItemOrderingModuleItemKind::TyAlias, "PascalCase".into()),
683 (SourceItemOrderingModuleItemKind::Enum, "PascalCase".into()),
684 (SourceItemOrderingModuleItemKind::Struct, "PascalCase".into()),
685 (SourceItemOrderingModuleItemKind::Union, "PascalCase".into()),
686 (SourceItemOrderingModuleItemKind::Trait, "PascalCase".into()),
687 (SourceItemOrderingModuleItemKind::TraitAlias, "PascalCase".into()),
688 (SourceItemOrderingModuleItemKind::Impl, "PascalCase".into()),
689 (SourceItemOrderingModuleItemKind::Fn, "lower_snake_case".into()),
690 ]),
691 }
692 }
693 fn display_default((): ()) -> impl Display {
694 r#"[["modules", ["extern_crate", "mod", "foreign_mod"]], ["use", ["use"]], ["macros", ["macro"]], ["global_asm", ["global_asm"]], ["UPPER_SNAKE_CASE", ["static", "const"]], ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], ["lower_snake_case", ["fn"]]]"#
695 }
696}
697impl DeserializeOrDefault<()> for SourceItemOrderingModuleItemGroupings {
698 fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>, default: ()) -> Self {
699 Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default))
700 }
701}
702
703conf_enum! {
704 #[derive(Debug, PartialEq)]
705 pub SourceItemOrderingTraitAssocItemKind {
706 Const("const"),
707 Fn("fn"),
708 Type("type"),
709 }
710}
711
712impl SourceItemOrderingTraitAssocItemKind {
713 pub fn all_variants() -> Vec<Self> {
714 #[allow(clippy::enum_glob_use)] use SourceItemOrderingTraitAssocItemKind::*;
716 vec![Const, Fn, Type]
717 }
718}
719
720#[derive(Clone)]
728pub struct SourceItemOrderingTraitAssocItemKinds(Vec<SourceItemOrderingTraitAssocItemKind>);
729
730impl fmt::Debug for SourceItemOrderingTraitAssocItemKinds {
731 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
732 self.0.fmt(f)
733 }
734}
735
736impl SourceItemOrderingTraitAssocItemKinds {
737 pub fn index_of(&self, item: SourceItemOrderingTraitAssocItemKind) -> Option<usize> {
738 self.0.iter().position(|&i| i == item)
739 }
740}
741
742impl Deserialize for SourceItemOrderingTraitAssocItemKinds {
743 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
744 let items = Vec::<SourceItemOrderingTraitAssocItemKind>::deserialize(dcx, value)?;
745
746 let mut expected_items = SourceItemOrderingTraitAssocItemKind::all_variants();
747 for item in &items {
748 expected_items.retain(|i| i != item);
749 }
750
751 let all_items = SourceItemOrderingTraitAssocItemKind::all_variants();
752 if expected_items.is_empty() && items.len() == all_items.len() {
753 Some(Self(items))
754 } else if items.len() != all_items.len() {
755 dcx.span_err(
756 value.span(),
757 format!(
758 "Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. \
759 The trait associated item kinds are: {all_items:?}",
760 )
761 );
762 None
763 } else {
764 dcx.span_err(
765 value.span(),
766 format!(
767 "Not all trait associated item kinds were part of the configured source ordering rule. \
768 All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
769 The trait associated item kinds are: {all_items:?}"
770 )
771 );
772 None
773 }
774 }
775}
776impl FromDefault<()> for SourceItemOrderingTraitAssocItemKinds {
777 fn from_default((): ()) -> Self {
778 Self(vec![
779 SourceItemOrderingTraitAssocItemKind::Const,
780 SourceItemOrderingTraitAssocItemKind::Type,
781 SourceItemOrderingTraitAssocItemKind::Fn,
782 ])
783 }
784 fn display_default((): ()) -> impl Display {
785 r#"["const", "type", "fn"]"#
786 }
787}
788impl DeserializeOrDefault<()> for SourceItemOrderingTraitAssocItemKinds {
789 fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>, default: ()) -> Self {
790 Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default))
791 }
792}
793
794#[derive(Clone, Debug)]
802pub enum SourceItemOrderingWithinModuleItemGroupings {
803 All,
805
806 None,
808
809 Custom(Vec<Spanned<String>>),
811}
812
813impl SourceItemOrderingWithinModuleItemGroupings {
814 pub fn ordered_within(&self, grouping_name: &String) -> bool {
815 match self {
816 SourceItemOrderingWithinModuleItemGroupings::All => true,
817 SourceItemOrderingWithinModuleItemGroupings::None => false,
818 SourceItemOrderingWithinModuleItemGroupings::Custom(groups) => {
819 groups.iter().any(|x| x.node == *grouping_name)
820 },
821 }
822 }
823
824 pub fn check_groupings(&self, sess: &Session, module_item_order_groupings: &SourceItemOrderingModuleItemGroupings) {
825 if let SourceItemOrderingWithinModuleItemGroupings::Custom(groupings) = self {
826 for grouping in groupings {
827 if !module_item_order_groupings.is_grouping(&grouping.node) {
828 let names = module_item_order_groupings
831 .groups
832 .iter()
833 .map(|(x, _)| &**x)
834 .collect::<Vec<_>>();
835 let suggestion = find_closest_match(&grouping.node, &names)
836 .map(|s| format!(" perhaps you meant `{s}`?"))
837 .unwrap_or_default();
838 let names = names.iter().map(|s| format!("`{s}`")).join(", ");
839 sess.dcx().span_err(grouping.span, format!(
840 "unknown ordering group: `{}` was not specified in `module-items-ordered-within-groupings`,{suggestion} expected one of: {names}",
841 grouping.node,
842 ));
843 }
844 }
845 }
846 }
847}
848
849impl Deserialize for SourceItemOrderingWithinModuleItemGroupings {
850 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
851 match value.as_ref() {
852 toml::de::DeValue::String(str_value) => match &**str_value {
853 "all" => Some(Self::All),
854 "none" => Some(Self::None),
855 _ => {
856 dcx.span_err(value.span(), "expected: `all`, `none` or a list of category names");
857 None
858 },
859 },
860 toml::de::DeValue::Array(_) => Vec::deserialize(dcx, value).map(Self::Custom),
861 _ => {
862 dcx.span_err(value.span(), "expected a string or an array of strings");
863 None
864 },
865 }
866 }
867}
868impl FromDefault<()> for SourceItemOrderingWithinModuleItemGroupings {
869 fn from_default((): ()) -> Self {
870 Self::None
871 }
872 fn display_default((): ()) -> impl Display {
873 r#""none""#
874 }
875}
876impl DeserializeOrDefault<()> for SourceItemOrderingWithinModuleItemGroupings {
877 fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>, default: ()) -> Self {
878 Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default))
879 }
880}
881
882conf_enum! {
883 #[derive(Debug, PartialEq, Eq, Hash)]
884 pub InherentImplLintScope {
885 Crate("crate"),
886 File("file"),
887 Module("module"),
888 }
889}
890
891conf_enum! {
892 #[derive(Debug, PartialEq, Eq, Hash)]
893 pub TraitImplItemOrder {
894 Alphabetical("alphabetical"),
895 TraitItemOrdering("trait_item_ordering"),
896 AlphabeticalOrTraitItemOrdering("alphabetical_or_trait_item_ordering"),
897 }
898}
899impl FromDefault<()> for TraitImplItemOrder {
900 fn from_default((): ()) -> Self {
901 Self::Alphabetical
902 }
903 fn display_default((): ()) -> impl Display {
904 r#""alphabetical""#
905 }
906}
907impl DeserializeOrDefault<()> for TraitImplItemOrder {
908 fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>, default: ()) -> Self {
909 Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default))
910 }
911}