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