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 TestBinderConstraints("test_binder_constraints"),
463 }
464}
465
466impl SourceItemOrderingModuleItemKind {
467 pub fn all_variants() -> Vec<Self> {
468 #[allow(clippy::enum_glob_use)] use SourceItemOrderingModuleItemKind::*;
470 vec![
471 ExternCrate,
472 Mod,
473 ForeignMod,
474 Use,
475 Macro,
476 GlobalAsm,
477 Static,
478 Const,
479 TyAlias,
480 Enum,
481 Struct,
482 Union,
483 Trait,
484 TraitAlias,
485 Impl,
486 Fn,
487 TestBinderConstraints,
488 ]
489 }
490}
491
492#[derive(Clone)]
497pub struct SourceItemOrderingModuleItemGroupings {
498 groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)>,
499 lut: HashMap<SourceItemOrderingModuleItemKind, usize>,
500 back_lut: HashMap<SourceItemOrderingModuleItemKind, String>,
501}
502
503impl fmt::Debug for SourceItemOrderingModuleItemGroupings {
504 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505 self.groups.fmt(f)
506 }
507}
508
509impl SourceItemOrderingModuleItemGroupings {
510 fn build_lut(
511 groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)],
512 ) -> HashMap<SourceItemOrderingModuleItemKind, usize> {
513 let mut lut = HashMap::new();
514 for (group_index, (_, items)) in groups.iter().enumerate() {
515 for &item in items {
516 lut.insert(item, group_index);
517 }
518 }
519 lut
520 }
521
522 fn build_back_lut(
523 groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)],
524 ) -> HashMap<SourceItemOrderingModuleItemKind, String> {
525 let mut lut = HashMap::new();
526 for (group_name, items) in groups {
527 for &item in items {
528 lut.insert(item, group_name.clone());
529 }
530 }
531 lut
532 }
533
534 pub fn grouping_name_of(&self, item: SourceItemOrderingModuleItemKind) -> Option<&String> {
535 self.back_lut.get(&item)
536 }
537
538 pub fn grouping_names(&self) -> Vec<String> {
539 self.groups.iter().map(|(name, _)| name.clone()).collect()
540 }
541
542 pub fn is_grouping(&self, grouping: &str) -> bool {
543 self.groups.iter().any(|(g, _)| g == grouping)
544 }
545
546 pub fn module_level_order_of(&self, item: SourceItemOrderingModuleItemKind) -> Option<usize> {
547 self.lut.get(&item).copied()
548 }
549}
550
551impl Deserialize for SourceItemOrderingModuleItemGroupings {
552 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
553 let Some(values) = value.as_ref().as_array() else {
554 dcx.span_err(value.span(), "expected an array");
555 return None;
556 };
557 let mut groups = Vec::with_capacity(values.len());
558 for value in values {
559 if let Some(values) = value.as_ref().as_array()
560 && let [value1, value2] = &**values
561 {
562 groups.push((
563 String::deserialize(dcx, value1)?,
564 Vec::<SourceItemOrderingModuleItemKind>::deserialize(dcx, value2)?,
565 ));
566 } else {
567 dcx.span_err(value.span(), "expected an array of length two");
568 return None;
569 }
570 }
571
572 let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum();
573 let lut = Self::build_lut(&groups);
574 let back_lut = Self::build_back_lut(&groups);
575
576 let mut expected_items = SourceItemOrderingModuleItemKind::all_variants();
577 for item in lut.keys() {
578 expected_items.retain(|i| i != item);
579 }
580
581 let all_items = SourceItemOrderingModuleItemKind::all_variants();
582 if expected_items.is_empty() && items_total == all_items.len() {
583 let Some(use_group_index) = lut.get(&SourceItemOrderingModuleItemKind::Use) else {
584 dcx.span_err(value.span(), "Error in internal LUT.");
585 return None;
586 };
587 let Some((_, use_group_items)) = groups.get(*use_group_index) else {
588 dcx.span_err(value.span(), "Error in internal LUT.");
589 return None;
590 };
591 if use_group_items.len() > 1 {
592 dcx.span_err(
593 value.span(),
594 "The group containing the \"use\" item kind may not contain any other item kinds. \
595 The \"use\" items will (generally) be sorted by rustfmt already. \
596 Therefore it makes no sense to implement linting rules that may conflict with rustfmt.",
597 );
598 return None;
599 }
600 Some(Self { groups, lut, back_lut })
601 } else if items_total != all_items.len() {
602 dcx.span_err(value.span(),
603 format!(
604 "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \
605 The module item kinds are: {all_items:?}"
606 )
607 );
608 None
609 } else {
610 dcx.span_err(value.span(),
611 format!(
612 "Not all module item kinds were part of the configured source ordering rule. \
613 All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
614 The module item kinds are: {all_items:?}"
615 )
616 );
617 None
618 }
619 }
620}
621impl FromDefault<()> for SourceItemOrderingModuleItemGroupings {
622 fn from_default((): ()) -> Self {
623 Self {
624 groups: vec![
625 (
626 "modules".into(),
627 vec![
628 SourceItemOrderingModuleItemKind::ExternCrate,
629 SourceItemOrderingModuleItemKind::Mod,
630 SourceItemOrderingModuleItemKind::ForeignMod,
631 ],
632 ),
633 ("use".into(), vec![SourceItemOrderingModuleItemKind::Use]),
634 ("macros".into(), vec![SourceItemOrderingModuleItemKind::Macro]),
635 ("global_asm".into(), vec![SourceItemOrderingModuleItemKind::GlobalAsm]),
636 (
637 "UPPER_SNAKE_CASE".into(),
638 vec![
639 SourceItemOrderingModuleItemKind::Static,
640 SourceItemOrderingModuleItemKind::Const,
641 ],
642 ),
643 (
644 "PascalCase".into(),
645 vec![
646 SourceItemOrderingModuleItemKind::TyAlias,
647 SourceItemOrderingModuleItemKind::Enum,
648 SourceItemOrderingModuleItemKind::Struct,
649 SourceItemOrderingModuleItemKind::Union,
650 SourceItemOrderingModuleItemKind::Trait,
651 SourceItemOrderingModuleItemKind::TraitAlias,
652 SourceItemOrderingModuleItemKind::Impl,
653 SourceItemOrderingModuleItemKind::TestBinderConstraints,
654 ],
655 ),
656 ("lower_snake_case".into(), vec![SourceItemOrderingModuleItemKind::Fn]),
657 ],
658 lut: HashMap::from_iter([
659 (SourceItemOrderingModuleItemKind::ExternCrate, 0),
660 (SourceItemOrderingModuleItemKind::Mod, 0),
661 (SourceItemOrderingModuleItemKind::ForeignMod, 0),
662 (SourceItemOrderingModuleItemKind::Use, 1),
663 (SourceItemOrderingModuleItemKind::Macro, 2),
664 (SourceItemOrderingModuleItemKind::GlobalAsm, 3),
665 (SourceItemOrderingModuleItemKind::Static, 4),
666 (SourceItemOrderingModuleItemKind::Const, 4),
667 (SourceItemOrderingModuleItemKind::TyAlias, 5),
668 (SourceItemOrderingModuleItemKind::Enum, 5),
669 (SourceItemOrderingModuleItemKind::Struct, 5),
670 (SourceItemOrderingModuleItemKind::Union, 5),
671 (SourceItemOrderingModuleItemKind::Trait, 5),
672 (SourceItemOrderingModuleItemKind::TraitAlias, 5),
673 (SourceItemOrderingModuleItemKind::Impl, 5),
674 (SourceItemOrderingModuleItemKind::TestBinderConstraints, 5),
675 (SourceItemOrderingModuleItemKind::Fn, 6),
676 ]),
677 back_lut: HashMap::from_iter([
678 (SourceItemOrderingModuleItemKind::ExternCrate, "modules".into()),
679 (SourceItemOrderingModuleItemKind::Mod, "modules".into()),
680 (SourceItemOrderingModuleItemKind::ForeignMod, "modules".into()),
681 (SourceItemOrderingModuleItemKind::Use, "use".into()),
682 (SourceItemOrderingModuleItemKind::Macro, "macros".into()),
683 (SourceItemOrderingModuleItemKind::GlobalAsm, "global_asm".into()),
684 (SourceItemOrderingModuleItemKind::Static, "UPPER_SNAKE_CASE".into()),
685 (SourceItemOrderingModuleItemKind::Const, "UPPER_SNAKE_CASE".into()),
686 (SourceItemOrderingModuleItemKind::TyAlias, "PascalCase".into()),
687 (SourceItemOrderingModuleItemKind::Enum, "PascalCase".into()),
688 (SourceItemOrderingModuleItemKind::Struct, "PascalCase".into()),
689 (SourceItemOrderingModuleItemKind::Union, "PascalCase".into()),
690 (SourceItemOrderingModuleItemKind::Trait, "PascalCase".into()),
691 (SourceItemOrderingModuleItemKind::TraitAlias, "PascalCase".into()),
692 (SourceItemOrderingModuleItemKind::Impl, "PascalCase".into()),
693 (
694 SourceItemOrderingModuleItemKind::TestBinderConstraints,
695 "PascalCase".into(),
696 ),
697 (SourceItemOrderingModuleItemKind::Fn, "lower_snake_case".into()),
698 ]),
699 }
700 }
701 fn display_default((): ()) -> impl Display {
702 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", "test_binder_constraints"]], ["lower_snake_case", ["fn"]]]"#
703 }
704}
705impl DeserializeOrDefault<()> for SourceItemOrderingModuleItemGroupings {
706 fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>, default: ()) -> Self {
707 Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default))
708 }
709}
710
711conf_enum! {
712 #[derive(Debug, PartialEq)]
713 pub SourceItemOrderingTraitAssocItemKind {
714 Const("const"),
715 Fn("fn"),
716 Type("type"),
717 }
718}
719
720impl SourceItemOrderingTraitAssocItemKind {
721 pub fn all_variants() -> Vec<Self> {
722 #[allow(clippy::enum_glob_use)] use SourceItemOrderingTraitAssocItemKind::*;
724 vec![Const, Fn, Type]
725 }
726}
727
728#[derive(Clone)]
736pub struct SourceItemOrderingTraitAssocItemKinds(Vec<SourceItemOrderingTraitAssocItemKind>);
737
738impl fmt::Debug for SourceItemOrderingTraitAssocItemKinds {
739 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
740 self.0.fmt(f)
741 }
742}
743
744impl SourceItemOrderingTraitAssocItemKinds {
745 pub fn index_of(&self, item: SourceItemOrderingTraitAssocItemKind) -> Option<usize> {
746 self.0.iter().position(|&i| i == item)
747 }
748}
749
750impl Deserialize for SourceItemOrderingTraitAssocItemKinds {
751 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
752 let items = Vec::<SourceItemOrderingTraitAssocItemKind>::deserialize(dcx, value)?;
753
754 let mut expected_items = SourceItemOrderingTraitAssocItemKind::all_variants();
755 for item in &items {
756 expected_items.retain(|i| i != item);
757 }
758
759 let all_items = SourceItemOrderingTraitAssocItemKind::all_variants();
760 if expected_items.is_empty() && items.len() == all_items.len() {
761 Some(Self(items))
762 } else if items.len() != all_items.len() {
763 dcx.span_err(
764 value.span(),
765 format!(
766 "Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. \
767 The trait associated item kinds are: {all_items:?}",
768 )
769 );
770 None
771 } else {
772 dcx.span_err(
773 value.span(),
774 format!(
775 "Not all trait associated item kinds were part of the configured source ordering rule. \
776 All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
777 The trait associated item kinds are: {all_items:?}"
778 )
779 );
780 None
781 }
782 }
783}
784impl FromDefault<()> for SourceItemOrderingTraitAssocItemKinds {
785 fn from_default((): ()) -> Self {
786 Self(vec![
787 SourceItemOrderingTraitAssocItemKind::Const,
788 SourceItemOrderingTraitAssocItemKind::Type,
789 SourceItemOrderingTraitAssocItemKind::Fn,
790 ])
791 }
792 fn display_default((): ()) -> impl Display {
793 r#"["const", "type", "fn"]"#
794 }
795}
796impl DeserializeOrDefault<()> for SourceItemOrderingTraitAssocItemKinds {
797 fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>, default: ()) -> Self {
798 Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default))
799 }
800}
801
802#[derive(Clone, Debug)]
810pub enum SourceItemOrderingWithinModuleItemGroupings {
811 All,
813
814 None,
816
817 Custom(Vec<Spanned<String>>),
819}
820
821impl SourceItemOrderingWithinModuleItemGroupings {
822 pub fn ordered_within(&self, grouping_name: &String) -> bool {
823 match self {
824 SourceItemOrderingWithinModuleItemGroupings::All => true,
825 SourceItemOrderingWithinModuleItemGroupings::None => false,
826 SourceItemOrderingWithinModuleItemGroupings::Custom(groups) => {
827 groups.iter().any(|x| x.node == *grouping_name)
828 },
829 }
830 }
831
832 pub fn check_groupings(&self, sess: &Session, module_item_order_groupings: &SourceItemOrderingModuleItemGroupings) {
833 if let SourceItemOrderingWithinModuleItemGroupings::Custom(groupings) = self {
834 for grouping in groupings {
835 if !module_item_order_groupings.is_grouping(&grouping.node) {
836 let names = module_item_order_groupings
839 .groups
840 .iter()
841 .map(|(x, _)| &**x)
842 .collect::<Vec<_>>();
843 let suggestion = find_closest_match(&grouping.node, &names)
844 .map(|s| format!(" perhaps you meant `{s}`?"))
845 .unwrap_or_default();
846 let names = names.iter().map(|s| format!("`{s}`")).join(", ");
847 sess.dcx().span_err(grouping.span, format!(
848 "unknown ordering group: `{}` was not specified in `module-items-ordered-within-groupings`,{suggestion} expected one of: {names}",
849 grouping.node,
850 ));
851 }
852 }
853 }
854 }
855}
856
857impl Deserialize for SourceItemOrderingWithinModuleItemGroupings {
858 fn deserialize(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>) -> Option<Self> {
859 match value.as_ref() {
860 toml::de::DeValue::String(str_value) => match &**str_value {
861 "all" => Some(Self::All),
862 "none" => Some(Self::None),
863 _ => {
864 dcx.span_err(value.span(), "expected: `all`, `none` or a list of category names");
865 None
866 },
867 },
868 toml::de::DeValue::Array(_) => Vec::deserialize(dcx, value).map(Self::Custom),
869 _ => {
870 dcx.span_err(value.span(), "expected a string or an array of strings");
871 None
872 },
873 }
874 }
875}
876impl FromDefault<()> for SourceItemOrderingWithinModuleItemGroupings {
877 fn from_default((): ()) -> Self {
878 Self::None
879 }
880 fn display_default((): ()) -> impl Display {
881 r#""none""#
882 }
883}
884impl DeserializeOrDefault<()> for SourceItemOrderingWithinModuleItemGroupings {
885 fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>, default: ()) -> Self {
886 Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default))
887 }
888}
889
890conf_enum! {
891 #[derive(Debug, PartialEq, Eq, Hash)]
892 pub InherentImplLintScope {
893 Crate("crate"),
894 File("file"),
895 Module("module"),
896 }
897}
898
899conf_enum! {
900 #[derive(Debug, PartialEq, Eq, Hash)]
901 pub TraitImplItemOrder {
902 Alphabetical("alphabetical"),
903 TraitItemOrdering("trait_item_ordering"),
904 AlphabeticalOrTraitItemOrdering("alphabetical_or_trait_item_ordering"),
905 }
906}
907impl FromDefault<()> for TraitImplItemOrder {
908 fn from_default((): ()) -> Self {
909 Self::Alphabetical
910 }
911 fn display_default((): ()) -> impl Display {
912 r#""alphabetical""#
913 }
914}
915impl DeserializeOrDefault<()> for TraitImplItemOrder {
916 fn deserialize_or_default(dcx: &DiagCtxt<'_>, value: &TomlValue<'_>, default: ()) -> Self {
917 Self::deserialize(dcx, value).unwrap_or_else(|| Self::from_default(default))
918 }
919}