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