solar_interface/diagnostics/
message.rs

1//! Modified from [`rustc_error_messages`](https://github.com/rust-lang/rust/blob/520e30be83b4ed57b609d33166c988d1512bf4f3/compiler/rustc_error_messages/src/lib.rs).
2
3use crate::Span;
4use std::borrow::Cow;
5
6#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct DiagnosticMessage {
8    inner: Cow<'static, str>,
9}
10
11impl From<&'static str> for DiagnosticMessage {
12    fn from(value: &'static str) -> Self {
13        Self { inner: Cow::Borrowed(value) }
14    }
15}
16
17impl From<String> for DiagnosticMessage {
18    fn from(value: String) -> Self {
19        Self { inner: Cow::Owned(value) }
20    }
21}
22
23impl From<Cow<'static, str>> for DiagnosticMessage {
24    fn from(value: Cow<'static, str>) -> Self {
25        Self { inner: value }
26    }
27}
28
29impl DiagnosticMessage {
30    /// Returns the message as a string.
31    #[inline]
32    pub fn as_str(&self) -> &str {
33        &self.inner
34    }
35}
36
37/// A span together with some additional data.
38#[derive(Clone, Debug)]
39pub struct SpanLabel {
40    /// The span we are going to include in the final snippet.
41    pub span: Span,
42
43    /// Is this a primary span? This is the "locus" of the message,
44    /// and is indicated with a `^^^^` underline, versus `----`.
45    pub is_primary: bool,
46
47    /// What label should we attach to this span (if any)?
48    pub label: Option<DiagnosticMessage>,
49}
50
51/// A collection of `Span`s.
52///
53/// Spans have two orthogonal attributes:
54/// - They can be *primary spans*. In this case they are the locus of the error, and would be
55///   rendered with `^^^`.
56/// - They can have a *label*. In this case, the label is written next to the mark in the snippet
57///   when we render.
58#[derive(Clone, Debug, PartialEq, Eq, Hash)]
59pub struct MultiSpan {
60    primary_spans: Vec<Span>,
61    span_labels: Vec<(Span, DiagnosticMessage)>,
62}
63
64impl MultiSpan {
65    #[inline]
66    pub fn new() -> Self {
67        Self { primary_spans: vec![], span_labels: vec![] }
68    }
69
70    pub fn from_span(primary_span: Span) -> Self {
71        Self { primary_spans: vec![primary_span], span_labels: vec![] }
72    }
73
74    pub fn from_spans(mut vec: Vec<Span>) -> Self {
75        vec.sort();
76        Self { primary_spans: vec, span_labels: vec![] }
77    }
78
79    pub fn push_span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) {
80        self.span_labels.push((span, label.into()));
81    }
82
83    /// Selects the first primary span (if any).
84    pub fn primary_span(&self) -> Option<Span> {
85        self.primary_spans.first().copied()
86    }
87
88    /// Returns all primary spans.
89    pub fn primary_spans(&self) -> &[Span] {
90        &self.primary_spans
91    }
92
93    /// Returns `true` if any of the primary spans are displayable.
94    pub fn has_primary_spans(&self) -> bool {
95        !self.is_dummy()
96    }
97
98    /// Returns `true` if this contains only a dummy primary span with any hygienic context.
99    pub fn is_dummy(&self) -> bool {
100        self.primary_spans.iter().all(|sp| sp.is_dummy())
101    }
102
103    /// Replaces all occurrences of one Span with another. Used to move `Span`s in areas that don't
104    /// display well (like std macros). Returns `true` if replacements occurred.
105    pub fn replace(&mut self, before: Span, after: Span) -> bool {
106        let mut replacements_occurred = false;
107        for primary_span in &mut self.primary_spans {
108            if *primary_span == before {
109                *primary_span = after;
110                replacements_occurred = true;
111            }
112        }
113        for span_label in &mut self.span_labels {
114            if span_label.0 == before {
115                span_label.0 = after;
116                replacements_occurred = true;
117            }
118        }
119        replacements_occurred
120    }
121
122    pub fn pop_span_label(&mut self) -> Option<(Span, DiagnosticMessage)> {
123        self.span_labels.pop()
124    }
125
126    /// Returns the strings to highlight. We always ensure that there
127    /// is an entry for each of the primary spans -- for each primary
128    /// span `P`, if there is at least one label with span `P`, we return
129    /// those labels (marked as primary). But otherwise we return
130    /// `SpanLabel` instances with empty labels.
131    pub fn span_labels(&self) -> Vec<SpanLabel> {
132        let is_primary = |span| self.primary_spans.contains(&span);
133
134        let mut span_labels = self
135            .span_labels
136            .iter()
137            .map(|&(span, ref label)| SpanLabel {
138                span,
139                is_primary: is_primary(span),
140                label: Some(label.clone()),
141            })
142            .collect::<Vec<_>>();
143
144        for &span in &self.primary_spans {
145            if !span_labels.iter().any(|sl| sl.span == span) {
146                span_labels.push(SpanLabel { span, is_primary: true, label: None });
147            }
148        }
149
150        span_labels
151    }
152
153    /// Returns `true` if any of the span labels is displayable.
154    pub fn has_span_labels(&self) -> bool {
155        self.span_labels.iter().any(|(sp, _)| !sp.is_dummy())
156    }
157
158    /// Clone this `MultiSpan` without keeping any of the span labels - sometimes a `MultiSpan` is
159    /// to be re-used in another diagnostic, but includes `span_labels` which have translated
160    /// messages. These translated messages would fail to translate without their diagnostic
161    /// arguments which are unlikely to be cloned alongside the `Span`.
162    pub fn clone_ignoring_labels(&self) -> Self {
163        Self { primary_spans: self.primary_spans.clone(), ..Self::new() }
164    }
165}
166
167impl Default for MultiSpan {
168    fn default() -> Self {
169        Self::new()
170    }
171}
172
173impl From<Span> for MultiSpan {
174    fn from(span: Span) -> Self {
175        Self::from_span(span)
176    }
177}
178
179impl From<Vec<Span>> for MultiSpan {
180    fn from(spans: Vec<Span>) -> Self {
181        Self::from_spans(spans)
182    }
183}