use alloc::boxed::Box;
use crate::{Arena, Handle};
#[cfg(feature = "wgsl-in")]
use crate::FastIndexMap;
#[cfg(feature = "wgsl-in")]
use crate::Span;
#[cfg(feature = "arbitrary")]
use arbitrary::Arbitrary;
#[cfg(feature = "deserialize")]
use serde::Deserialize;
#[cfg(feature = "serialize")]
use serde::Serialize;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
pub enum Severity {
Off,
Info,
Warning,
Error,
}
impl Severity {
pub(crate) fn report_diag<E>(
self,
err: E,
log_handler: impl FnOnce(E, log::Level),
) -> Result<(), E> {
let log_level = match self {
Severity::Off => return Ok(()),
Severity::Info => log::Level::Info,
Severity::Warning => log::Level::Warn,
Severity::Error => return Err(err),
};
log_handler(err, log_level);
Ok(())
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
pub enum FilterableTriggeringRule {
Standard(StandardFilterableTriggeringRule),
Unknown(Box<str>),
User(Box<[Box<str>; 2]>),
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
pub enum StandardFilterableTriggeringRule {
DerivativeUniformity,
}
impl StandardFilterableTriggeringRule {
pub(crate) const fn default_severity(self) -> Severity {
match self {
Self::DerivativeUniformity => Severity::Error,
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
pub struct DiagnosticFilter {
pub new_severity: Severity,
pub triggering_rule: FilterableTriggeringRule,
}
#[cfg(feature = "wgsl-in")]
pub(crate) enum ShouldConflictOnFullDuplicate {
Yes,
No,
}
#[derive(Clone, Debug, Default)]
#[cfg(feature = "wgsl-in")]
pub(crate) struct DiagnosticFilterMap(FastIndexMap<FilterableTriggeringRule, (Severity, Span)>);
#[cfg(feature = "wgsl-in")]
impl DiagnosticFilterMap {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn add(
&mut self,
diagnostic_filter: DiagnosticFilter,
span: Span,
should_conflict_on_full_duplicate: ShouldConflictOnFullDuplicate,
) -> Result<(), ConflictingDiagnosticRuleError> {
use indexmap::map::Entry;
let &mut Self(ref mut diagnostic_filters) = self;
let DiagnosticFilter {
new_severity,
triggering_rule,
} = diagnostic_filter;
match diagnostic_filters.entry(triggering_rule.clone()) {
Entry::Vacant(entry) => {
entry.insert((new_severity, span));
}
Entry::Occupied(entry) => {
let &(first_severity, first_span) = entry.get();
let should_conflict_on_full_duplicate = match should_conflict_on_full_duplicate {
ShouldConflictOnFullDuplicate::Yes => true,
ShouldConflictOnFullDuplicate::No => false,
};
if first_severity != new_severity || should_conflict_on_full_duplicate {
return Err(ConflictingDiagnosticRuleError {
triggering_rule_spans: [first_span, span],
});
}
}
}
Ok(())
}
pub(crate) fn is_empty(&self) -> bool {
let &Self(ref map) = self;
map.is_empty()
}
pub(crate) fn spans(&self) -> impl Iterator<Item = Span> + '_ {
let &Self(ref map) = self;
map.iter().map(|(_, &(_, span))| span)
}
}
#[cfg(feature = "wgsl-in")]
impl IntoIterator for DiagnosticFilterMap {
type Item = (FilterableTriggeringRule, (Severity, Span));
type IntoIter = indexmap::map::IntoIter<FilterableTriggeringRule, (Severity, Span)>;
fn into_iter(self) -> Self::IntoIter {
let Self(this) = self;
this.into_iter()
}
}
#[cfg(feature = "wgsl-in")]
#[derive(Clone, Debug)]
pub(crate) struct ConflictingDiagnosticRuleError {
pub triggering_rule_spans: [Span; 2],
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
pub struct DiagnosticFilterNode {
pub inner: DiagnosticFilter,
pub parent: Option<Handle<DiagnosticFilterNode>>,
}
impl DiagnosticFilterNode {
pub(crate) fn search(
node: Option<Handle<Self>>,
arena: &Arena<Self>,
triggering_rule: StandardFilterableTriggeringRule,
) -> Severity {
let mut next = node;
while let Some(handle) = next {
let node = &arena[handle];
let &Self { ref inner, parent } = node;
let &DiagnosticFilter {
triggering_rule: ref rule,
new_severity,
} = inner;
if rule == &FilterableTriggeringRule::Standard(triggering_rule) {
return new_severity;
}
next = parent;
}
triggering_rule.default_severity()
}
}