clippy_config/
types.rs

1use clippy_utils::def_path_def_ids;
2use rustc_errors::{Applicability, Diag};
3use rustc_hir::def_id::DefIdMap;
4use rustc_middle::ty::TyCtxt;
5use rustc_span::Span;
6use serde::de::{self, Deserializer, Visitor};
7use serde::{Deserialize, Serialize, ser};
8use std::collections::HashMap;
9use std::fmt;
10
11#[derive(Debug, Deserialize)]
12pub struct Rename {
13    pub path: String,
14    pub rename: String,
15}
16
17pub type DisallowedPathWithoutReplacement = DisallowedPath<false>;
18
19#[derive(Debug, Serialize)]
20pub struct DisallowedPath<const REPLACEMENT_ALLOWED: bool = true> {
21    path: String,
22    reason: Option<String>,
23    replacement: Option<String>,
24}
25
26impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath<REPLACEMENT_ALLOWED> {
27    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
28    where
29        D: Deserializer<'de>,
30    {
31        let enum_ = DisallowedPathEnum::deserialize(deserializer)?;
32        if !REPLACEMENT_ALLOWED && enum_.replacement().is_some() {
33            return Err(de::Error::custom("replacement not allowed for this configuration"));
34        }
35        Ok(Self {
36            path: enum_.path().to_owned(),
37            reason: enum_.reason().map(ToOwned::to_owned),
38            replacement: enum_.replacement().map(ToOwned::to_owned),
39        })
40    }
41}
42
43// `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just
44// above. `DisallowedPathEnum` is not meant to be used outside of this file.
45#[derive(Debug, Deserialize, Serialize)]
46#[serde(untagged)]
47enum DisallowedPathEnum {
48    Simple(String),
49    WithReason {
50        path: String,
51        reason: Option<String>,
52        replacement: Option<String>,
53    },
54}
55
56impl<const REPLACEMENT_ALLOWED: bool> DisallowedPath<REPLACEMENT_ALLOWED> {
57    pub fn path(&self) -> &str {
58        &self.path
59    }
60
61    pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) + use<'_, REPLACEMENT_ALLOWED> {
62        move |diag| {
63            if let Some(replacement) = &self.replacement {
64                diag.span_suggestion(
65                    span,
66                    self.reason.as_ref().map_or_else(|| String::from("use"), Clone::clone),
67                    replacement,
68                    Applicability::MachineApplicable,
69                );
70            } else if let Some(reason) = &self.reason {
71                diag.note(reason.clone());
72            }
73        }
74    }
75}
76
77impl DisallowedPathEnum {
78    pub fn path(&self) -> &str {
79        let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
80
81        path
82    }
83
84    fn reason(&self) -> Option<&str> {
85        match &self {
86            Self::WithReason { reason, .. } => reason.as_deref(),
87            Self::Simple(_) => None,
88        }
89    }
90
91    fn replacement(&self) -> Option<&str> {
92        match &self {
93            Self::WithReason { replacement, .. } => replacement.as_deref(),
94            Self::Simple(_) => None,
95        }
96    }
97}
98
99/// Creates a map of disallowed items to the reason they were disallowed.
100pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
101    tcx: TyCtxt<'_>,
102    disallowed: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
103) -> DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> {
104    disallowed
105        .iter()
106        .map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x))
107        .flat_map(|(name, path, disallowed_path)| {
108            def_path_def_ids(tcx, &path).map(move |id| (id, (name, disallowed_path)))
109        })
110        .collect()
111}
112
113#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
114pub enum MatchLintBehaviour {
115    AllTypes,
116    WellKnownTypes,
117    Never,
118}
119
120#[derive(Debug)]
121pub struct MacroMatcher {
122    pub name: String,
123    pub braces: (char, char),
124}
125
126impl<'de> Deserialize<'de> for MacroMatcher {
127    fn deserialize<D>(deser: D) -> Result<Self, D::Error>
128    where
129        D: Deserializer<'de>,
130    {
131        #[derive(Deserialize)]
132        #[serde(field_identifier, rename_all = "lowercase")]
133        enum Field {
134            Name,
135            Brace,
136        }
137        struct MacVisitor;
138        impl<'de> Visitor<'de> for MacVisitor {
139            type Value = MacroMatcher;
140
141            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
142                formatter.write_str("struct MacroMatcher")
143            }
144
145            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
146            where
147                V: de::MapAccess<'de>,
148            {
149                let mut name = None;
150                let mut brace: Option<char> = None;
151                while let Some(key) = map.next_key()? {
152                    match key {
153                        Field::Name => {
154                            if name.is_some() {
155                                return Err(de::Error::duplicate_field("name"));
156                            }
157                            name = Some(map.next_value()?);
158                        },
159                        Field::Brace => {
160                            if brace.is_some() {
161                                return Err(de::Error::duplicate_field("brace"));
162                            }
163                            brace = Some(map.next_value()?);
164                        },
165                    }
166                }
167                let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
168                let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?;
169                Ok(MacroMatcher {
170                    name,
171                    braces: [('(', ')'), ('{', '}'), ('[', ']')]
172                        .into_iter()
173                        .find(|b| b.0 == brace)
174                        .map(|(o, c)| (o.to_owned(), c.to_owned()))
175                        .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?,
176                })
177            }
178        }
179
180        const FIELDS: &[&str] = &["name", "brace"];
181        deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor)
182    }
183}
184
185/// Represents the item categories that can be ordered by the source ordering lint.
186#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
187#[serde(rename_all = "snake_case")]
188pub enum SourceItemOrderingCategory {
189    Enum,
190    Impl,
191    Module,
192    Struct,
193    Trait,
194}
195
196/// Represents which item categories are enabled for ordering.
197///
198/// The [`Deserialize`] implementation checks that there are no duplicates in
199/// the user configuration.
200pub struct SourceItemOrdering(Vec<SourceItemOrderingCategory>);
201
202impl SourceItemOrdering {
203    pub fn contains(&self, category: &SourceItemOrderingCategory) -> bool {
204        self.0.contains(category)
205    }
206}
207
208impl<T> From<T> for SourceItemOrdering
209where
210    T: Into<Vec<SourceItemOrderingCategory>>,
211{
212    fn from(value: T) -> Self {
213        Self(value.into())
214    }
215}
216
217impl core::fmt::Debug for SourceItemOrdering {
218    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219        self.0.fmt(f)
220    }
221}
222
223impl<'de> Deserialize<'de> for SourceItemOrdering {
224    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
225    where
226        D: Deserializer<'de>,
227    {
228        let items = Vec::<SourceItemOrderingCategory>::deserialize(deserializer)?;
229        let mut items_set = std::collections::HashSet::new();
230
231        for item in &items {
232            if items_set.contains(item) {
233                return Err(de::Error::custom(format!(
234                    "The category \"{item:?}\" was enabled more than once in the source ordering configuration."
235                )));
236            }
237            items_set.insert(item);
238        }
239
240        Ok(Self(items))
241    }
242}
243
244impl Serialize for SourceItemOrdering {
245    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
246    where
247        S: ser::Serializer,
248    {
249        self.0.serialize(serializer)
250    }
251}
252
253/// Represents the items that can occur within a module.
254#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
255#[serde(rename_all = "snake_case")]
256pub enum SourceItemOrderingModuleItemKind {
257    ExternCrate,
258    Mod,
259    ForeignMod,
260    Use,
261    Macro,
262    GlobalAsm,
263    Static,
264    Const,
265    TyAlias,
266    Enum,
267    Struct,
268    Union,
269    Trait,
270    TraitAlias,
271    Impl,
272    Fn,
273}
274
275impl SourceItemOrderingModuleItemKind {
276    pub fn all_variants() -> Vec<Self> {
277        #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
278        use SourceItemOrderingModuleItemKind::*;
279        vec![
280            ExternCrate,
281            Mod,
282            ForeignMod,
283            Use,
284            Macro,
285            GlobalAsm,
286            Static,
287            Const,
288            TyAlias,
289            Enum,
290            Struct,
291            Union,
292            Trait,
293            TraitAlias,
294            Impl,
295            Fn,
296        ]
297    }
298}
299
300/// Represents the configured ordering of items within a module.
301///
302/// The [`Deserialize`] implementation checks that no item kinds have been
303/// omitted and that there are no duplicates in the user configuration.
304#[derive(Clone)]
305pub struct SourceItemOrderingModuleItemGroupings {
306    groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)>,
307    lut: HashMap<SourceItemOrderingModuleItemKind, usize>,
308    back_lut: HashMap<SourceItemOrderingModuleItemKind, String>,
309}
310
311impl SourceItemOrderingModuleItemGroupings {
312    fn build_lut(
313        groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)],
314    ) -> HashMap<SourceItemOrderingModuleItemKind, usize> {
315        let mut lut = HashMap::new();
316        for (group_index, (_, items)) in groups.iter().enumerate() {
317            for item in items {
318                lut.insert(item.clone(), group_index);
319            }
320        }
321        lut
322    }
323
324    fn build_back_lut(
325        groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)],
326    ) -> HashMap<SourceItemOrderingModuleItemKind, String> {
327        let mut lut = HashMap::new();
328        for (group_name, items) in groups {
329            for item in items {
330                lut.insert(item.clone(), group_name.clone());
331            }
332        }
333        lut
334    }
335
336    pub fn grouping_name_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<&String> {
337        self.back_lut.get(item)
338    }
339
340    pub fn grouping_names(&self) -> Vec<String> {
341        self.groups.iter().map(|(name, _)| name.clone()).collect()
342    }
343
344    pub fn is_grouping(&self, grouping: &str) -> bool {
345        self.groups.iter().any(|(g, _)| g == grouping)
346    }
347
348    pub fn module_level_order_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<usize> {
349        self.lut.get(item).copied()
350    }
351}
352
353impl From<&[(&str, &[SourceItemOrderingModuleItemKind])]> for SourceItemOrderingModuleItemGroupings {
354    fn from(value: &[(&str, &[SourceItemOrderingModuleItemKind])]) -> Self {
355        let groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)> =
356            value.iter().map(|item| (item.0.to_string(), item.1.to_vec())).collect();
357        let lut = Self::build_lut(&groups);
358        let back_lut = Self::build_back_lut(&groups);
359        Self { groups, lut, back_lut }
360    }
361}
362
363impl core::fmt::Debug for SourceItemOrderingModuleItemGroupings {
364    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365        self.groups.fmt(f)
366    }
367}
368
369impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings {
370    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
371    where
372        D: Deserializer<'de>,
373    {
374        let groups = Vec::<(String, Vec<SourceItemOrderingModuleItemKind>)>::deserialize(deserializer)?;
375        let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum();
376        let lut = Self::build_lut(&groups);
377        let back_lut = Self::build_back_lut(&groups);
378
379        let mut expected_items = SourceItemOrderingModuleItemKind::all_variants();
380        for item in lut.keys() {
381            expected_items.retain(|i| i != item);
382        }
383
384        let all_items = SourceItemOrderingModuleItemKind::all_variants();
385        if expected_items.is_empty() && items_total == all_items.len() {
386            let Some(use_group_index) = lut.get(&SourceItemOrderingModuleItemKind::Use) else {
387                return Err(de::Error::custom("Error in internal LUT."));
388            };
389            let Some((_, use_group_items)) = groups.get(*use_group_index) else {
390                return Err(de::Error::custom("Error in internal LUT."));
391            };
392            if use_group_items.len() > 1 {
393                return Err(de::Error::custom(
394                    "The group containing the \"use\" item kind may not contain any other item kinds. \
395                    The \"use\" items will (generally) be sorted by rustfmt already. \
396                    Therefore it makes no sense to implement linting rules that may conflict with rustfmt.",
397                ));
398            }
399
400            Ok(Self { groups, lut, back_lut })
401        } else if items_total != all_items.len() {
402            Err(de::Error::custom(format!(
403                "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \
404                The module item kinds are: {all_items:?}"
405            )))
406        } else {
407            Err(de::Error::custom(format!(
408                "Not all module item kinds were part of the configured source ordering rule. \
409                All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
410                The module item kinds are: {all_items:?}"
411            )))
412        }
413    }
414}
415
416impl Serialize for SourceItemOrderingModuleItemGroupings {
417    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
418    where
419        S: ser::Serializer,
420    {
421        self.groups.serialize(serializer)
422    }
423}
424
425/// Represents all kinds of trait associated items.
426#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
427#[serde(rename_all = "snake_case")]
428pub enum SourceItemOrderingTraitAssocItemKind {
429    Const,
430    Fn,
431    Type,
432}
433
434impl SourceItemOrderingTraitAssocItemKind {
435    pub fn all_variants() -> Vec<Self> {
436        #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
437        use SourceItemOrderingTraitAssocItemKind::*;
438        vec![Const, Fn, Type]
439    }
440}
441
442/// Represents the order in which associated trait items should be ordered.
443///
444/// The reason to wrap a `Vec` in a newtype is to be able to implement
445/// [`Deserialize`]. Implementing `Deserialize` allows for implementing checks
446/// on configuration completeness at the time of loading the clippy config,
447/// letting the user know if there's any issues with the config (e.g. not
448/// listing all item kinds that should be sorted).
449#[derive(Clone)]
450pub struct SourceItemOrderingTraitAssocItemKinds(Vec<SourceItemOrderingTraitAssocItemKind>);
451
452impl SourceItemOrderingTraitAssocItemKinds {
453    pub fn index_of(&self, item: &SourceItemOrderingTraitAssocItemKind) -> Option<usize> {
454        self.0.iter().position(|i| i == item)
455    }
456}
457
458impl<T> From<T> for SourceItemOrderingTraitAssocItemKinds
459where
460    T: Into<Vec<SourceItemOrderingTraitAssocItemKind>>,
461{
462    fn from(value: T) -> Self {
463        Self(value.into())
464    }
465}
466
467impl core::fmt::Debug for SourceItemOrderingTraitAssocItemKinds {
468    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469        self.0.fmt(f)
470    }
471}
472
473impl<'de> Deserialize<'de> for SourceItemOrderingTraitAssocItemKinds {
474    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
475    where
476        D: Deserializer<'de>,
477    {
478        let items = Vec::<SourceItemOrderingTraitAssocItemKind>::deserialize(deserializer)?;
479
480        let mut expected_items = SourceItemOrderingTraitAssocItemKind::all_variants();
481        for item in &items {
482            expected_items.retain(|i| i != item);
483        }
484
485        let all_items = SourceItemOrderingTraitAssocItemKind::all_variants();
486        if expected_items.is_empty() && items.len() == all_items.len() {
487            Ok(Self(items))
488        } else if items.len() != all_items.len() {
489            Err(de::Error::custom(format!(
490                "Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. \
491                The trait associated item kinds are: {all_items:?}",
492            )))
493        } else {
494            Err(de::Error::custom(format!(
495                "Not all trait associated item kinds were part of the configured source ordering rule. \
496                All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
497                The trait associated item kinds are: {all_items:?}"
498            )))
499        }
500    }
501}
502
503impl Serialize for SourceItemOrderingTraitAssocItemKinds {
504    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
505    where
506        S: ser::Serializer,
507    {
508        self.0.serialize(serializer)
509    }
510}
511
512/// Describes which specific groupings should have their items ordered
513/// alphabetically.
514///
515/// This is separate from defining and enforcing groupings. For example,
516/// defining enums are grouped before structs still allows for an enum B to be
517/// placed before an enum A. Only when enforcing ordering within the grouping,
518/// will it be checked if A is placed before B.
519#[derive(Clone, Debug)]
520pub enum SourceItemOrderingWithinModuleItemGroupings {
521    /// All groupings should have their items ordered.
522    All,
523
524    /// None of the groupings should have their order checked.
525    None,
526
527    /// Only the specified groupings should have their order checked.
528    Custom(Vec<String>),
529}
530
531impl SourceItemOrderingWithinModuleItemGroupings {
532    pub fn ordered_within(&self, grouping_name: &String) -> bool {
533        match self {
534            SourceItemOrderingWithinModuleItemGroupings::All => true,
535            SourceItemOrderingWithinModuleItemGroupings::None => false,
536            SourceItemOrderingWithinModuleItemGroupings::Custom(groups) => groups.contains(grouping_name),
537        }
538    }
539}
540
541/// Helper struct for deserializing the [`SourceItemOrderingWithinModuleItemGroupings`].
542#[derive(Deserialize)]
543#[serde(untagged)]
544enum StringOrVecOfString {
545    String(String),
546    Vec(Vec<String>),
547}
548
549impl<'de> Deserialize<'de> for SourceItemOrderingWithinModuleItemGroupings {
550    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
551    where
552        D: Deserializer<'de>,
553    {
554        let description = "The available options for configuring an ordering within module item groups are: \
555                    \"all\", \"none\", or a list of module item group names \
556                    (as configured with the `module-item-order-groupings` configuration option).";
557
558        match StringOrVecOfString::deserialize(deserializer) {
559            Ok(StringOrVecOfString::String(preset)) if preset == "all" => {
560                Ok(SourceItemOrderingWithinModuleItemGroupings::All)
561            },
562            Ok(StringOrVecOfString::String(preset)) if preset == "none" => {
563                Ok(SourceItemOrderingWithinModuleItemGroupings::None)
564            },
565            Ok(StringOrVecOfString::String(preset)) => Err(de::Error::custom(format!(
566                "Unknown configuration option: {preset}.\n{description}"
567            ))),
568            Ok(StringOrVecOfString::Vec(groupings)) => {
569                Ok(SourceItemOrderingWithinModuleItemGroupings::Custom(groupings))
570            },
571            Err(e) => Err(de::Error::custom(format!("{e}\n{description}"))),
572        }
573    }
574}
575
576impl Serialize for SourceItemOrderingWithinModuleItemGroupings {
577    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
578    where
579        S: ser::Serializer,
580    {
581        match self {
582            SourceItemOrderingWithinModuleItemGroupings::All => serializer.serialize_str("all"),
583            SourceItemOrderingWithinModuleItemGroupings::None => serializer.serialize_str("none"),
584            SourceItemOrderingWithinModuleItemGroupings::Custom(vec) => vec.serialize(serializer),
585        }
586    }
587}
588
589// these impls are never actually called but are used by the various config options that default to
590// empty lists
591macro_rules! unimplemented_serialize {
592    ($($t:ty,)*) => {
593        $(
594            impl Serialize for $t {
595                fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
596                where
597                    S: ser::Serializer,
598                {
599                    Err(ser::Error::custom("unimplemented"))
600                }
601            }
602        )*
603    }
604}
605
606unimplemented_serialize! {
607    Rename,
608    MacroMatcher,
609}
610
611#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
612pub enum PubUnderscoreFieldsBehaviour {
613    PubliclyExported,
614    AllPubFields,
615}