slint/sixtyfps_compiler/diagnostics.rs
Olivier Goffart 250b0a2e5b Rust macro: get the right span for the diagnostics
Merge the features proc_macro_span and proc_macro_diagnostics, they are the same.
2020-05-27 18:37:45 +02:00

148 lines
4.4 KiB
Rust

#[derive(Debug, Clone, Default)]
pub struct Span {
pub offset: usize,
#[cfg(feature = "proc_macro_span")]
pub span: Option<proc_macro::Span>,
}
impl PartialEq for Span {
fn eq(&self, other: &Span) -> bool {
self.offset == other.offset
}
}
impl Span {
pub fn new(offset: usize) -> Self {
Self { offset, ..Default::default() }
}
}
#[cfg(feature = "proc_macro_span")]
impl From<proc_macro::Span> for Span {
fn from(span: proc_macro::Span) -> Self {
Self { span: Some(span), ..Default::default() }
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct CompilerDiagnostic {
pub message: String,
pub span: Span,
}
#[derive(Default, Debug, Clone)]
pub struct Diagnostics {
pub inner: Vec<CompilerDiagnostic>,
pub current_path: std::path::PathBuf,
}
impl IntoIterator for Diagnostics {
type Item = CompilerDiagnostic;
type IntoIter = <Vec<CompilerDiagnostic> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl Diagnostics {
pub fn push_error(&mut self, message: String, span: Span) {
self.inner.push(CompilerDiagnostic { message, span });
}
pub fn push_compiler_error(&mut self, error: CompilerDiagnostic) {
self.inner.push(error);
}
pub fn has_error(&self) -> bool {
!self.inner.is_empty()
}
/// Returns the path for a given span
///
/// (currently just return the current path)
pub fn path(&self, _span: Span) -> &std::path::Path {
&*self.current_path
}
#[cfg(feature = "display-diagnostics")]
/// Print the diagnostics on the console
pub fn print(self, source: String) {
let mut codemap = codemap::CodeMap::new();
let file = codemap.add_file(self.current_path.to_string_lossy().to_string(), source);
let file_span = file.span;
let diags: Vec<_> = self
.inner
.into_iter()
.map(|CompilerDiagnostic { message, span }| {
let s = codemap_diagnostic::SpanLabel {
span: file_span.subspan(span.offset as u64, span.offset as u64),
style: codemap_diagnostic::SpanStyle::Primary,
label: None,
};
codemap_diagnostic::Diagnostic {
level: codemap_diagnostic::Level::Error,
message,
code: None,
spans: vec![s],
}
})
.collect();
let mut emitter = codemap_diagnostic::Emitter::stderr(
codemap_diagnostic::ColorConfig::Always,
Some(&codemap),
);
emitter.emit(&diags);
}
#[cfg(feature = "display-diagnostics")]
pub fn check_and_exit_on_error(self, source: String) -> (Self, String) {
if self.has_error() {
self.print(source);
std::process::exit(-1);
}
(self, source)
}
#[cfg(feature = "proc_macro_span")]
/// Will convert the diagnostics that only have offsets to the actual span
pub fn map_offsets_to_span(&mut self, span_map: &[crate::parser::Token]) {
for d in &mut self.inner {
if d.span.span.is_none() {
//let pos =
//span_map.binary_search_by_key(d.span.offset, |x| x.0).unwrap_or_else(|x| x);
//d.span.span = span_map.get(pos).as_ref().map(|x| x.1);
let mut offset = 0;
d.span.span = span_map.iter().find_map(|t| {
if d.span.offset <= offset {
t.span
} else {
offset += t.text.len();
None
}
});
}
}
}
}
#[cfg(feature = "proc_macro_span")]
use quote::quote;
#[cfg(feature = "proc_macro_span")]
impl quote::ToTokens for Diagnostics {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let diags: Vec<_> = self
.clone()
.into_iter()
.map(|CompilerDiagnostic { message, span }| {
if let Some(span) = span.span {
quote::quote_spanned!(span.into() => compile_error!{ #message })
} else {
quote!(compile_error! { #message })
}
})
.collect();
quote!(#(#diags)*).to_tokens(tokens);
}
}