Error formatting impls closer to errors

This is achieved by making a ErrorFormatter helper that is passed to
fmt_pretty, making the impls a lot simpler.
This commit is contained in:
Mikko Lehtonen
2021-07-29 23:13:25 +03:00
committed by Dzmitry Malyshau
parent e4cdddc639
commit 019dca3cfa
9 changed files with 300 additions and 239 deletions

View File

@@ -1,5 +1,6 @@
use crate::{
device::{DeviceError, MissingDownlevelFlags, MissingFeatures, SHADER_STAGE_COUNT},
error::{ErrorFormatter, PrettyError},
hub::Resource,
id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId, Valid},
memory_init_tracker::MemoryInitTrackerAction,
@@ -155,6 +156,33 @@ pub enum CreateBindGroupError {
ResourceUsageConflict(#[from] UsageConflict),
}
impl PrettyError for CreateBindGroupError {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
fmt.error(self);
match *self {
Self::BindingZeroSize(id) => {
fmt.buffer_label(&id);
}
Self::BindingRangeTooLarge { buffer, .. } => {
fmt.buffer_label(&buffer);
}
Self::BindingSizeTooSmall { buffer, .. } => {
fmt.buffer_label(&buffer);
}
Self::InvalidBuffer(id) => {
fmt.buffer_label(&id);
}
Self::InvalidTextureView(id) => {
fmt.texture_view_label(&id);
}
Self::InvalidSampler(id) => {
fmt.sampler_label(&id);
}
_ => {}
};
}
}
#[derive(Clone, Debug, Error)]
pub enum BindingZone {
#[error("stage {0:?}")]
@@ -441,6 +469,15 @@ pub enum CreatePipelineLayoutError {
TooManyGroups { actual: usize, max: usize },
}
impl PrettyError for CreatePipelineLayoutError {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
fmt.error(self);
if let Self::InvalidBindGroupLayout(id) = *self {
fmt.bind_group_layout_label(&id);
};
}
}
#[derive(Clone, Debug, Error)]
pub enum PushConstantUploadError {
#[error("provided push constant with indices {offset}..{end_offset} overruns matching push constant range at index {idx}, with stage(s) {:?} and indices {:?}", range.stages, range.range)]

View File

@@ -43,6 +43,7 @@ use crate::{
AttachmentData, Device, DeviceError, MissingDownlevelFlags, RenderPassContext,
SHADER_STAGE_COUNT,
},
error::{ErrorFormatter, PrettyError},
hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Resource, Storage, Token},
id,
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
@@ -560,6 +561,17 @@ pub enum ExecutionError {
#[error("using {0} in a render bundle is not implemented")]
Unimplemented(&'static str),
}
impl PrettyError for ExecutionError {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
fmt.error(self);
match *self {
Self::DestroyedBuffer(id) => {
fmt.buffer_label(&id);
}
Self::Unimplemented(_reason) => {}
};
}
}
pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
@@ -1161,6 +1173,14 @@ impl RenderBundleError {
inner: RenderBundleErrorInner::Device(DeviceError::Invalid),
};
}
impl PrettyError for RenderBundleError {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
// This error is wrapper for the inner error,
// but the scope has useful labels
fmt.error(self);
self.scope.fmt_pretty(fmt);
}
}
impl<T, E> MapPassErr<T, RenderBundleError> for Result<T, E>
where

View File

@@ -6,6 +6,7 @@ use crate::{
StateChange,
},
device::MissingDownlevelFlags,
error::{ErrorFormatter, PrettyError},
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id,
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
@@ -161,6 +162,24 @@ pub enum ComputePassErrorInner {
MissingDownlevelFlags(#[from] MissingDownlevelFlags),
}
impl PrettyError for ComputePassErrorInner {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
fmt.error(self);
match *self {
Self::InvalidBindGroup(id) => {
fmt.bind_group_label(&id);
}
Self::InvalidPipeline(id) => {
fmt.compute_pipeline_label(&id);
}
Self::InvalidIndirectBuffer(id) => {
fmt.buffer_label(&id);
}
_ => {}
};
}
}
/// Error encountered when performing a compute pass.
#[derive(Clone, Debug, Error)]
#[error("{scope}")]
@@ -169,6 +188,14 @@ pub struct ComputePassError {
#[source]
inner: ComputePassErrorInner,
}
impl PrettyError for ComputePassError {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
// This error is wrapper for the inner error,
// but the scope has useful labels
fmt.error(self);
self.scope.fmt_pretty(fmt);
}
}
impl<T, E> MapPassErr<T, ComputePassError> for Result<T, E>
where

View File

@@ -3,6 +3,7 @@
use crate::{
binding_model::PushConstantUploadError,
error::ErrorFormatter,
id,
track::UseExtendError,
validation::{MissingBufferUsageError, MissingTextureUsageError},
@@ -93,6 +94,23 @@ pub enum RenderCommandError {
#[error("Support for {0} is not implemented yet")]
Unimplemented(&'static str),
}
impl crate::error::PrettyError for RenderCommandError {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
fmt.error(self);
match *self {
Self::InvalidBindGroup(id) => {
fmt.bind_group_label(&id);
}
Self::InvalidPipeline(id) => {
fmt.render_pipeline_label(&id);
}
Self::Buffer(id, ..) | Self::DestroyedBuffer(id) => {
fmt.buffer_label(&id);
}
_ => {}
};
}
}
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(

View File

@@ -14,6 +14,7 @@ pub use self::query::*;
pub use self::render::*;
pub use self::transfer::*;
use crate::error::{ErrorFormatter, PrettyError};
use crate::{
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id,
@@ -499,3 +500,40 @@ pub enum PassErrorScope {
#[error("In a pop_debug_group command")]
PopDebugGroup,
}
impl PrettyError for PassErrorScope {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
// This error is not in the error chain, only notes are needed
match *self {
Self::Pass(id) => {
fmt.command_buffer_label(&id);
}
Self::SetBindGroup(id) => {
fmt.bind_group_label(&id);
}
Self::SetPipelineRender(id) => {
fmt.render_pipeline_label(&id);
}
Self::SetPipelineCompute(id) => {
fmt.compute_pipeline_label(&id);
}
Self::SetVertexBuffer(id) => {
fmt.buffer_label(&id);
}
Self::SetIndexBuffer(id) => {
fmt.buffer_label(&id);
}
Self::Draw {
pipeline: Some(id), ..
} => {
fmt.render_pipeline_label(&id);
}
Self::Dispatch {
pipeline: Some(id), ..
} => {
fmt.compute_pipeline_label(&id);
}
_ => {}
}
}
}

View File

@@ -10,6 +10,7 @@ use crate::{
AttachmentData, MissingDownlevelFlags, MissingFeatures, RenderPassCompatibilityError,
RenderPassContext,
},
error::{ErrorFormatter, PrettyError},
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id,
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
@@ -458,6 +459,15 @@ pub enum RenderPassErrorInner {
QueryUse(#[from] QueryUseError),
}
impl PrettyError for RenderPassErrorInner {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
fmt.error(self);
if let Self::InvalidAttachment(id) = *self {
fmt.texture_view_label_with_key(&id, "attachment");
};
}
}
impl From<MissingBufferUsageError> for RenderPassErrorInner {
fn from(error: MissingBufferUsageError) -> Self {
Self::RenderCommand(error.into())
@@ -478,6 +488,14 @@ pub struct RenderPassError {
#[source]
inner: RenderPassErrorInner,
}
impl PrettyError for RenderPassError {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
// This error is wrapper for the inner error,
// but the scope has useful labels
fmt.error(self);
self.scope.fmt_pretty(fmt);
}
}
impl<T, E> MapPassErr<T, RenderPassError> for Result<T, E>
where

View File

@@ -3,6 +3,7 @@ use crate::device::trace::Command as TraceCommand;
use crate::{
command::{CommandBuffer, CommandEncoderError},
conv,
error::{ErrorFormatter, PrettyError},
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id::{BufferId, CommandEncoderId, TextureId},
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
@@ -92,6 +93,38 @@ pub enum TransferError {
CopyToForbiddenTextureFormat(wgt::TextureFormat),
}
impl PrettyError for TransferError {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
fmt.error(self);
match *self {
Self::InvalidBuffer(id) => {
fmt.buffer_label(&id);
}
Self::InvalidTexture(id) => {
fmt.texture_label(&id);
}
// Self::MissingCopySrcUsageFlag(buf_opt, tex_opt) => {
// if let Some(buf) = buf_opt {
// let name = crate::gfx_select!(buf => global.buffer_label(buf));
// ret.push_str(&format_label_line("source", &name));
// }
// if let Some(tex) = tex_opt {
// let name = crate::gfx_select!(tex => global.texture_label(tex));
// ret.push_str(&format_label_line("source", &name));
// }
// }
Self::MissingCopyDstUsageFlag(buf_opt, tex_opt) => {
if let Some(buf) = buf_opt {
fmt.buffer_label_with_key(&buf, "destination");
}
if let Some(tex) = tex_opt {
fmt.texture_label_with_key(&tex, "destination");
}
}
_ => {}
};
}
}
/// Error encountered while attempting to do a copy on a command encoder.
#[derive(Clone, Debug, Error)]
pub enum CopyError {

View File

@@ -16,288 +16,145 @@ impl<T: fmt::Display> AsDisplay for T {
}
}
pub trait PrettyError: Error {
fn fmt_pretty(&self, _global: &Global<IdentityManagerFactory>) -> String {
format_error_line(self.as_display())
}
pub struct ErrorFormatter<'a> {
writer: &'a mut dyn fmt::Write,
global: &'a Global<IdentityManagerFactory>,
}
impl PrettyError for super::command::RenderCommandError {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
let mut ret = format_error_line(self);
match *self {
Self::InvalidBindGroup(id) => {
let name = gfx_select!(id => global.bind_group_label(id));
ret.push_str(&format_label_line("bind group", &name));
}
Self::InvalidPipeline(id) => {
let name = gfx_select!(id => global.render_pipeline_label(id));
ret.push_str(&format_label_line("render pipeline", &name));
}
Self::Buffer(id, ..) | Self::DestroyedBuffer(id) => {
let name = gfx_select!(id => global.buffer_label(id));
ret.push_str(&format_label_line("buffer", &name));
}
_ => {}
};
ret
impl<'a> ErrorFormatter<'a> {
pub fn error(&mut self, err: &dyn fmt::Display) {
writeln!(self.writer, " {}", err).expect("Error formatting error");
}
}
impl PrettyError for crate::binding_model::CreateBindGroupError {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
let mut ret = format_error_line(self);
match *self {
Self::InvalidBuffer(id) => {
let name = crate::gfx_select!(id => global.buffer_label(id));
ret.push_str(&format_label_line("buffer", &name));
}
Self::InvalidTextureView(id) => {
let name = crate::gfx_select!(id => global.texture_view_label(id));
ret.push_str(&format_label_line("texture view", &name));
}
Self::InvalidSampler(id) => {
let name = crate::gfx_select!(id => global.sampler_label(id));
ret.push_str(&format_label_line("sampler", &name));
}
_ => {}
};
ret
}
}
impl PrettyError for crate::binding_model::CreatePipelineLayoutError {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
let mut ret = format_error_line(self);
if let Self::InvalidBindGroupLayout(id) = *self {
let name = crate::gfx_select!(id => global.bind_group_layout_label(id));
ret.push_str(&format_label_line("bind group layout", &name));
};
ret
pub fn note(&mut self, note: &dyn fmt::Display) {
writeln!(self.writer, " note: {}", note).expect("Error formatting error");
}
}
impl PrettyError for crate::command::ExecutionError {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
let mut ret = format_error_line(self);
match *self {
Self::DestroyedBuffer(id) => {
let name = crate::gfx_select!(id => global.buffer_label(id));
ret.push_str(&format_label_line("buffer", &name));
}
Self::Unimplemented(_reason) => {}
};
ret
}
}
impl PrettyError for crate::command::RenderPassErrorInner {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
let mut ret = format_error_line(self);
if let Self::InvalidAttachment(id) = *self {
let name = crate::gfx_select!(id => global.texture_view_label(id));
ret.push_str(&format_label_line("attachment", &name));
};
ret
}
}
impl PrettyError for crate::command::RenderPassError {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
// This error is wrapper for the inner error,
// but the scope has useful labels
format_error_line(self) + &self.scope.fmt_pretty(global)
}
}
impl PrettyError for crate::command::ComputePassError {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
// This error is wrapper for the inner error,
// but the scope has useful labels
format_error_line(self) + &self.scope.fmt_pretty(global)
}
}
impl PrettyError for crate::command::RenderBundleError {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
// This error is wrapper for the inner error,
// but the scope has useful labels
format_error_line(self) + &self.scope.fmt_pretty(global)
}
}
impl PrettyError for crate::command::ComputePassErrorInner {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
let mut ret = format_error_line(self);
match *self {
Self::InvalidBindGroup(id) => {
let name = crate::gfx_select!(id => global.bind_group_label(id));
ret.push_str(&format_label_line("bind group", &name));
}
Self::InvalidPipeline(id) => {
let name = crate::gfx_select!(id => global.compute_pipeline_label(id));
ret.push_str(&format_label_line("pipeline", &name));
}
Self::InvalidIndirectBuffer(id) => {
let name = crate::gfx_select!(id => global.buffer_label(id));
ret.push_str(&format_label_line("indirect buffer", &name));
}
_ => {}
};
ret
}
}
impl PrettyError for crate::command::TransferError {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
let mut ret = format_error_line(self);
match *self {
Self::InvalidBuffer(id) => {
let name = crate::gfx_select!(id => global.buffer_label(id));
ret.push_str(&format_label_line("label", &name));
}
Self::InvalidTexture(id) => {
let name = crate::gfx_select!(id => global.texture_label(id));
ret.push_str(&format_label_line("texture", &name));
}
// Self::MissingCopySrcUsageFlag(buf_opt, tex_opt) => {
// if let Some(buf) = buf_opt {
// let name = crate::gfx_select!(buf => global.buffer_label(buf));
// ret.push_str(&format_label_line("source", &name));
// }
// if let Some(tex) = tex_opt {
// let name = crate::gfx_select!(tex => global.texture_label(tex));
// ret.push_str(&format_label_line("source", &name));
// }
// }
Self::MissingCopyDstUsageFlag(buf_opt, tex_opt) => {
if let Some(buf) = buf_opt {
let name = crate::gfx_select!(buf => global.buffer_label(buf));
ret.push_str(&format_label_line("destination", &name));
}
if let Some(tex) = tex_opt {
let name = crate::gfx_select!(tex => global.texture_label(tex));
ret.push_str(&format_label_line("destination", &name));
}
}
_ => {}
};
ret
}
}
impl PrettyError for crate::command::PassErrorScope {
fn fmt_pretty(&self, global: &Global<IdentityManagerFactory>) -> String {
// This error is not in the error chain, only notes are needed
match *self {
Self::Pass(id) => {
let name = crate::gfx_select!(id => global.command_buffer_label(id));
format_label_line("command buffer", &name)
}
Self::SetBindGroup(id) => {
let name = crate::gfx_select!(id => global.bind_group_label(id));
format_label_line("bind group", &name)
}
Self::SetPipelineRender(id) => {
let name = crate::gfx_select!(id => global.render_pipeline_label(id));
format_label_line("render pipeline", &name)
}
Self::SetPipelineCompute(id) => {
let name = crate::gfx_select!(id => global.compute_pipeline_label(id));
format_label_line("compute pipeline", &name)
}
Self::SetVertexBuffer(id) => {
let name = crate::gfx_select!(id => global.buffer_label(id));
format_label_line("buffer", &name)
}
Self::SetIndexBuffer(id) => {
let name = crate::gfx_select!(id => global.buffer_label(id));
format_label_line("buffer", &name)
}
Self::Draw { pipeline, .. } => {
if let Some(id) = pipeline {
let name = crate::gfx_select!(id => global.render_pipeline_label(id));
format_label_line("render pipeline", &name)
} else {
String::new()
}
}
Self::Dispatch { pipeline, .. } => {
if let Some(id) = pipeline {
let name = crate::gfx_select!(id => global.compute_pipeline_label(id));
format_label_line("compute pipeline", &name)
} else {
String::new()
}
}
_ => String::new(),
pub fn label(&mut self, label_key: &str, label_value: &str) {
if !label_key.is_empty() && !label_value.is_empty() {
self.note(&format!("{} = `{}`", label_key, label_value));
}
}
}
impl PrettyError for ContextError {
fn fmt_pretty(&self, _global: &Global<IdentityManagerFactory>) -> String {
format_error_line(self.as_display()) + &format_label_line(self.label_key, &self.label)
pub fn bind_group_label(&mut self, id: &crate::id::BindGroupId) {
let global = self.global;
let label = gfx_select!(id => global.bind_group_label(*id));
self.label("bind group", &label);
}
pub fn bind_group_layout_label(&mut self, id: &crate::id::BindGroupLayoutId) {
let global = self.global;
let label = gfx_select!(id => global.bind_group_layout_label(*id));
self.label("bind group layout", &label);
}
pub fn render_pipeline_label(&mut self, id: &crate::id::RenderPipelineId) {
let global = self.global;
let label = gfx_select!(id => global.render_pipeline_label(*id));
self.label("render pipeline", &label);
}
pub fn compute_pipeline_label(&mut self, id: &crate::id::ComputePipelineId) {
let global = self.global;
let label = gfx_select!(id => global.compute_pipeline_label(*id));
self.label("compute pipeline", &label);
}
pub fn buffer_label_with_key(&mut self, id: &crate::id::BufferId, key: &str) {
let global = self.global;
let label = gfx_select!(id => global.buffer_label(*id));
self.label(key, &label);
}
pub fn buffer_label(&mut self, id: &crate::id::BufferId) {
self.buffer_label_with_key(id, "buffer");
}
pub fn texture_label_with_key(&mut self, id: &crate::id::TextureId, key: &str) {
let global = self.global;
let label = gfx_select!(id => global.texture_label(*id));
self.label(key, &label);
}
pub fn texture_label(&mut self, id: &crate::id::TextureId) {
self.texture_label_with_key(id, "texture");
}
pub fn texture_view_label_with_key(&mut self, id: &crate::id::TextureViewId, key: &str) {
let global = self.global;
let label = gfx_select!(id => global.texture_view_label(*id));
self.label(key, &label);
}
pub fn texture_view_label(&mut self, id: &crate::id::TextureViewId) {
self.texture_view_label_with_key(id, "texture view");
}
pub fn sampler_label(&mut self, id: &crate::id::SamplerId) {
let global = self.global;
let label = gfx_select!(id => global.sampler_label(*id));
self.label("sampler", &label);
}
pub fn command_buffer_label(&mut self, id: &crate::id::CommandBufferId) {
let global = self.global;
let label = gfx_select!(id => global.command_buffer_label(*id));
self.label("command buffer", &label);
}
}
pub fn format_error_line(err: &dyn fmt::Display) -> String {
format!(" {}\n", err)
}
pub fn format_note_line(note: &dyn fmt::Display) -> String {
format!(" note: {}\n", note)
}
pub fn format_label_line(label_key: &str, label_value: &str) -> String {
if label_key.is_empty() || label_value.is_empty() {
String::new()
} else {
format_note_line(&format!("{} = `{}`", label_key, label_value))
pub trait PrettyError: Error {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
fmt.error(self.as_display());
}
}
pub fn format_pretty_any(
writer: &mut dyn fmt::Write,
global: &Global<IdentityManagerFactory>,
error: &(dyn Error + 'static),
) -> String {
) {
let mut fmt = ErrorFormatter { writer, global };
if let Some(pretty_err) = error.downcast_ref::<ContextError>() {
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
if let Some(pretty_err) = error.downcast_ref::<crate::command::RenderCommandError>() {
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
if let Some(pretty_err) = error.downcast_ref::<crate::binding_model::CreateBindGroupError>() {
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
if let Some(pretty_err) =
error.downcast_ref::<crate::binding_model::CreatePipelineLayoutError>()
{
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
if let Some(pretty_err) = error.downcast_ref::<crate::command::ExecutionError>() {
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
if let Some(pretty_err) = error.downcast_ref::<crate::command::RenderPassErrorInner>() {
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
if let Some(pretty_err) = error.downcast_ref::<crate::command::RenderPassError>() {
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
if let Some(pretty_err) = error.downcast_ref::<crate::command::ComputePassErrorInner>() {
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
if let Some(pretty_err) = error.downcast_ref::<crate::command::ComputePassError>() {
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
if let Some(pretty_err) = error.downcast_ref::<crate::command::RenderBundleError>() {
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
if let Some(pretty_err) = error.downcast_ref::<crate::command::TransferError>() {
return pretty_err.fmt_pretty(global);
return pretty_err.fmt_pretty(&mut fmt);
}
// default
format_error_line(error.as_display())
fmt.error(error.as_display())
}
#[derive(Debug)]
@@ -308,6 +165,13 @@ pub struct ContextError {
pub label: String,
}
impl PrettyError for ContextError {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
fmt.error(self.as_display());
fmt.label(self.label_key, &self.label);
}
}
impl fmt::Display for ContextError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "In {}", self.string)

View File

@@ -188,11 +188,17 @@ impl Context {
fn format_error(&self, err: &(impl Error + 'static)) -> String {
let global = self.global();
let mut err_descs = vec![wgc::error::format_pretty_any(global, err)];
let mut err_descs = vec![];
let mut err_str = String::new();
wgc::error::format_pretty_any(&mut err_str, global, err);
err_descs.push(err_str);
let mut source_opt = err.source();
while let Some(source) = source_opt {
err_descs.push(wgc::error::format_pretty_any(global, source));
let mut source_str = String::new();
wgc::error::format_pretty_any(&mut source_str, global, source);
err_descs.push(source_str);
source_opt = source.source();
}