clippy_utils/ty/type_certainty/
certainty.rs

1use rustc_hir::def_id::DefId;
2use std::fmt::Debug;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum Certainty {
6    /// Determining the type requires contextual information.
7    Uncertain,
8
9    /// The type can be determined purely from subexpressions. If the argument is `Some(..)`, the
10    /// specific `DefId` is known. Such arguments are needed to handle path segments whose `res` is
11    /// `Res::Err`.
12    Certain(Option<DefId>),
13
14    /// The heuristic believes that more than one `DefId` applies to a type---this is a bug.
15    Contradiction,
16}
17
18pub trait Meet {
19    fn meet(self, other: Self) -> Self;
20}
21
22pub trait TryJoin: Sized {
23    fn try_join(self, other: Self) -> Option<Self>;
24}
25
26impl Meet for Option<DefId> {
27    fn meet(self, other: Self) -> Self {
28        match (self, other) {
29            (None, _) | (_, None) => None,
30            (Some(lhs), Some(rhs)) => (lhs == rhs).then_some(lhs),
31        }
32    }
33}
34
35impl TryJoin for Option<DefId> {
36    fn try_join(self, other: Self) -> Option<Self> {
37        match (self, other) {
38            (Some(lhs), Some(rhs)) => (lhs == rhs).then_some(Some(lhs)),
39            (Some(def_id), _) | (_, Some(def_id)) => Some(Some(def_id)),
40            (None, None) => Some(None),
41        }
42    }
43}
44
45impl Meet for Certainty {
46    fn meet(self, other: Self) -> Self {
47        match (self, other) {
48            (Certainty::Uncertain, _) | (_, Certainty::Uncertain) => Certainty::Uncertain,
49            (Certainty::Certain(lhs), Certainty::Certain(rhs)) => Certainty::Certain(lhs.meet(rhs)),
50            (Certainty::Certain(inner), _) | (_, Certainty::Certain(inner)) => Certainty::Certain(inner),
51            (Certainty::Contradiction, Certainty::Contradiction) => Certainty::Contradiction,
52        }
53    }
54}
55
56impl Certainty {
57    /// Join two `Certainty`s preserving their `DefId`s (if any). Generally speaking, this method
58    /// should be used only when `self` and `other` refer directly to types. Otherwise,
59    /// `join_clearing_def_ids` should be used.
60    pub fn join(self, other: Self) -> Self {
61        match (self, other) {
62            (Certainty::Contradiction, _) | (_, Certainty::Contradiction) => Certainty::Contradiction,
63
64            (Certainty::Certain(lhs), Certainty::Certain(rhs)) => {
65                if let Some(inner) = lhs.try_join(rhs) {
66                    Certainty::Certain(inner)
67                } else {
68                    debug_assert!(false, "Contradiction with {lhs:?} and {rhs:?}");
69                    Certainty::Contradiction
70                }
71            },
72
73            (Certainty::Certain(inner), _) | (_, Certainty::Certain(inner)) => Certainty::Certain(inner),
74
75            (Certainty::Uncertain, Certainty::Uncertain) => Certainty::Uncertain,
76        }
77    }
78
79    /// Join two `Certainty`s after clearing their `DefId`s. This method should be used when `self`
80    /// or `other` do not necessarily refer to types, e.g., when they are aggregations of other
81    /// `Certainty`s.
82    pub fn join_clearing_def_ids(self, other: Self) -> Self {
83        self.clear_def_id().join(other.clear_def_id())
84    }
85
86    pub fn clear_def_id(self) -> Certainty {
87        if matches!(self, Certainty::Certain(_)) {
88            Certainty::Certain(None)
89        } else {
90            self
91        }
92    }
93
94    pub fn with_def_id(self, def_id: DefId) -> Certainty {
95        if matches!(self, Certainty::Certain(_)) {
96            Certainty::Certain(Some(def_id))
97        } else {
98            self
99        }
100    }
101
102    pub fn to_def_id(self) -> Option<DefId> {
103        match self {
104            Certainty::Certain(inner) => inner,
105            _ => None,
106        }
107    }
108
109    pub fn is_certain(self) -> bool {
110        matches!(self, Self::Certain(_))
111    }
112}
113
114/// Think: `iter.all(/* is certain */)`
115pub fn meet(iter: impl Iterator<Item = Certainty>) -> Certainty {
116    iter.fold(Certainty::Certain(None), Certainty::meet)
117}
118
119/// Think: `iter.any(/* is certain */)`
120pub fn join(iter: impl Iterator<Item = Certainty>) -> Certainty {
121    iter.fold(Certainty::Uncertain, Certainty::join)
122}