// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#[cfg(test)]
mod tests;
use crate::compile::{Source, SourceMap};
use miette::{Diagnostic, MietteError, MietteSpanContents, SourceCode, SourceSpan, SpanContents};
use std::{
error::Error,
fmt::{self, Debug, Display, Formatter},
};
#[derive(Clone, Debug)]
pub struct WithSource<E> {
sources: Vec<Source>,
error: E,
}
impl<E: Diagnostic + Send + Sync> WithSource<E> {
pub fn error(&self) -> &E {
&self.error
}
pub fn into_error(self) -> E {
self.error
}
/// Construct a diagnostic with source information from a source map.
/// Since errors may contain labeled spans from any source file in the
/// compilation, the entire source map is needed to resolve offsets.
pub fn from_map(sources: &SourceMap, error: E) -> Self {
// Filter the source map to the relevant sources
// to avoid cloning all of them.
let mut filtered = Vec::<Source>::new();
for offset in error
.labels()
.into_iter()
.flatten()
.map(|label| u32::try_from(label.offset()).expect("offset should fit into u32"))
{
let source = sources
.find_by_offset(offset)
.expect("expected to find source at offset");
// Keep the vector sorted by source offsets
match filtered.binary_search_by_key(&source.offset, |s| s.offset) {
Ok(_) => {} // source already in vector
Err(pos) => filtered.insert(pos, source.clone()),
}
}
Self {
sources: filtered,
error,
}
}
pub fn into_with_source<T>(self) -> WithSource<T>
where
T: From<E>,
{
WithSource {
sources: self.sources,
error: self.error.into(),
}
}
/// Takes a span that uses `SourceMap` offsets, and returns
/// a span that is relative to the `Source` that the span falls into,
/// along with a reference to the `Source`.
pub fn resolve_span(&self, span: &SourceSpan) -> (&Source, SourceSpan) {
let offset = u32::try_from(span.offset()).expect("expected the offset to fit into u32");
let source = self
.sources
.iter()
.rev()
.find(|source| offset >= source.offset)
.expect("expected to find source at span");
(source, with_offset(span, |o| o - (source.offset as usize)))
}
}
impl<E: Diagnostic> Error for WithSource<E> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.error.source()
}
}
impl<E: Diagnostic + Send + Sync> Diagnostic for WithSource<E> {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.error.code()
}
fn severity(&self) -> Option<miette::Severity> {
self.error.severity()
}
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.error.help()
}
fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.error.url()
}
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
Some(self)
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
self.error.labels()
}
fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
self.error.related()
}
fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
self.error.diagnostic_source()
}
}
impl<E: Diagnostic + Display> Display for WithSource<E> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
std::fmt::Display::fmt(&self.error, f)
}
}
impl<E: Diagnostic + Sync + Send> SourceCode for WithSource<E> {
fn read_span<'a>(
&'a self,
span: &SourceSpan,
context_lines_before: usize,
context_lines_after: usize,
) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError> {
let (source, source_relative_span) = self.resolve_span(span);
let contents = source.contents.read_span(
&source_relative_span,
context_lines_before,
context_lines_after,
)?;
Ok(Box::new(MietteSpanContents::new_named(
source.name.to_string(),
contents.data(),
with_offset(contents.span(), |o| o + (source.offset as usize)),
contents.line(),
contents.column(),
contents.line_count(),
)))
}
}
fn with_offset(span: &SourceSpan, f: impl FnOnce(usize) -> usize) -> SourceSpan {
SourceSpan::new(f(span.offset()).into(), span.len().into())
}microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
compiler/qsc_frontend/src/error.rs
159lines · modepreview