clippy_config/
types.rs

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