1use clippy_utils::paths::{PathNS, find_crates, lookup_path};
2use rustc_data_structures::fx::FxHashMap;
3use rustc_errors::{Applicability, Diag};
4use rustc_hir::PrimTy;
5use rustc_hir::def::DefKind;
6use rustc_hir::def_id::DefIdMap;
7use rustc_middle::ty::TyCtxt;
8use rustc_span::{Span, Symbol};
9use serde::de::{self, Deserializer, Visitor};
10use serde::{Deserialize, Serialize, ser};
11use std::collections::HashMap;
12use std::fmt;
13
14#[derive(Debug, Deserialize)]
15#[serde(deny_unknown_fields)]
16pub struct Rename {
17 pub path: String,
18 pub rename: String,
19}
20
21pub type DisallowedPathWithoutReplacement = DisallowedPath<false>;
22
23#[derive(Debug, Serialize)]
24pub struct DisallowedPath<const REPLACEMENT_ALLOWED: bool = true> {
25 path: String,
26 reason: Option<String>,
27 replacement: Option<String>,
28 allow_invalid: bool,
34 #[serde(skip_serializing)]
38 span: Span,
39}
40
41impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath<REPLACEMENT_ALLOWED> {
42 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
43 where
44 D: Deserializer<'de>,
45 {
46 let enum_ = DisallowedPathEnum::deserialize(deserializer)?;
47 if !REPLACEMENT_ALLOWED && enum_.replacement().is_some() {
48 return Err(de::Error::custom("replacement not allowed for this configuration"));
49 }
50 Ok(Self {
51 path: enum_.path().to_owned(),
52 reason: enum_.reason().map(ToOwned::to_owned),
53 replacement: enum_.replacement().map(ToOwned::to_owned),
54 allow_invalid: enum_.allow_invalid(),
55 span: Span::default(),
56 })
57 }
58}
59
60#[derive(Debug, Deserialize, Serialize)]
63#[serde(untagged, deny_unknown_fields)]
64enum DisallowedPathEnum {
65 Simple(String),
66 WithReason {
67 path: String,
68 reason: Option<String>,
69 replacement: Option<String>,
70 #[serde(rename = "allow-invalid")]
71 allow_invalid: Option<bool>,
72 },
73}
74
75impl<const REPLACEMENT_ALLOWED: bool> DisallowedPath<REPLACEMENT_ALLOWED> {
76 pub fn path(&self) -> &str {
77 &self.path
78 }
79
80 pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) {
81 move |diag| {
82 if let Some(replacement) = &self.replacement {
83 diag.span_suggestion(
84 span,
85 self.reason.as_ref().map_or_else(|| String::from("use"), Clone::clone),
86 replacement,
87 Applicability::MachineApplicable,
88 );
89 } else if let Some(reason) = &self.reason {
90 diag.note(reason.clone());
91 }
92 }
93 }
94
95 pub fn span(&self) -> Span {
96 self.span
97 }
98
99 pub fn set_span(&mut self, span: Span) {
100 self.span = span;
101 }
102}
103
104impl DisallowedPathEnum {
105 pub fn path(&self) -> &str {
106 let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
107
108 path
109 }
110
111 fn reason(&self) -> Option<&str> {
112 match &self {
113 Self::WithReason { reason, .. } => reason.as_deref(),
114 Self::Simple(_) => None,
115 }
116 }
117
118 fn replacement(&self) -> Option<&str> {
119 match &self {
120 Self::WithReason { replacement, .. } => replacement.as_deref(),
121 Self::Simple(_) => None,
122 }
123 }
124
125 fn allow_invalid(&self) -> bool {
126 match &self {
127 Self::WithReason { allow_invalid, .. } => allow_invalid.unwrap_or_default(),
128 Self::Simple(_) => false,
129 }
130 }
131}
132
133#[allow(clippy::type_complexity)]
135pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
136 tcx: TyCtxt<'_>,
137 disallowed_paths: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
138 ns: PathNS,
139 def_kind_predicate: impl Fn(DefKind) -> bool,
140 predicate_description: &str,
141 allow_prim_tys: bool,
142) -> (
143 DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)>,
144 FxHashMap<PrimTy, (&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)>,
145) {
146 let mut def_ids: DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> = DefIdMap::default();
147 let mut prim_tys: FxHashMap<PrimTy, (&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> =
148 FxHashMap::default();
149 for disallowed_path in disallowed_paths {
150 let path = disallowed_path.path();
151 let sym_path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect();
152 let mut resolutions = lookup_path(tcx, ns, &sym_path);
153 resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id)));
154
155 let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice()
156 && let Some(prim) = PrimTy::from_name(name)
157 {
158 (allow_prim_tys.then_some(prim), true)
159 } else {
160 (None, false)
161 };
162
163 if resolutions.is_empty()
164 && prim_ty.is_none()
165 && !disallowed_path.allow_invalid
166 && (sym_path.len() < 2 || !find_crates(tcx, sym_path[0]).is_empty())
169 {
170 let found_def_ids = lookup_path(tcx, PathNS::Arbitrary, &sym_path);
172 let message = if let Some(&def_id) = found_def_ids.first() {
173 let (article, description) = tcx.article_and_description(def_id);
174 format!("expected a {predicate_description}, found {article} {description}")
175 } else if found_prim_ty {
176 format!("expected a {predicate_description}, found a primitive type")
177 } else {
178 format!("`{path}` does not refer to a reachable {predicate_description}")
179 };
180 tcx.sess
181 .dcx()
182 .struct_span_warn(disallowed_path.span(), message)
183 .with_help("add `allow-invalid = true` to the entry to suppress this warning")
184 .emit();
185 }
186
187 for def_id in resolutions {
188 def_ids.insert(def_id, (path, disallowed_path));
189 }
190 if let Some(ty) = prim_ty {
191 prim_tys.insert(ty, (path, disallowed_path));
192 }
193 }
194
195 (def_ids, prim_tys)
196}
197
198#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
199pub enum MatchLintBehaviour {
200 AllTypes,
201 WellKnownTypes,
202 Never,
203}
204
205#[derive(Debug)]
206pub struct MacroMatcher {
207 pub name: String,
208 pub braces: (char, char),
209}
210
211impl<'de> Deserialize<'de> for MacroMatcher {
212 fn deserialize<D>(deser: D) -> Result<Self, D::Error>
213 where
214 D: Deserializer<'de>,
215 {
216 #[derive(Deserialize)]
217 #[serde(field_identifier, rename_all = "lowercase")]
218 enum Field {
219 Name,
220 Brace,
221 }
222 struct MacVisitor;
223 impl<'de> Visitor<'de> for MacVisitor {
224 type Value = MacroMatcher;
225
226 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
227 formatter.write_str("struct MacroMatcher")
228 }
229
230 fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
231 where
232 V: de::MapAccess<'de>,
233 {
234 let mut name = None;
235 let mut brace: Option<char> = None;
236 while let Some(key) = map.next_key()? {
237 match key {
238 Field::Name => {
239 if name.is_some() {
240 return Err(de::Error::duplicate_field("name"));
241 }
242 name = Some(map.next_value()?);
243 },
244 Field::Brace => {
245 if brace.is_some() {
246 return Err(de::Error::duplicate_field("brace"));
247 }
248 brace = Some(map.next_value()?);
249 },
250 }
251 }
252 let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
253 let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?;
254 Ok(MacroMatcher {
255 name,
256 braces: [('(', ')'), ('{', '}'), ('[', ']')]
257 .into_iter()
258 .find(|b| b.0 == brace)
259 .map(|(o, c)| (o.to_owned(), c.to_owned()))
260 .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?,
261 })
262 }
263 }
264
265 const FIELDS: &[&str] = &["name", "brace"];
266 deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor)
267 }
268}
269
270#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
272#[serde(rename_all = "snake_case")]
273pub enum SourceItemOrderingCategory {
274 Enum,
275 Impl,
276 Module,
277 Struct,
278 Trait,
279}
280
281pub struct SourceItemOrdering(Vec<SourceItemOrderingCategory>);
286
287impl SourceItemOrdering {
288 pub fn contains(&self, category: &SourceItemOrderingCategory) -> bool {
289 self.0.contains(category)
290 }
291}
292
293impl<T> From<T> for SourceItemOrdering
294where
295 T: Into<Vec<SourceItemOrderingCategory>>,
296{
297 fn from(value: T) -> Self {
298 Self(value.into())
299 }
300}
301
302impl core::fmt::Debug for SourceItemOrdering {
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 self.0.fmt(f)
305 }
306}
307
308impl<'de> Deserialize<'de> for SourceItemOrdering {
309 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
310 where
311 D: Deserializer<'de>,
312 {
313 let items = Vec::<SourceItemOrderingCategory>::deserialize(deserializer)?;
314 let mut items_set = std::collections::HashSet::new();
315
316 for item in &items {
317 if items_set.contains(item) {
318 return Err(de::Error::custom(format!(
319 "The category \"{item:?}\" was enabled more than once in the source ordering configuration."
320 )));
321 }
322 items_set.insert(item);
323 }
324
325 Ok(Self(items))
326 }
327}
328
329impl Serialize for SourceItemOrdering {
330 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
331 where
332 S: ser::Serializer,
333 {
334 self.0.serialize(serializer)
335 }
336}
337
338#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
340#[serde(rename_all = "snake_case")]
341pub enum SourceItemOrderingModuleItemKind {
342 ExternCrate,
343 Mod,
344 ForeignMod,
345 Use,
346 Macro,
347 GlobalAsm,
348 Static,
349 Const,
350 TyAlias,
351 Enum,
352 Struct,
353 Union,
354 Trait,
355 TraitAlias,
356 Impl,
357 Fn,
358}
359
360impl SourceItemOrderingModuleItemKind {
361 pub fn all_variants() -> Vec<Self> {
362 #[allow(clippy::enum_glob_use)] use SourceItemOrderingModuleItemKind::*;
364 vec![
365 ExternCrate,
366 Mod,
367 ForeignMod,
368 Use,
369 Macro,
370 GlobalAsm,
371 Static,
372 Const,
373 TyAlias,
374 Enum,
375 Struct,
376 Union,
377 Trait,
378 TraitAlias,
379 Impl,
380 Fn,
381 ]
382 }
383}
384
385#[derive(Clone)]
390pub struct SourceItemOrderingModuleItemGroupings {
391 groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)>,
392 lut: HashMap<SourceItemOrderingModuleItemKind, usize>,
393 back_lut: HashMap<SourceItemOrderingModuleItemKind, String>,
394}
395
396impl SourceItemOrderingModuleItemGroupings {
397 fn build_lut(
398 groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)],
399 ) -> HashMap<SourceItemOrderingModuleItemKind, usize> {
400 let mut lut = HashMap::new();
401 for (group_index, (_, items)) in groups.iter().enumerate() {
402 for item in items {
403 lut.insert(item.clone(), group_index);
404 }
405 }
406 lut
407 }
408
409 fn build_back_lut(
410 groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)],
411 ) -> HashMap<SourceItemOrderingModuleItemKind, String> {
412 let mut lut = HashMap::new();
413 for (group_name, items) in groups {
414 for item in items {
415 lut.insert(item.clone(), group_name.clone());
416 }
417 }
418 lut
419 }
420
421 pub fn grouping_name_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<&String> {
422 self.back_lut.get(item)
423 }
424
425 pub fn grouping_names(&self) -> Vec<String> {
426 self.groups.iter().map(|(name, _)| name.clone()).collect()
427 }
428
429 pub fn is_grouping(&self, grouping: &str) -> bool {
430 self.groups.iter().any(|(g, _)| g == grouping)
431 }
432
433 pub fn module_level_order_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<usize> {
434 self.lut.get(item).copied()
435 }
436}
437
438impl From<&[(&str, &[SourceItemOrderingModuleItemKind])]> for SourceItemOrderingModuleItemGroupings {
439 fn from(value: &[(&str, &[SourceItemOrderingModuleItemKind])]) -> Self {
440 let groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)> =
441 value.iter().map(|item| (item.0.to_string(), item.1.to_vec())).collect();
442 let lut = Self::build_lut(&groups);
443 let back_lut = Self::build_back_lut(&groups);
444 Self { groups, lut, back_lut }
445 }
446}
447
448impl core::fmt::Debug for SourceItemOrderingModuleItemGroupings {
449 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450 self.groups.fmt(f)
451 }
452}
453
454impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings {
455 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
456 where
457 D: Deserializer<'de>,
458 {
459 let groups = Vec::<(String, Vec<SourceItemOrderingModuleItemKind>)>::deserialize(deserializer)?;
460 let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum();
461 let lut = Self::build_lut(&groups);
462 let back_lut = Self::build_back_lut(&groups);
463
464 let mut expected_items = SourceItemOrderingModuleItemKind::all_variants();
465 for item in lut.keys() {
466 expected_items.retain(|i| i != item);
467 }
468
469 let all_items = SourceItemOrderingModuleItemKind::all_variants();
470 if expected_items.is_empty() && items_total == all_items.len() {
471 let Some(use_group_index) = lut.get(&SourceItemOrderingModuleItemKind::Use) else {
472 return Err(de::Error::custom("Error in internal LUT."));
473 };
474 let Some((_, use_group_items)) = groups.get(*use_group_index) else {
475 return Err(de::Error::custom("Error in internal LUT."));
476 };
477 if use_group_items.len() > 1 {
478 return Err(de::Error::custom(
479 "The group containing the \"use\" item kind may not contain any other item kinds. \
480 The \"use\" items will (generally) be sorted by rustfmt already. \
481 Therefore it makes no sense to implement linting rules that may conflict with rustfmt.",
482 ));
483 }
484
485 Ok(Self { groups, lut, back_lut })
486 } else if items_total != all_items.len() {
487 Err(de::Error::custom(format!(
488 "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \
489 The module item kinds are: {all_items:?}"
490 )))
491 } else {
492 Err(de::Error::custom(format!(
493 "Not all module item kinds were part of the configured source ordering rule. \
494 All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
495 The module item kinds are: {all_items:?}"
496 )))
497 }
498 }
499}
500
501impl Serialize for SourceItemOrderingModuleItemGroupings {
502 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
503 where
504 S: ser::Serializer,
505 {
506 self.groups.serialize(serializer)
507 }
508}
509
510#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
512#[serde(rename_all = "snake_case")]
513pub enum SourceItemOrderingTraitAssocItemKind {
514 Const,
515 Fn,
516 Type,
517}
518
519impl SourceItemOrderingTraitAssocItemKind {
520 pub fn all_variants() -> Vec<Self> {
521 #[allow(clippy::enum_glob_use)] use SourceItemOrderingTraitAssocItemKind::*;
523 vec![Const, Fn, Type]
524 }
525}
526
527#[derive(Clone)]
535pub struct SourceItemOrderingTraitAssocItemKinds(Vec<SourceItemOrderingTraitAssocItemKind>);
536
537impl SourceItemOrderingTraitAssocItemKinds {
538 pub fn index_of(&self, item: &SourceItemOrderingTraitAssocItemKind) -> Option<usize> {
539 self.0.iter().position(|i| i == item)
540 }
541}
542
543impl<T> From<T> for SourceItemOrderingTraitAssocItemKinds
544where
545 T: Into<Vec<SourceItemOrderingTraitAssocItemKind>>,
546{
547 fn from(value: T) -> Self {
548 Self(value.into())
549 }
550}
551
552impl core::fmt::Debug for SourceItemOrderingTraitAssocItemKinds {
553 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
554 self.0.fmt(f)
555 }
556}
557
558impl<'de> Deserialize<'de> for SourceItemOrderingTraitAssocItemKinds {
559 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
560 where
561 D: Deserializer<'de>,
562 {
563 let items = Vec::<SourceItemOrderingTraitAssocItemKind>::deserialize(deserializer)?;
564
565 let mut expected_items = SourceItemOrderingTraitAssocItemKind::all_variants();
566 for item in &items {
567 expected_items.retain(|i| i != item);
568 }
569
570 let all_items = SourceItemOrderingTraitAssocItemKind::all_variants();
571 if expected_items.is_empty() && items.len() == all_items.len() {
572 Ok(Self(items))
573 } else if items.len() != all_items.len() {
574 Err(de::Error::custom(format!(
575 "Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. \
576 The trait associated item kinds are: {all_items:?}",
577 )))
578 } else {
579 Err(de::Error::custom(format!(
580 "Not all trait associated item kinds were part of the configured source ordering rule. \
581 All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
582 The trait associated item kinds are: {all_items:?}"
583 )))
584 }
585 }
586}
587
588impl Serialize for SourceItemOrderingTraitAssocItemKinds {
589 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
590 where
591 S: ser::Serializer,
592 {
593 self.0.serialize(serializer)
594 }
595}
596
597#[derive(Clone, Debug)]
605pub enum SourceItemOrderingWithinModuleItemGroupings {
606 All,
608
609 None,
611
612 Custom(Vec<String>),
614}
615
616impl SourceItemOrderingWithinModuleItemGroupings {
617 pub fn ordered_within(&self, grouping_name: &String) -> bool {
618 match self {
619 SourceItemOrderingWithinModuleItemGroupings::All => true,
620 SourceItemOrderingWithinModuleItemGroupings::None => false,
621 SourceItemOrderingWithinModuleItemGroupings::Custom(groups) => groups.contains(grouping_name),
622 }
623 }
624}
625
626#[derive(Deserialize)]
628#[serde(untagged)]
629enum StringOrVecOfString {
630 String(String),
631 Vec(Vec<String>),
632}
633
634impl<'de> Deserialize<'de> for SourceItemOrderingWithinModuleItemGroupings {
635 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
636 where
637 D: Deserializer<'de>,
638 {
639 let description = "The available options for configuring an ordering within module item groups are: \
640 \"all\", \"none\", or a list of module item group names \
641 (as configured with the `module-item-order-groupings` configuration option).";
642
643 match StringOrVecOfString::deserialize(deserializer) {
644 Ok(StringOrVecOfString::String(preset)) if preset == "all" => {
645 Ok(SourceItemOrderingWithinModuleItemGroupings::All)
646 },
647 Ok(StringOrVecOfString::String(preset)) if preset == "none" => {
648 Ok(SourceItemOrderingWithinModuleItemGroupings::None)
649 },
650 Ok(StringOrVecOfString::String(preset)) => Err(de::Error::custom(format!(
651 "Unknown configuration option: {preset}.\n{description}"
652 ))),
653 Ok(StringOrVecOfString::Vec(groupings)) => {
654 Ok(SourceItemOrderingWithinModuleItemGroupings::Custom(groupings))
655 },
656 Err(e) => Err(de::Error::custom(format!("{e}\n{description}"))),
657 }
658 }
659}
660
661impl Serialize for SourceItemOrderingWithinModuleItemGroupings {
662 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
663 where
664 S: ser::Serializer,
665 {
666 match self {
667 SourceItemOrderingWithinModuleItemGroupings::All => serializer.serialize_str("all"),
668 SourceItemOrderingWithinModuleItemGroupings::None => serializer.serialize_str("none"),
669 SourceItemOrderingWithinModuleItemGroupings::Custom(vec) => vec.serialize(serializer),
670 }
671 }
672}
673
674macro_rules! unimplemented_serialize {
677 ($($t:ty,)*) => {
678 $(
679 impl Serialize for $t {
680 fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
681 where
682 S: ser::Serializer,
683 {
684 Err(ser::Error::custom("unimplemented"))
685 }
686 }
687 )*
688 }
689}
690
691unimplemented_serialize! {
692 Rename,
693 MacroMatcher,
694}
695
696#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
697pub enum PubUnderscoreFieldsBehaviour {
698 PubliclyExported,
699 AllPubFields,
700}