clippy_config/
types.rs

1use clippy_utils::def_path_def_ids;
2use rustc_hir::def_id::DefIdMap;
3use rustc_middle::ty::TyCtxt;
4use serde::de::{self, Deserializer, Visitor};
5use serde::{Deserialize, Serialize, ser};
6use std::collections::HashMap;
7use std::fmt;
8
9#[derive(Debug, Deserialize)]
10pub struct Rename {
11    pub path: String,
12    pub rename: String,
13}
14
15#[derive(Debug, Deserialize)]
16#[serde(untagged)]
17pub enum DisallowedPath {
18    Simple(String),
19    WithReason { path: String, reason: Option<String> },
20}
21
22impl DisallowedPath {
23    pub fn path(&self) -> &str {
24        let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
25
26        path
27    }
28
29    pub fn reason(&self) -> Option<&str> {
30        match &self {
31            Self::WithReason { reason, .. } => reason.as_deref(),
32            Self::Simple(_) => None,
33        }
34    }
35}
36
37/// Creates a map of disallowed items to the reason they were disallowed.
38pub fn create_disallowed_map(
39    tcx: TyCtxt<'_>,
40    disallowed: &'static [DisallowedPath],
41) -> DefIdMap<(&'static str, Option<&'static str>)> {
42    disallowed
43        .iter()
44        .map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x.reason()))
45        .flat_map(|(name, path, reason)| def_path_def_ids(tcx, &path).map(move |id| (id, (name, reason))))
46        .collect()
47}
48
49#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
50pub enum MatchLintBehaviour {
51    AllTypes,
52    WellKnownTypes,
53    Never,
54}
55
56#[derive(Debug)]
57pub struct MacroMatcher {
58    pub name: String,
59    pub braces: (char, char),
60}
61
62impl<'de> Deserialize<'de> for MacroMatcher {
63    fn deserialize<D>(deser: D) -> Result<Self, D::Error>
64    where
65        D: Deserializer<'de>,
66    {
67        #[derive(Deserialize)]
68        #[serde(field_identifier, rename_all = "lowercase")]
69        enum Field {
70            Name,
71            Brace,
72        }
73        struct MacVisitor;
74        impl<'de> Visitor<'de> for MacVisitor {
75            type Value = MacroMatcher;
76
77            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78                formatter.write_str("struct MacroMatcher")
79            }
80
81            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
82            where
83                V: de::MapAccess<'de>,
84            {
85                let mut name = None;
86                let mut brace: Option<char> = None;
87                while let Some(key) = map.next_key()? {
88                    match key {
89                        Field::Name => {
90                            if name.is_some() {
91                                return Err(de::Error::duplicate_field("name"));
92                            }
93                            name = Some(map.next_value()?);
94                        },
95                        Field::Brace => {
96                            if brace.is_some() {
97                                return Err(de::Error::duplicate_field("brace"));
98                            }
99                            brace = Some(map.next_value()?);
100                        },
101                    }
102                }
103                let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
104                let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?;
105                Ok(MacroMatcher {
106                    name,
107                    braces: [('(', ')'), ('{', '}'), ('[', ']')]
108                        .into_iter()
109                        .find(|b| b.0 == brace)
110                        .map(|(o, c)| (o.to_owned(), c.to_owned()))
111                        .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?,
112                })
113            }
114        }
115
116        const FIELDS: &[&str] = &["name", "brace"];
117        deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor)
118    }
119}
120
121/// Represents the item categories that can be ordered by the source ordering lint.
122#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
123#[serde(rename_all = "snake_case")]
124pub enum SourceItemOrderingCategory {
125    Enum,
126    Impl,
127    Module,
128    Struct,
129    Trait,
130}
131
132/// Represents which item categories are enabled for ordering.
133///
134/// The [`Deserialize`] implementation checks that there are no duplicates in
135/// the user configuration.
136pub struct SourceItemOrdering(Vec<SourceItemOrderingCategory>);
137
138impl SourceItemOrdering {
139    pub fn contains(&self, category: &SourceItemOrderingCategory) -> bool {
140        self.0.contains(category)
141    }
142}
143
144impl<T> From<T> for SourceItemOrdering
145where
146    T: Into<Vec<SourceItemOrderingCategory>>,
147{
148    fn from(value: T) -> Self {
149        Self(value.into())
150    }
151}
152
153impl core::fmt::Debug for SourceItemOrdering {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        self.0.fmt(f)
156    }
157}
158
159impl<'de> Deserialize<'de> for SourceItemOrdering {
160    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161    where
162        D: Deserializer<'de>,
163    {
164        let items = Vec::<SourceItemOrderingCategory>::deserialize(deserializer)?;
165        let mut items_set = std::collections::HashSet::new();
166
167        for item in &items {
168            if items_set.contains(item) {
169                return Err(de::Error::custom(format!(
170                    "The category \"{item:?}\" was enabled more than once in the source ordering configuration."
171                )));
172            }
173            items_set.insert(item);
174        }
175
176        Ok(Self(items))
177    }
178}
179
180impl Serialize for SourceItemOrdering {
181    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
182    where
183        S: ser::Serializer,
184    {
185        self.0.serialize(serializer)
186    }
187}
188
189/// Represents the items that can occur within a module.
190#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
191#[serde(rename_all = "snake_case")]
192pub enum SourceItemOrderingModuleItemKind {
193    ExternCrate,
194    Mod,
195    ForeignMod,
196    Use,
197    Macro,
198    GlobalAsm,
199    Static,
200    Const,
201    TyAlias,
202    Enum,
203    Struct,
204    Union,
205    Trait,
206    TraitAlias,
207    Impl,
208    Fn,
209}
210
211impl SourceItemOrderingModuleItemKind {
212    pub fn all_variants() -> Vec<Self> {
213        #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
214        use SourceItemOrderingModuleItemKind::*;
215        vec![
216            ExternCrate,
217            Mod,
218            ForeignMod,
219            Use,
220            Macro,
221            GlobalAsm,
222            Static,
223            Const,
224            TyAlias,
225            Enum,
226            Struct,
227            Union,
228            Trait,
229            TraitAlias,
230            Impl,
231            Fn,
232        ]
233    }
234}
235
236/// Represents the configured ordering of items within a module.
237///
238/// The [`Deserialize`] implementation checks that no item kinds have been
239/// omitted and that there are no duplicates in the user configuration.
240#[derive(Clone)]
241pub struct SourceItemOrderingModuleItemGroupings {
242    groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)>,
243    lut: HashMap<SourceItemOrderingModuleItemKind, usize>,
244}
245
246impl SourceItemOrderingModuleItemGroupings {
247    fn build_lut(
248        groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)],
249    ) -> HashMap<SourceItemOrderingModuleItemKind, usize> {
250        let mut lut = HashMap::new();
251        for (group_index, (_, items)) in groups.iter().enumerate() {
252            for item in items {
253                lut.insert(item.clone(), group_index);
254            }
255        }
256        lut
257    }
258
259    pub fn module_level_order_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<usize> {
260        self.lut.get(item).copied()
261    }
262}
263
264impl From<&[(&str, &[SourceItemOrderingModuleItemKind])]> for SourceItemOrderingModuleItemGroupings {
265    fn from(value: &[(&str, &[SourceItemOrderingModuleItemKind])]) -> Self {
266        let groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)> =
267            value.iter().map(|item| (item.0.to_string(), item.1.to_vec())).collect();
268        let lut = Self::build_lut(&groups);
269        Self { groups, lut }
270    }
271}
272
273impl core::fmt::Debug for SourceItemOrderingModuleItemGroupings {
274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275        self.groups.fmt(f)
276    }
277}
278
279impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings {
280    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
281    where
282        D: Deserializer<'de>,
283    {
284        let groups = Vec::<(String, Vec<SourceItemOrderingModuleItemKind>)>::deserialize(deserializer)?;
285        let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum();
286        let lut = Self::build_lut(&groups);
287
288        let mut expected_items = SourceItemOrderingModuleItemKind::all_variants();
289        for item in lut.keys() {
290            expected_items.retain(|i| i != item);
291        }
292
293        let all_items = SourceItemOrderingModuleItemKind::all_variants();
294        if expected_items.is_empty() && items_total == all_items.len() {
295            let Some(use_group_index) = lut.get(&SourceItemOrderingModuleItemKind::Use) else {
296                return Err(de::Error::custom("Error in internal LUT."));
297            };
298            let Some((_, use_group_items)) = groups.get(*use_group_index) else {
299                return Err(de::Error::custom("Error in internal LUT."));
300            };
301            if use_group_items.len() > 1 {
302                return Err(de::Error::custom(
303                    "The group containing the \"use\" item kind may not contain any other item kinds. \
304                    The \"use\" items will (generally) be sorted by rustfmt already. \
305                    Therefore it makes no sense to implement linting rules that may conflict with rustfmt.",
306                ));
307            }
308
309            Ok(Self { groups, lut })
310        } else if items_total != all_items.len() {
311            Err(de::Error::custom(format!(
312                "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \
313                The module item kinds are: {all_items:?}"
314            )))
315        } else {
316            Err(de::Error::custom(format!(
317                "Not all module item kinds were part of the configured source ordering rule. \
318                All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
319                The module item kinds are: {all_items:?}"
320            )))
321        }
322    }
323}
324
325impl Serialize for SourceItemOrderingModuleItemGroupings {
326    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
327    where
328        S: ser::Serializer,
329    {
330        self.groups.serialize(serializer)
331    }
332}
333
334/// Represents all kinds of trait associated items.
335#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
336#[serde(rename_all = "snake_case")]
337pub enum SourceItemOrderingTraitAssocItemKind {
338    Const,
339    Fn,
340    Type,
341}
342
343impl SourceItemOrderingTraitAssocItemKind {
344    pub fn all_variants() -> Vec<Self> {
345        #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
346        use SourceItemOrderingTraitAssocItemKind::*;
347        vec![Const, Fn, Type]
348    }
349}
350
351/// Represents the order in which associated trait items should be ordered.
352///
353/// The reason to wrap a `Vec` in a newtype is to be able to implement
354/// [`Deserialize`]. Implementing `Deserialize` allows for implementing checks
355/// on configuration completeness at the time of loading the clippy config,
356/// letting the user know if there's any issues with the config (e.g. not
357/// listing all item kinds that should be sorted).
358#[derive(Clone)]
359pub struct SourceItemOrderingTraitAssocItemKinds(Vec<SourceItemOrderingTraitAssocItemKind>);
360
361impl SourceItemOrderingTraitAssocItemKinds {
362    pub fn index_of(&self, item: &SourceItemOrderingTraitAssocItemKind) -> Option<usize> {
363        self.0.iter().position(|i| i == item)
364    }
365}
366
367impl<T> From<T> for SourceItemOrderingTraitAssocItemKinds
368where
369    T: Into<Vec<SourceItemOrderingTraitAssocItemKind>>,
370{
371    fn from(value: T) -> Self {
372        Self(value.into())
373    }
374}
375
376impl core::fmt::Debug for SourceItemOrderingTraitAssocItemKinds {
377    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378        self.0.fmt(f)
379    }
380}
381
382impl<'de> Deserialize<'de> for SourceItemOrderingTraitAssocItemKinds {
383    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
384    where
385        D: Deserializer<'de>,
386    {
387        let items = Vec::<SourceItemOrderingTraitAssocItemKind>::deserialize(deserializer)?;
388
389        let mut expected_items = SourceItemOrderingTraitAssocItemKind::all_variants();
390        for item in &items {
391            expected_items.retain(|i| i != item);
392        }
393
394        let all_items = SourceItemOrderingTraitAssocItemKind::all_variants();
395        if expected_items.is_empty() && items.len() == all_items.len() {
396            Ok(Self(items))
397        } else if items.len() != all_items.len() {
398            Err(de::Error::custom(format!(
399                "Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. \
400                The trait associated item kinds are: {all_items:?}",
401            )))
402        } else {
403            Err(de::Error::custom(format!(
404                "Not all trait associated item kinds were part of the configured source ordering rule. \
405                All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
406                The trait associated item kinds are: {all_items:?}"
407            )))
408        }
409    }
410}
411
412impl Serialize for SourceItemOrderingTraitAssocItemKinds {
413    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
414    where
415        S: ser::Serializer,
416    {
417        self.0.serialize(serializer)
418    }
419}
420
421// these impls are never actually called but are used by the various config options that default to
422// empty lists
423macro_rules! unimplemented_serialize {
424    ($($t:ty,)*) => {
425        $(
426            impl Serialize for $t {
427                fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
428                where
429                    S: ser::Serializer,
430                {
431                    Err(ser::Error::custom("unimplemented"))
432                }
433            }
434        )*
435    }
436}
437
438unimplemented_serialize! {
439    DisallowedPath,
440    Rename,
441    MacroMatcher,
442}
443
444#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
445pub enum PubUnderscoreFieldsBehaviour {
446    PubliclyExported,
447    AllPubFields,
448}