rune/diagnostics/
emit.rs

1//! Runtime helpers for loading code and emitting diagnostics.
2
3use core::fmt;
4
5use std::io;
6
7use codespan_reporting::diagnostic as d;
8use codespan_reporting::term;
9pub use codespan_reporting::term::termcolor;
10use codespan_reporting::term::termcolor::WriteColor;
11
12use crate::alloc::fmt::TryWrite;
13use crate::alloc::prelude::*;
14use crate::alloc::{self, String};
15use crate::ast::{Span, Spanned};
16use crate::compile::{ErrorKind, LinkerError, Location};
17use crate::diagnostics::{
18    Diagnostic, FatalDiagnostic, FatalDiagnosticKind, RuntimeWarningDiagnostic,
19    RuntimeWarningDiagnosticKind, WarningDiagnostic, WarningDiagnosticKind,
20};
21use crate::hash::Hash;
22use crate::runtime::DebugInfo;
23use crate::runtime::{DebugInst, Protocol, Unit, VmError, VmErrorAt, VmErrorKind};
24use crate::Context;
25use crate::{Diagnostics, Source, SourceId, Sources};
26
27struct StackFrame {
28    source_id: SourceId,
29    span: Span,
30}
31
32/// Errors that can be raised when formatting diagnostics.
33#[derive(Debug)]
34#[non_exhaustive]
35pub enum EmitError {
36    /// Source Error.
37    Io(io::Error),
38    /// Allocation error.
39    Alloc(alloc::Error),
40    /// Codespan reporting error.
41    CodespanReporting(codespan_reporting::files::Error),
42}
43
44impl fmt::Display for EmitError {
45    #[inline]
46    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
47        match self {
48            EmitError::Io(error) => error.fmt(f),
49            EmitError::Alloc(error) => error.fmt(f),
50            EmitError::CodespanReporting(error) => error.fmt(f),
51        }
52    }
53}
54
55impl From<io::Error> for EmitError {
56    fn from(error: io::Error) -> Self {
57        EmitError::Io(error)
58    }
59}
60
61impl From<alloc::Error> for EmitError {
62    fn from(error: alloc::Error) -> Self {
63        EmitError::Alloc(error)
64    }
65}
66
67impl From<codespan_reporting::files::Error> for EmitError {
68    fn from(error: codespan_reporting::files::Error) -> Self {
69        EmitError::CodespanReporting(error)
70    }
71}
72
73impl core::error::Error for EmitError {
74    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
75        match self {
76            EmitError::Io(error) => Some(error),
77            EmitError::Alloc(error) => Some(error),
78            EmitError::CodespanReporting(error) => Some(error),
79        }
80    }
81}
82
83impl Diagnostics {
84    /// Generate formatted diagnostics capable of referencing source lines and
85    /// hints.
86    ///
87    /// See [prepare][crate::prepare] for how to use.
88    pub fn emit<O>(&self, out: &mut O, sources: &Sources) -> Result<(), EmitError>
89    where
90        O: WriteColor,
91    {
92        if self.is_empty() {
93            return Ok(());
94        }
95
96        let config = term::Config::default();
97
98        for diagnostic in self.diagnostics() {
99            match diagnostic {
100                Diagnostic::Fatal(e) => {
101                    fatal_diagnostics_emit(e, out, sources, &config)?;
102                }
103                Diagnostic::Warning(w) => {
104                    warning_diagnostics_emit(w, out, sources, &config)?;
105                }
106                Diagnostic::RuntimeWarning(w) => {
107                    runtime_warning_diagnostics_emit(w, out, sources, &config, None, None)?;
108                }
109            }
110        }
111
112        Ok(())
113    }
114
115    /// Generate formatted diagnostics capable of referencing source lines and
116    /// hints.
117    ///
118    /// See [prepare][crate::prepare] for how to use.
119    pub fn emit_detailed<O>(
120        &self,
121        out: &mut O,
122        sources: &Sources,
123        unit: &Unit,
124        context: &Context,
125    ) -> Result<(), EmitError>
126    where
127        O: WriteColor,
128    {
129        if self.is_empty() {
130            return Ok(());
131        }
132
133        let debug_info = unit.debug_info();
134
135        let config = term::Config::default();
136
137        for diagnostic in self.diagnostics() {
138            match diagnostic {
139                Diagnostic::Fatal(e) => {
140                    fatal_diagnostics_emit(e, out, sources, &config)?;
141                }
142                Diagnostic::Warning(w) => {
143                    warning_diagnostics_emit(w, out, sources, &config)?;
144                }
145                Diagnostic::RuntimeWarning(w) => {
146                    runtime_warning_diagnostics_emit(
147                        w,
148                        out,
149                        sources,
150                        &config,
151                        debug_info,
152                        Some(context),
153                    )?;
154                }
155            }
156        }
157
158        Ok(())
159    }
160}
161
162impl VmError {
163    /// Generate formatted diagnostics capable of referencing source lines and
164    /// hints.
165    ///
166    /// See [prepare][crate::prepare] for how to use.
167    pub fn emit<O>(&self, out: &mut O, sources: &Sources) -> Result<(), EmitError>
168    where
169        O: WriteColor,
170    {
171        let mut red = termcolor::ColorSpec::new();
172        red.set_fg(Some(termcolor::Color::Red));
173
174        let mut backtrace = vec![];
175        let config = term::Config::default();
176
177        for l in &self.inner.stacktrace {
178            let debug_info = match l.unit.debug_info() {
179                Some(debug_info) => debug_info,
180                None => continue,
181            };
182
183            for ip in [l.ip]
184                .into_iter()
185                .chain(l.frames.iter().rev().map(|v| v.ip))
186            {
187                let debug_inst = match debug_info.instruction_at(ip) {
188                    Some(debug_inst) => debug_inst,
189                    None => continue,
190                };
191
192                let source_id = debug_inst.source_id;
193                let span = debug_inst.span;
194
195                backtrace.push(StackFrame { source_id, span });
196            }
197        }
198
199        let mut labels = ::rust_alloc::vec::Vec::new();
200        let mut notes = ::rust_alloc::vec::Vec::new();
201
202        let get = |at: &VmErrorAt| -> Option<&DebugInst> {
203            let l = self.inner.stacktrace.get(at.index())?;
204            let debug_info = l.unit.debug_info()?;
205            let debug_inst = debug_info.instruction_at(l.ip)?;
206            Some(debug_inst)
207        };
208
209        let get_ident = |at: &VmErrorAt, hash: Hash| {
210            let l = self.inner.stacktrace.get(at.index())?;
211            let debug_info = l.unit.debug_info()?;
212            debug_info.ident_for_hash(hash)
213        };
214
215        for at in &self.inner.chain {
216            // Populate source-specific notes.
217            match at.kind() {
218                VmErrorKind::UnsupportedBinaryOperation { lhs, rhs, .. } => {
219                    notes.extend(vec![
220                        format!("Left hand side has type `{}`", lhs),
221                        format!("Right hand side has type `{}`", rhs),
222                    ]);
223                }
224                VmErrorKind::BadArgumentCount { actual, expected } => {
225                    notes.extend([
226                        format!("Expected `{}`", expected),
227                        format!("Got `{}`", actual),
228                    ]);
229                }
230                _ => {}
231            };
232
233            if let Some(&DebugInst {
234                source_id, span, ..
235            }) = get(at)
236            {
237                labels.push(
238                    d::Label::primary(source_id, span.range()).with_message(at.try_to_string()?),
239                );
240            }
241        }
242
243        if let Some(&DebugInst {
244            source_id, span, ..
245        }) = get(&self.inner.error)
246        {
247            labels.push(
248                d::Label::primary(source_id, span.range())
249                    .with_message(self.inner.error.try_to_string()?),
250            );
251        };
252
253        for at in [&self.inner.error].into_iter().chain(&self.inner.chain) {
254            // Populate source-specific notes.
255            if let VmErrorKind::MissingInstanceFunction { hash, instance } = at.kind() {
256                // Undo instance function hashing to extract the hash of the
257                // name. This is an implementation detail in how hash mixing
258                // works, in that it can be reversed because we simply xor
259                // the values together with an associated function seed. But
260                // this is not guaranteed to work everywhere.
261
262                if let Some(&DebugInst {
263                    source_id, span, ..
264                }) = get(at)
265                {
266                    let instance_hash = Hash::associated_function(instance.type_hash(), *hash);
267
268                    if let Some(ident) = get_ident(at, instance_hash) {
269                        labels.push(d::Label::secondary(source_id, span.range()).with_message(
270                            format!(
271                                "This corresponds to the `{instance}::{ident}` instance function"
272                            ),
273                        ));
274                    }
275
276                    if let Some(protocol) = Protocol::from_hash(instance_hash) {
277                        labels.push(
278                            d::Label::secondary(source_id, span.range())
279                                .with_message(format!("This corresponds to the `{protocol}` protocol function for `{instance}`")),
280                        );
281                    }
282                }
283            };
284        }
285
286        let diagnostic = d::Diagnostic::error()
287            .with_message(self.inner.error.try_to_string()?)
288            .with_labels(labels)
289            .with_notes(notes);
290
291        term::emit(out, &config, sources, &diagnostic)?;
292
293        if !backtrace.is_empty() {
294            writeln!(out, "Backtrace:")?;
295
296            for frame in &backtrace {
297                let Some(source) = sources.get(frame.source_id) else {
298                    continue;
299                };
300
301                let (line, line_count, [prefix, mid, suffix]) = match source.line(frame.span) {
302                    Some((line, line_count, text)) => {
303                        (line.saturating_add(1), line_count.saturating_add(1), text)
304                    }
305                    None => continue,
306                };
307
308                writeln!(out, "{}:{line}:{line_count}:", source.name())?;
309                write!(out, "{prefix}")?;
310                out.set_color(&red)?;
311                write!(out, "{mid}")?;
312                out.reset()?;
313                writeln!(out, "{}", suffix.trim_end_matches(['\n', '\r']))?;
314            }
315        }
316
317        Ok(())
318    }
319}
320
321impl FatalDiagnostic {
322    /// Generate formatted diagnostics capable of referencing source lines and
323    /// hints.
324    ///
325    /// See [prepare][crate::prepare] for how to use.
326    pub fn emit<O>(&self, out: &mut O, sources: &Sources) -> Result<(), EmitError>
327    where
328        O: WriteColor,
329    {
330        let config = term::Config::default();
331        fatal_diagnostics_emit(self, out, sources, &config)
332    }
333}
334
335impl WarningDiagnostic {
336    /// Generate formatted diagnostics capable of referencing source lines and
337    /// hints.
338    ///
339    /// See [prepare][crate::prepare] for how to use.
340    pub fn emit<O>(&self, out: &mut O, sources: &Sources) -> Result<(), EmitError>
341    where
342        O: WriteColor,
343    {
344        let config = term::Config::default();
345        warning_diagnostics_emit(self, out, sources, &config)
346    }
347}
348
349impl RuntimeWarningDiagnostic {
350    /// Generate formatted diagnostics capable of referencing source lines and
351    /// hints.
352    ///
353    /// See [prepare][crate::prepare] for how to use.
354    pub fn emit<O>(
355        &self,
356        out: &mut O,
357        sources: &Sources,
358        debug_info: Option<&DebugInfo>,
359        context: Option<&Context>,
360    ) -> Result<(), EmitError>
361    where
362        O: WriteColor,
363    {
364        let config = term::Config::default();
365        runtime_warning_diagnostics_emit(self, out, sources, &config, debug_info, context)
366    }
367}
368
369impl Unit {
370    /// Dump instructions in a human readable manner.
371    pub fn emit_instructions<O>(
372        &self,
373        out: &mut O,
374        sources: &Sources,
375        without_source: bool,
376    ) -> io::Result<()>
377    where
378        O: WriteColor,
379    {
380        let mut first_function = true;
381
382        for (n, inst) in self.iter_instructions() {
383            let debug = self.debug_info().and_then(|d| d.instruction_at(n));
384
385            if let Some((hash, signature)) = self.debug_info().and_then(|d| d.function_at(n)) {
386                if !std::mem::take(&mut first_function) {
387                    writeln!(out)?;
388                }
389
390                writeln!(out, "fn {} ({}):", signature, hash)?;
391            }
392
393            for label in debug.map(|d| d.labels.as_slice()).unwrap_or_default() {
394                writeln!(out, "{}:", label)?;
395            }
396
397            write!(out, "  {n:04} = {inst}")?;
398
399            if let Some(comment) = debug.and_then(|d| d.comment.as_ref()) {
400                write!(out, " // {}", comment)?;
401            }
402
403            writeln!(out)?;
404
405            if !without_source {
406                if let Some((source, span)) =
407                    debug.and_then(|d| sources.get(d.source_id).map(|s| (s, d.span)))
408                {
409                    if let Some(line) = source.source_line(span) {
410                        write!(out, "  ")?;
411                        line.write(out)?;
412                        writeln!(out)?;
413                    }
414                }
415            }
416        }
417
418        Ok(())
419    }
420}
421
422impl Source {
423    /// Print formatted diagnostics about a source conveniently.
424    pub fn source_line(&self, span: Span) -> Option<SourceLine<'_>> {
425        let (count, column, line, span) = line_for(self, span)?;
426
427        Some(SourceLine {
428            name: self.name(),
429            count,
430            column,
431            line,
432            span,
433        })
434    }
435}
436
437/// An extracted source line.
438pub struct SourceLine<'a> {
439    name: &'a str,
440    count: usize,
441    column: usize,
442    line: &'a str,
443    span: Span,
444}
445
446impl SourceLine<'_> {
447    /// Write a source line to the given output.
448    pub fn write(&self, o: &mut dyn WriteColor) -> io::Result<()> {
449        let mut highlight = termcolor::ColorSpec::new();
450        highlight.set_fg(Some(termcolor::Color::Yellow));
451
452        let mut new_line = termcolor::ColorSpec::new();
453        new_line.set_fg(Some(termcolor::Color::Red));
454
455        let line = self.line.trim_end();
456        let end = self.span.end.into_usize().min(line.len());
457
458        let before = &line[0..self.span.start.into_usize()].trim_start();
459        let inner = &line[self.span.start.into_usize()..end];
460        let after = &line[end..];
461
462        {
463            let name = self.name;
464            let column = self.count + 1;
465            let start = self.column + 1;
466            let end = start + inner.chars().count();
467            write!(o, "{name}:{column}:{start}-{end}: ")?;
468        }
469
470        write!(o, "{before}")?;
471        o.set_color(&highlight)?;
472        write!(o, "{inner}")?;
473        o.reset()?;
474        write!(o, "{after}")?;
475
476        if self.span.end != end {
477            o.set_color(&new_line)?;
478            write!(o, "\\n")?;
479            o.reset()?;
480        }
481
482        Ok(())
483    }
484}
485
486/// Get the line number and source line for the given source and span.
487pub fn line_for(source: &Source, span: Span) -> Option<(usize, usize, &str, Span)> {
488    let line_starts = source.line_starts();
489
490    let line = match line_starts.binary_search(&span.start.into_usize()) {
491        Ok(n) => n,
492        Err(n) => n.saturating_sub(1),
493    };
494
495    let start = *line_starts.get(line)?;
496    let end = line.checked_add(1)?;
497
498    let s = if let Some(end) = line_starts.get(end) {
499        source.get(start..*end)?
500    } else {
501        source.get(start..)?
502    };
503
504    let line_end = span.start.into_usize().saturating_sub(start);
505    let column = s
506        .get(..line_end)
507        .into_iter()
508        .flat_map(|s| s.chars())
509        .count();
510
511    let start = start.try_into().unwrap();
512
513    Some((
514        line,
515        column,
516        s,
517        Span::new(
518            span.start.saturating_sub(start),
519            span.end.saturating_sub(start),
520        ),
521    ))
522}
523
524/// Helper to emit diagnostics for a warning.
525fn warning_diagnostics_emit<O>(
526    this: &WarningDiagnostic,
527    out: &mut O,
528    sources: &Sources,
529    config: &term::Config,
530) -> Result<(), EmitError>
531where
532    O: WriteColor,
533{
534    let mut notes = ::rust_alloc::vec::Vec::new();
535    let mut labels = ::rust_alloc::vec::Vec::new();
536
537    labels.push(
538        d::Label::primary(this.source_id(), this.span().range())
539            .with_message(this.try_to_string()?),
540    );
541
542    match this.kind() {
543        WarningDiagnosticKind::LetPatternMightPanic { span, .. } => {
544            if let Some(binding) = sources.source(this.source_id(), *span) {
545                let mut note = String::new();
546                writeln!(note, "Hint: Rewrite to:")?;
547                writeln!(note, "if {} {{", binding)?;
548                writeln!(note, "    // ..")?;
549                writeln!(note, "}}")?;
550                notes.push(note.into_std());
551            }
552        }
553        WarningDiagnosticKind::RemoveTupleCallParams { variant, .. } => {
554            if let Some(variant) = sources.source(this.source_id(), *variant) {
555                let mut note = String::new();
556                writeln!(note, "Hint: Rewrite to `{}`", variant)?;
557                notes.push(note.into_std());
558            }
559        }
560        WarningDiagnosticKind::Unreachable { cause, .. } => {
561            labels.push(
562                d::Label::secondary(this.source_id(), cause.range())
563                    .with_message("This code diverges"),
564            );
565        }
566        _ => {}
567    };
568
569    if let Some(context) = this.context() {
570        labels.push(
571            d::Label::secondary(this.source_id(), context.range()).with_message("In this context"),
572        );
573    }
574
575    let diagnostic = d::Diagnostic::warning()
576        .with_message("Warning")
577        .with_labels(labels)
578        .with_notes(notes);
579
580    term::emit(out, config, sources, &diagnostic)?;
581    Ok(())
582}
583
584/// Helper to emit diagnostics for a runtime warning.
585fn runtime_warning_diagnostics_emit<O>(
586    this: &RuntimeWarningDiagnostic,
587    out: &mut O,
588    sources: &Sources,
589    config: &term::Config,
590    debug_info: Option<&DebugInfo>,
591    context: Option<&Context>,
592) -> Result<(), EmitError>
593where
594    O: WriteColor,
595{
596    let mut notes = ::rust_alloc::vec::Vec::new();
597    let mut labels = ::rust_alloc::vec::Vec::new();
598    let mut message = String::new();
599
600    match this.kind {
601        RuntimeWarningDiagnosticKind::UsedDeprecated { hash } => {
602            // try to get the function name - this needs to be improved
603            let name = match context
604                .map(|c| c.lookup_meta_by_hash(hash))
605                .and_then(|m| m.into_iter().next())
606                .and_then(|e| e.item.as_ref())
607            {
608                Some(e) => e.try_to_string()?,
609                None => hash.try_to_string()?,
610            };
611            writeln!(message, "Used deprecated function: {}", name)?;
612
613            // Deprecation message if it's availble
614            if let Some(context) = context {
615                if let Some(deprecation) = context.lookup_deprecation(hash) {
616                    let mut note = String::new();
617                    writeln!(note, "Deprecated: {}", deprecation)?;
618                    notes.push(note.into_std());
619                }
620            }
621
622            // Show the span, where the problem occoured
623            if let Some(inst) = debug_info.and_then(|d| d.instruction_at(this.ip)) {
624                labels.push(
625                    d::Label::primary(inst.source_id, inst.span.range())
626                        .with_message(this.try_to_string()?),
627                );
628            }
629        }
630    };
631
632    let diagnostic = d::Diagnostic::warning()
633        .with_message(message)
634        .with_labels(labels)
635        .with_notes(notes);
636
637    term::emit(out, config, sources, &diagnostic)?;
638    Ok(())
639}
640
641/// Custom shared helper for emitting diagnostics for a single error.
642fn fatal_diagnostics_emit<O>(
643    this: &FatalDiagnostic,
644    out: &mut O,
645    sources: &Sources,
646    config: &term::Config,
647) -> Result<(), EmitError>
648where
649    O: WriteColor,
650{
651    let mut labels = ::rust_alloc::vec::Vec::new();
652    let mut notes = ::rust_alloc::vec::Vec::new();
653
654    if let Some(span) = this.span() {
655        labels.push(
656            d::Label::primary(this.source_id(), span.range())
657                .with_message(this.kind().try_to_string()?),
658        );
659    }
660
661    match this.kind() {
662        FatalDiagnosticKind::Internal(message) => {
663            writeln!(out, "internal error: {}", message)?;
664            return Ok(());
665        }
666        FatalDiagnosticKind::LinkError(error) => {
667            match error {
668                LinkerError::MissingFunction { hash, spans } => {
669                    let mut labels = ::rust_alloc::vec::Vec::new();
670
671                    for (span, source_id) in spans {
672                        labels.push(
673                            d::Label::primary(*source_id, span.range())
674                                .with_message("called here."),
675                        );
676                    }
677
678                    let diagnostic = d::Diagnostic::error()
679                        .with_message(format!(
680                            "linker error: missing function with hash `{}`",
681                            hash
682                        ))
683                        .with_labels(labels);
684
685                    term::emit(out, config, sources, &diagnostic)?;
686                }
687            }
688
689            return Ok(());
690        }
691        FatalDiagnosticKind::CompileError(error) => {
692            format_compile_error(
693                this,
694                sources,
695                error.span(),
696                error.kind(),
697                &mut labels,
698                &mut notes,
699            )?;
700        }
701    };
702
703    let diagnostic = d::Diagnostic::error()
704        .with_message(this.kind().try_to_string()?)
705        .with_labels(labels)
706        .with_notes(notes);
707
708    term::emit(out, config, sources, &diagnostic)?;
709    return Ok(());
710
711    fn format_compile_error(
712        this: &FatalDiagnostic,
713        sources: &Sources,
714        span: Span,
715        kind: &ErrorKind,
716        labels: &mut ::rust_alloc::vec::Vec<d::Label<SourceId>>,
717        notes: &mut ::rust_alloc::vec::Vec<rust_alloc::string::String>,
718    ) -> Result<(), EmitError> {
719        match kind {
720            ErrorKind::ImportCycle { path } => {
721                let mut it = path.iter();
722                let last = it.next_back();
723
724                for (step, entry) in (1..).zip(it) {
725                    labels.push(
726                        d::Label::secondary(entry.location.source_id, entry.location.span.range())
727                            .with_message(format!("Step #{} for `{}`", step, entry.item)),
728                    );
729                }
730
731                if let Some(entry) = last {
732                    labels.push(
733                        d::Label::secondary(entry.location.source_id, entry.location.span.range())
734                            .with_message(format!("Final step cycling back to `{}`", entry.item)),
735                    );
736                }
737            }
738            ErrorKind::NotVisible {
739                chain,
740                location: Location { source_id, span },
741                ..
742            } => {
743                for Location { source_id, span } in chain {
744                    labels.push(
745                        d::Label::secondary(*source_id, span.range())
746                            .with_message("Re-exported here"),
747                    );
748                }
749
750                labels.push(
751                    d::Label::secondary(*source_id, span.range()).with_message("defined here"),
752                );
753            }
754            ErrorKind::NotVisibleMod {
755                chain,
756                location: Location { source_id, span },
757                ..
758            } => {
759                for Location { source_id, span } in chain {
760                    labels.push(
761                        d::Label::secondary(*source_id, span.range())
762                            .with_message("Re-exported here"),
763                    );
764                }
765
766                labels.push(
767                    d::Label::secondary(*source_id, span.range())
768                        .with_message("Module defined here"),
769                );
770            }
771            ErrorKind::AmbiguousItem { locations, .. } => {
772                for (Location { source_id, span }, item) in locations {
773                    labels.push(
774                        d::Label::secondary(*source_id, span.range())
775                            .with_message(format!("Here as `{item}`")),
776                    );
777                }
778            }
779            ErrorKind::AmbiguousContextItem { infos, .. } => {
780                for info in infos.as_ref() {
781                    labels.push(
782                        d::Label::secondary(this.source_id, span.range())
783                            .with_message(format!("Could be `{info}`")),
784                    );
785                }
786            }
787            ErrorKind::DuplicateObjectKey { existing, object } => {
788                labels.push(
789                    d::Label::secondary(this.source_id(), existing.range())
790                        .with_message("Previously defined here"),
791                );
792
793                labels.push(
794                    d::Label::secondary(this.source_id(), object.range())
795                        .with_message("Object being defined here"),
796                );
797            }
798            ErrorKind::ModAlreadyLoaded { existing, .. } => {
799                let (existing_source_id, existing_span) = *existing;
800
801                labels.push(
802                    d::Label::secondary(existing_source_id, existing_span.range())
803                        .with_message("Previously loaded here"),
804                );
805            }
806            ErrorKind::ExpectedBlockSemiColon { followed_span } => {
807                labels.push(
808                    d::Label::secondary(this.source_id(), followed_span.range())
809                        .with_message("Because this immediately follows"),
810                );
811
812                let binding = sources.source(this.source_id(), span);
813
814                if let Some(binding) = binding {
815                    let mut note = String::new();
816                    writeln!(note, "Hint: Rewrite to `{};`", binding)?;
817                    notes.push(note.into_std());
818                }
819            }
820            ErrorKind::VariableMoved { moved_at, .. } => {
821                labels.push(
822                    d::Label::secondary(this.source_id(), moved_at.range())
823                        .with_message("Moved here"),
824                );
825            }
826            ErrorKind::NestedTest { nested_span } => {
827                labels.push(
828                    d::Label::secondary(this.source_id(), nested_span.range())
829                        .with_message("Nested in here"),
830                );
831            }
832            ErrorKind::NestedBench { nested_span } => {
833                labels.push(
834                    d::Label::secondary(this.source_id(), nested_span.range())
835                        .with_message("Nested in here"),
836                );
837            }
838            ErrorKind::PatternMissingFields { fields, .. } => {
839                let pl = if fields.len() == 1 { "field" } else { "fields" };
840
841                let fields = fields.join(", ");
842
843                labels.push(
844                    d::Label::secondary(this.source_id(), span.range())
845                        .with_message(format!("Missing {}: {}", pl, fields)),
846                );
847
848                notes.push(
849                    "You can also make the pattern non-exhaustive by adding `..`"
850                        .try_to_string()?
851                        .into_std(),
852                );
853            }
854            ErrorKind::ConflictingLabels { existing, .. } => {
855                labels.push(
856                    d::Label::secondary(this.source_id(), existing.range())
857                        .with_message("Existing label here"),
858                );
859            }
860            ErrorKind::DuplicateSelectDefault { existing, .. } => {
861                labels.push(
862                    d::Label::secondary(this.source_id(), existing.range())
863                        .with_message("Existing branch here"),
864                );
865            }
866            _ => (),
867        }
868
869        Ok(())
870    }
871}