Skip to main content

clippy_config/
types.rs

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    /// Setting `allow_invalid` to true suppresses a warning if `path` does not refer to an existing
29    /// definition.
30    ///
31    /// This could be useful when conditional compilation is used, or when a clippy.toml file is
32    /// shared among multiple projects.
33    allow_invalid: bool,
34    /// The span of the `DisallowedPath`.
35    ///
36    /// Used for diagnostics.
37    #[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// `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just
70// above. `DisallowedPathEnum` is not meant to be used outside of this file.
71#[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/// Creates a map of disallowed items to the reason they were disallowed.
143#[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            // Don't warn about unloaded crates:
176            // https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221
177            && (sym_path.len() < 2 || !find_crates(tcx, sym_path[0]).is_empty())
178        {
179            // Relookup the path in an arbitrary namespace to get a good `expected, found` message
180            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/// Represents the item categories that can be ordered by the source ordering lint.
280#[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
290/// Represents which item categories are enabled for ordering.
291///
292/// The [`Deserialize`] implementation checks that there are no duplicates in
293/// the user configuration.
294pub 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/// Represents the items that can occur within a module.
348#[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)] // Very local glob use for legibility.
372        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/// Represents the configured ordering of items within a module.
395///
396/// The [`Deserialize`] implementation checks that no item kinds have been
397/// omitted and that there are no duplicates in the user configuration.
398#[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/// Represents all kinds of trait associated items.
520#[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)] // Very local glob use for legibility.
531        use SourceItemOrderingTraitAssocItemKind::*;
532        vec![Const, Fn, Type]
533    }
534}
535
536/// Represents the order in which associated trait items should be ordered.
537///
538/// The reason to wrap a `Vec` in a newtype is to be able to implement
539/// [`Deserialize`]. Implementing `Deserialize` allows for implementing checks
540/// on configuration completeness at the time of loading the clippy config,
541/// letting the user know if there's any issues with the config (e.g. not
542/// listing all item kinds that should be sorted).
543#[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/// Describes which specific groupings should have their items ordered
607/// alphabetically.
608///
609/// This is separate from defining and enforcing groupings. For example,
610/// defining enums are grouped before structs still allows for an enum B to be
611/// placed before an enum A. Only when enforcing ordering within the grouping,
612/// will it be checked if A is placed before B.
613#[derive(Clone, Debug)]
614pub enum SourceItemOrderingWithinModuleItemGroupings {
615    /// All groupings should have their items ordered.
616    All,
617
618    /// None of the groupings should have their order checked.
619    None,
620
621    /// Only the specified groupings should have their order checked.
622    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/// Helper struct for deserializing the [`SourceItemOrderingWithinModuleItemGroupings`].
636#[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
683// these impls are never actually called but are used by the various config options that default to
684// empty lists
685macro_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}