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