1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! A mostly transparent wrapper around any type to indicate that it carries
//! a `Span` with it.
//!
//! The recommended pattern is that whenever you have some type `T` that
//! needs a method `foo(&self, span: Span)`, you should implement
//! `foo(&self)` for `Sp<T>` that gives its associated span to the inner call.
//! Macros are provided to facilitate this usage.
//!
//! Note: yous should generally prefer putting the `Sp` on the outside
//! in public function arguments, and on the inside on return values.
//! That is,
//! - use `Sp<&T>`, `Sp<Box<T>>`, `Sp<Result<T, E>>` in inputs and struct fields,
//! - use `&Sp<T>`, `Box<Sp<T>>`, `Result<Sp<T>, E>>` in outputs.
//! The reason is that `Sp` implements the following conversions:
//! - `&Sp<T> -> Sp<&T>` through `as_ref`,
//! - `&mut Sp<T> -> Sp<&mut T>` through `as_ref_mut`,
//! - `Box<Sp<T>> -> Sp<Box<T>>` through `boxed`,
//! - `Sp<Result<T, E>> -> Result<Sp<T>, E>` through `transpose`.
//! Thus a function that takes as input a `Sp<&T>` is more general than
//! a function that takes a `&Sp<T>`.
//!
//! The above does not necessarily apply to functions that are strictly for
//! internal use, since if all callers are known the benefit of the generality
//! is lessened, and passing `&Sp<T>` may reduce copies of `Span` that come with
//! each invocation of `as_ref`.

use std::fmt;
use std::hash::{Hash, Hasher};

/// A type that transparently implements `PartialEq` and `Hash`, to be used
/// in structs that carry additional data that should not be relevant in comparisons.
///
/// It additionally supports creating dummy values and they will also compare
/// equal to all other and hash identically.
///
/// The use of a `Transparent<_>` as a field of a struct should be understood
/// to convey the following intent: this field is useful only for diagnostics
/// and is never relevant for decisions.
/// This makes `Transparent::forge` suitable for use in unit tests where said
/// diagnostic data may not be available and we still want the computation to
/// be unaffected.
///
/// In fact the computation is guaranteed to be unaffected because `Transparent`
/// provides no peeking method, and the only way to extract data is `unwrap`
/// that will panic if the inner value was forged.
#[derive(Clone, Copy)]
pub struct Transparent<T> {
    /// Payload.
    inner: Result<T, &'static str>,
}

impl<T> fmt::Debug for Transparent<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "_")
    }
}

impl<T> Transparent<T> {
    /// Assert that the value was not forged and get its contents.
    /// # Panics
    /// fails if `self` was obtainedy by `Transparent::forge()`.
    pub fn unwrap(self) -> T {
        match self.inner {
            Ok(x) => x,
            Err(l) => crate::abort!(
                "Attempted to read a forged transparent value. Forged at {}",
                l
            ),
        }
    }

    /// Create a dummy value.
    ///
    /// It is recommended to use the `loc` parameter as a way to track the location
    /// where it was forged so that if it is accidentally unwrapped the error message
    /// is more readable, e.g. by using either an identifying string or even better
    /// the output of the `here!()` macro that gives the location in the source code.
    #[must_use]
    pub fn forge(loc: &'static str) -> Self {
        Self { inner: Err(loc) }
    }
}

impl<T> Transparent<Option<T>> {
    /// If there is not already data, add it.
    /// This is a noop if the contents were not forged.
    pub fn try_override_inner(&mut self, t: T) {
        match &mut self.inner {
            Ok(s) => {
                if s.is_none() {
                    *s = Some(t);
                }
            }
            Err(_) => self.inner = Ok(Some(t)),
        }
    }
}

impl<T> Transparent<Transparent<T>> {
    /// Remove the outer `Transparent`
    pub fn flatten(self) -> Transparent<T> {
        match self.inner {
            Ok(x1) => match x1.inner {
                Ok(x2) => Transparent { inner: Ok(x2) },
                Err(loc) => Transparent { inner: Err(loc) },
            },
            Err(loc) => Transparent { inner: Err(loc) },
        }
    }
}

impl<T> Transparent<Option<T>> {
    /// Remove the outer `Transparent`
    pub fn flatten(self) -> Option<Transparent<T>> {
        match self.inner {
            Ok(x1) => Some(Transparent { inner: Ok(x1?) }),
            Err(loc) => Some(Transparent { inner: Err(loc) }),
        }
    }
}

impl<T> Transparent<T> {
    /// Wrap in a transparent.
    pub fn from(inner: T) -> Self {
        Self { inner: Ok(inner) }
    }
}

impl<T> PartialEq for Transparent<T> {
    fn eq(&self, _: &Self) -> bool {
        true
    }
}

impl<T> Eq for Transparent<T> {}

impl<T> Hash for Transparent<T> {
    fn hash<H: Hasher>(&self, _: &mut H) {}
}

impl<T: fmt::Display> fmt::Display for Transparent<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.inner {
            Ok(v) => write!(f, "{v}"),
            Err(_) => write!(f, "<undef>"),
        }
    }
}

impl Transparent<proc_macro2::Span> {
    /// Map `join` to the inner `span`s
    #[must_use]
    pub fn join(self, other: Self) -> Option<Self> {
        let Ok(v1) = self.inner else {
            return Some(self);
        };
        let Ok(v2) = other.inner else {
            return Some(self);
        };
        Some(Self {
            inner: Ok(v1.join(v2)?),
        })
    }
}

impl<T: crate::TrySpan> crate::TrySpan for Transparent<T> {
    /// `Sp` always has a span, so `TrySpan` is guaranteed to succeed.
    fn try_span(&self) -> Option<crate::Span> {
        self.inner.try_span()
    }
}