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