diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/naga/src/span.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/naga/src/span.rs')
-rw-r--r-- | third_party/rust/naga/src/span.rs | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/third_party/rust/naga/src/span.rs b/third_party/rust/naga/src/span.rs new file mode 100644 index 0000000000..90bf3d174e --- /dev/null +++ b/third_party/rust/naga/src/span.rs @@ -0,0 +1,523 @@ +use crate::{Arena, Handle, UniqueArena}; +use std::{error::Error, fmt, ops::Range}; + +/// A source code span, used for error reporting. +#[derive(Clone, Copy, Debug, PartialEq, Default)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Span { + start: u32, + end: u32, +} + +impl Span { + pub const UNDEFINED: Self = Self { start: 0, end: 0 }; + /// Creates a new `Span` from a range of byte indices + /// + /// Note: end is exclusive, it doesn't belong to the `Span` + pub const fn new(start: u32, end: u32) -> Self { + Span { start, end } + } + + /// Returns a new `Span` starting at `self` and ending at `other` + pub const fn until(&self, other: &Self) -> Self { + Span { + start: self.start, + end: other.end, + } + } + + /// Modifies `self` to contain the smallest `Span` possible that + /// contains both `self` and `other` + pub fn subsume(&mut self, other: Self) { + *self = if !self.is_defined() { + // self isn't defined so use other + other + } else if !other.is_defined() { + // other isn't defined so don't try to subsume + *self + } else { + // Both self and other are defined so calculate the span that contains them both + Span { + start: self.start.min(other.start), + end: self.end.max(other.end), + } + } + } + + /// Returns the smallest `Span` possible that contains all the `Span`s + /// defined in the `from` iterator + pub fn total_span<T: Iterator<Item = Self>>(from: T) -> Self { + let mut span: Self = Default::default(); + for other in from { + span.subsume(other); + } + span + } + + /// Converts `self` to a range if the span is not unknown + pub fn to_range(self) -> Option<Range<usize>> { + if self.is_defined() { + Some(self.start as usize..self.end as usize) + } else { + None + } + } + + /// Check whether `self` was defined or is a default/unknown span + pub fn is_defined(&self) -> bool { + *self != Self::default() + } + + /// Return a [`SourceLocation`] for this span in the provided source. + pub fn location(&self, source: &str) -> SourceLocation { + let prefix = &source[..self.start as usize]; + let line_number = prefix.matches('\n').count() as u32 + 1; + let line_start = prefix.rfind('\n').map(|pos| pos + 1).unwrap_or(0); + let line_position = source[line_start..self.start as usize].chars().count() as u32 + 1; + + SourceLocation { + line_number, + line_position, + offset: self.start, + length: self.end - self.start, + } + } +} + +impl From<Range<usize>> for Span { + fn from(range: Range<usize>) -> Self { + Span { + start: range.start as u32, + end: range.end as u32, + } + } +} + +impl std::ops::Index<Span> for str { + type Output = str; + + #[inline] + fn index(&self, span: Span) -> &str { + &self[span.start as usize..span.end as usize] + } +} + +/// A human-readable representation for a span, tailored for text source. +/// +/// Corresponds to the positional members of [`GPUCompilationMessage`][gcm] from +/// the WebGPU specification, except that `offset` and `length` are in bytes +/// (UTF-8 code units), instead of UTF-16 code units. +/// +/// [gcm]: https://www.w3.org/TR/webgpu/#gpucompilationmessage +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct SourceLocation { + /// 1-based line number. + pub line_number: u32, + /// 1-based column of the start of this span + pub line_position: u32, + /// 0-based Offset in code units (in bytes) of the start of the span. + pub offset: u32, + /// Length in code units (in bytes) of the span. + pub length: u32, +} + +/// A source code span together with "context", a user-readable description of what part of the error it refers to. +pub type SpanContext = (Span, String); + +/// Wrapper class for [`Error`], augmenting it with a list of [`SpanContext`]s. +#[derive(Debug, Clone)] +pub struct WithSpan<E> { + inner: E, + #[cfg(feature = "span")] + spans: Vec<SpanContext>, +} + +impl<E> fmt::Display for WithSpan<E> +where + E: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + self.inner.fmt(f) + } +} + +#[cfg(test)] +impl<E> PartialEq for WithSpan<E> +where + E: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.inner.eq(&other.inner) + } +} + +impl<E> Error for WithSpan<E> +where + E: Error, +{ + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.inner.source() + } +} + +impl<E> WithSpan<E> { + /// Create a new [`WithSpan`] from an [`Error`], containing no spans. + pub const fn new(inner: E) -> Self { + Self { + inner, + #[cfg(feature = "span")] + spans: Vec::new(), + } + } + + /// Reverse of [`Self::new`], discards span information and returns an inner error. + #[allow(clippy::missing_const_for_fn)] // ignore due to requirement of #![feature(const_precise_live_drops)] + pub fn into_inner(self) -> E { + self.inner + } + + pub const fn as_inner(&self) -> &E { + &self.inner + } + + /// Iterator over stored [`SpanContext`]s. + pub fn spans(&self) -> impl Iterator<Item = &SpanContext> + ExactSizeIterator { + #[cfg(feature = "span")] + return self.spans.iter(); + #[cfg(not(feature = "span"))] + return std::iter::empty(); + } + + /// Add a new span with description. + #[cfg_attr(not(feature = "span"), allow(unused_variables, unused_mut))] + pub fn with_span<S>(mut self, span: Span, description: S) -> Self + where + S: ToString, + { + #[cfg(feature = "span")] + if span.is_defined() { + self.spans.push((span, description.to_string())); + } + self + } + + /// Add a [`SpanContext`]. + pub fn with_context(self, span_context: SpanContext) -> Self { + let (span, description) = span_context; + self.with_span(span, description) + } + + /// Add a [`Handle`] from either [`Arena`] or [`UniqueArena`], borrowing its span information from there + /// and annotating with a type and the handle representation. + pub(crate) fn with_handle<T, A: SpanProvider<T>>(self, handle: Handle<T>, arena: &A) -> Self { + self.with_context(arena.get_span_context(handle)) + } + + /// Convert inner error using [`From`]. + pub fn into_other<E2>(self) -> WithSpan<E2> + where + E2: From<E>, + { + WithSpan { + inner: self.inner.into(), + #[cfg(feature = "span")] + spans: self.spans, + } + } + + /// Convert inner error into another type. Joins span information contained in `self` + /// with what is returned from `func`. + pub fn and_then<F, E2>(self, func: F) -> WithSpan<E2> + where + F: FnOnce(E) -> WithSpan<E2>, + { + #[cfg_attr(not(feature = "span"), allow(unused_mut))] + let mut res = func(self.inner); + #[cfg(feature = "span")] + res.spans.extend(self.spans); + res + } + + #[cfg(feature = "span")] + /// Return a [`SourceLocation`] for our first span, if we have one. + pub fn location(&self, source: &str) -> Option<SourceLocation> { + if self.spans.is_empty() { + return None; + } + + Some(self.spans[0].0.location(source)) + } + + #[cfg(not(feature = "span"))] + /// Return a [`SourceLocation`] for our first span, if we have one. + pub fn location(&self, _source: &str) -> Option<SourceLocation> { + None + } + + #[cfg(feature = "span")] + fn diagnostic(&self) -> codespan_reporting::diagnostic::Diagnostic<()> + where + E: Error, + { + use codespan_reporting::diagnostic::{Diagnostic, Label}; + let diagnostic = Diagnostic::error() + .with_message(self.inner.to_string()) + .with_labels( + self.spans() + .map(|&(span, ref desc)| { + Label::primary((), span.to_range().unwrap()).with_message(desc.to_owned()) + }) + .collect(), + ) + .with_notes({ + let mut notes = Vec::new(); + let mut source: &dyn Error = &self.inner; + while let Some(next) = Error::source(source) { + notes.push(next.to_string()); + source = next; + } + notes + }); + diagnostic + } + + /// Emits a summary of the error to standard error stream. + #[cfg(feature = "span")] + pub fn emit_to_stderr(&self, source: &str) + where + E: Error, + { + self.emit_to_stderr_with_path(source, "wgsl") + } + + /// Emits a summary of the error to standard error stream. + #[cfg(feature = "span")] + pub fn emit_to_stderr_with_path(&self, source: &str, path: &str) + where + E: Error, + { + use codespan_reporting::{files, term}; + use term::termcolor::{ColorChoice, StandardStream}; + + let files = files::SimpleFile::new(path, source); + let config = term::Config::default(); + let writer = StandardStream::stderr(ColorChoice::Auto); + term::emit(&mut writer.lock(), &config, &files, &self.diagnostic()) + .expect("cannot write error"); + } + + /// Emits a summary of the error to a string. + #[cfg(feature = "span")] + pub fn emit_to_string(&self, source: &str) -> String + where + E: Error, + { + self.emit_to_string_with_path(source, "wgsl") + } + + /// Emits a summary of the error to a string. + #[cfg(feature = "span")] + pub fn emit_to_string_with_path(&self, source: &str, path: &str) -> String + where + E: Error, + { + use codespan_reporting::{files, term}; + use term::termcolor::NoColor; + + let files = files::SimpleFile::new(path, source); + let config = codespan_reporting::term::Config::default(); + let mut writer = NoColor::new(Vec::new()); + term::emit(&mut writer, &config, &files, &self.diagnostic()).expect("cannot write error"); + String::from_utf8(writer.into_inner()).unwrap() + } +} + +/// Convenience trait for [`Error`] to be able to apply spans to anything. +pub(crate) trait AddSpan: Sized { + type Output; + /// See [`WithSpan::new`]. + fn with_span(self) -> Self::Output; + /// See [`WithSpan::with_span`]. + fn with_span_static(self, span: Span, description: &'static str) -> Self::Output; + /// See [`WithSpan::with_context`]. + fn with_span_context(self, span_context: SpanContext) -> Self::Output; + /// See [`WithSpan::with_handle`]. + fn with_span_handle<T, A: SpanProvider<T>>(self, handle: Handle<T>, arena: &A) -> Self::Output; +} + +/// Trait abstracting over getting a span from an [`Arena`] or a [`UniqueArena`]. +pub(crate) trait SpanProvider<T> { + fn get_span(&self, handle: Handle<T>) -> Span; + fn get_span_context(&self, handle: Handle<T>) -> SpanContext { + match self.get_span(handle) { + x if !x.is_defined() => (Default::default(), "".to_string()), + known => ( + known, + format!("{} {:?}", std::any::type_name::<T>(), handle), + ), + } + } +} + +impl<T> SpanProvider<T> for Arena<T> { + fn get_span(&self, handle: Handle<T>) -> Span { + self.get_span(handle) + } +} + +impl<T> SpanProvider<T> for UniqueArena<T> { + fn get_span(&self, handle: Handle<T>) -> Span { + self.get_span(handle) + } +} + +impl<E> AddSpan for E +where + E: Error, +{ + type Output = WithSpan<Self>; + fn with_span(self) -> WithSpan<Self> { + WithSpan::new(self) + } + + fn with_span_static(self, span: Span, description: &'static str) -> WithSpan<Self> { + WithSpan::new(self).with_span(span, description) + } + + fn with_span_context(self, span_context: SpanContext) -> WithSpan<Self> { + WithSpan::new(self).with_context(span_context) + } + + fn with_span_handle<T, A: SpanProvider<T>>( + self, + handle: Handle<T>, + arena: &A, + ) -> WithSpan<Self> { + WithSpan::new(self).with_handle(handle, arena) + } +} + +/// Convenience trait for [`Result`], adding a [`MapErrWithSpan::map_err_inner`] +/// mapping to [`WithSpan::and_then`]. +pub trait MapErrWithSpan<E, E2>: Sized { + type Output: Sized; + fn map_err_inner<F, E3>(self, func: F) -> Self::Output + where + F: FnOnce(E) -> WithSpan<E3>, + E2: From<E3>; +} + +impl<T, E, E2> MapErrWithSpan<E, E2> for Result<T, WithSpan<E>> { + type Output = Result<T, WithSpan<E2>>; + fn map_err_inner<F, E3>(self, func: F) -> Result<T, WithSpan<E2>> + where + F: FnOnce(E) -> WithSpan<E3>, + E2: From<E3>, + { + self.map_err(|e| e.and_then(func).into_other::<E2>()) + } +} + +#[test] +fn span_location() { + let source = "12\n45\n\n89\n"; + assert_eq!( + Span { start: 0, end: 1 }.location(source), + SourceLocation { + line_number: 1, + line_position: 1, + offset: 0, + length: 1 + } + ); + assert_eq!( + Span { start: 1, end: 2 }.location(source), + SourceLocation { + line_number: 1, + line_position: 2, + offset: 1, + length: 1 + } + ); + assert_eq!( + Span { start: 2, end: 3 }.location(source), + SourceLocation { + line_number: 1, + line_position: 3, + offset: 2, + length: 1 + } + ); + assert_eq!( + Span { start: 3, end: 5 }.location(source), + SourceLocation { + line_number: 2, + line_position: 1, + offset: 3, + length: 2 + } + ); + assert_eq!( + Span { start: 4, end: 6 }.location(source), + SourceLocation { + line_number: 2, + line_position: 2, + offset: 4, + length: 2 + } + ); + assert_eq!( + Span { start: 5, end: 6 }.location(source), + SourceLocation { + line_number: 2, + line_position: 3, + offset: 5, + length: 1 + } + ); + assert_eq!( + Span { start: 6, end: 7 }.location(source), + SourceLocation { + line_number: 3, + line_position: 1, + offset: 6, + length: 1 + } + ); + assert_eq!( + Span { start: 7, end: 8 }.location(source), + SourceLocation { + line_number: 4, + line_position: 1, + offset: 7, + length: 1 + } + ); + assert_eq!( + Span { start: 8, end: 9 }.location(source), + SourceLocation { + line_number: 4, + line_position: 2, + offset: 8, + length: 1 + } + ); + assert_eq!( + Span { start: 9, end: 10 }.location(source), + SourceLocation { + line_number: 4, + line_position: 3, + offset: 9, + length: 1 + } + ); + assert_eq!( + Span { start: 10, end: 11 }.location(source), + SourceLocation { + line_number: 5, + line_position: 1, + offset: 10, + length: 1 + } + ); +} |