Files
arret
arret_compiler
arret_lsp_server
arret_rfi_derive
arret_runtime
arret_runtime_syntax
arret_syntax
stdlib
  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
use codespan_reporting::diagnostic::{Diagnostic, Label};

use arret_syntax::span::{FileId, Span};

use crate::source::SourceLoader;

/// Traces the location of report through macro expansions
#[derive(Debug, PartialEq, Clone)]
pub struct LocTrace {
    origin: Span,
    macro_invocation: Option<Span>,
}

impl LocTrace {
    pub fn new(origin: Span, macro_invocation: Option<Span>) -> LocTrace {
        LocTrace {
            origin,
            macro_invocation,
        }
    }

    pub fn with_macro_invocation(self, macro_invocation: Span) -> LocTrace {
        LocTrace {
            macro_invocation: Some(macro_invocation),
            ..self
        }
    }

    pub fn origin(&self) -> Span {
        self.origin
    }

    pub fn macro_invocation(&self) -> Option<Span> {
        self.macro_invocation
    }

    pub fn label_macro_invocation(&self, mut diagnostic: Diagnostic<FileId>) -> Diagnostic<FileId> {
        match self.macro_invocation {
            Some(macro_invocation_span) if !macro_invocation_span.contains(self.origin) => {
                let secondary_label =
                    new_secondary_label(macro_invocation_span, "in this macro invocation");

                diagnostic.labels.push(secondary_label);
                diagnostic
            }
            _ => diagnostic,
        }
    }
}

impl From<Span> for LocTrace {
    fn from(span: Span) -> LocTrace {
        LocTrace::new(span, None)
    }
}

/// Helper for converting a series of errors in to diagnostics
///
/// This is intended for use with `map_err`
pub fn errors_to_diagnostics<E: Into<Diagnostic<FileId>>>(
    errors: Vec<E>,
) -> Vec<Diagnostic<FileId>> {
    errors.into_iter().map(Into::into).collect()
}

/// Returns a diagnostic for the passed syntax errror
///
/// This is required because `arret-syntax` doesn't depend on `codespan-reporting`. It requires
/// its consumers to handle reporting themselves.
pub fn diagnostic_for_syntax_error(error: &arret_syntax::error::Error) -> Diagnostic<FileId> {
    let origin = error.span();
    let within = error.kind().within_context();

    let primary_label_message = within
        .and_then(|within| within.expected_next())
        .map(|en| en.description())
        .unwrap_or_else(|| "syntax error".to_owned());

    let primary_label = new_primary_label(origin, primary_label_message);

    let diagnostic = Diagnostic::error()
        .with_message(error.kind().message())
        .with_labels(vec![]);

    if let Some(within) = within {
        if let Some(open_char_span) = within.open_char_span() {
            let secondary_label = new_secondary_label(
                open_char_span,
                format!("{} starts here", within.description()),
            );

            return diagnostic.with_labels(vec![primary_label, secondary_label]);
        }
    }

    diagnostic.with_labels(vec![primary_label])
}

pub fn new_primary_label(span: Span, message: impl Into<String>) -> Label<FileId> {
    Label::primary(span.file_id().unwrap(), span.byte_range()).with_message(message)
}

pub fn new_secondary_label(span: Span, message: impl Into<String>) -> Label<FileId> {
    Label::secondary(span.file_id().unwrap(), span.byte_range()).with_message(message)
}

/// Emits a series of diagnostics to standard error
///
/// This ensures the diagnostics are emitted as a contiguous group even when multiple threads
/// are emitting concurrently.
pub fn emit_diagnostics_to_stderr(
    source_loader: &SourceLoader,
    diagnostics: impl IntoIterator<Item = Diagnostic<FileId>>,
) {
    use codespan_reporting::term;
    use termcolor::{ColorChoice, StandardStream};

    let config = term::Config::default();

    let stderr = StandardStream::stderr(ColorChoice::Auto);
    let mut stderr_lock = stderr.lock();

    for diagnostic in diagnostics {
        let _ = codespan_reporting::term::emit(
            &mut stderr_lock,
            &config,
            &source_loader.files(),
            &diagnostic,
        );
    }
}