Skip to main content

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