1use alloc::boxed::Box;
4
5use crate::{Arena, Handle};
6
7#[cfg(feature = "wgsl-in")]
8use crate::FastIndexMap;
9#[cfg(feature = "wgsl-in")]
10use crate::Span;
11#[cfg(feature = "arbitrary")]
12use arbitrary::Arbitrary;
13#[cfg(feature = "deserialize")]
14use serde::Deserialize;
15#[cfg(feature = "serialize")]
16use serde::Serialize;
17
18#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
22#[cfg_attr(feature = "serialize", derive(Serialize))]
23#[cfg_attr(feature = "deserialize", derive(Deserialize))]
24#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
25pub enum Severity {
26    Off,
27    Info,
28    Warning,
29    Error,
30}
31
32impl Severity {
33    pub(crate) fn report_diag<E>(
39        self,
40        err: E,
41        log_handler: impl FnOnce(E, log::Level),
42    ) -> Result<(), E> {
43        let log_level = match self {
44            Severity::Off => return Ok(()),
45
46            Severity::Info => log::Level::Info,
48            Severity::Warning => log::Level::Warn,
49
50            Severity::Error => return Err(err),
51        };
52        log_handler(err, log_level);
53        Ok(())
54    }
55}
56
57#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
61#[cfg_attr(feature = "serialize", derive(Serialize))]
62#[cfg_attr(feature = "deserialize", derive(Deserialize))]
63#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
64pub enum FilterableTriggeringRule {
65    Standard(StandardFilterableTriggeringRule),
66    Unknown(Box<str>),
67    User(Box<[Box<str>; 2]>),
68}
69
70#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
74#[cfg_attr(feature = "serialize", derive(Serialize))]
75#[cfg_attr(feature = "deserialize", derive(Deserialize))]
76#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
77pub enum StandardFilterableTriggeringRule {
78    DerivativeUniformity,
79}
80
81impl StandardFilterableTriggeringRule {
82    pub(crate) const fn default_severity(self) -> Severity {
87        match self {
88            Self::DerivativeUniformity => Severity::Error,
89        }
90    }
91}
92
93#[derive(Clone, Debug)]
97#[cfg_attr(feature = "serialize", derive(Serialize))]
98#[cfg_attr(feature = "deserialize", derive(Deserialize))]
99#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
100pub struct DiagnosticFilter {
101    pub new_severity: Severity,
102    pub triggering_rule: FilterableTriggeringRule,
103}
104
105#[cfg(feature = "wgsl-in")]
110pub(crate) enum ShouldConflictOnFullDuplicate {
111    Yes,
113    No,
115}
116
117#[derive(Clone, Debug, Default)]
135#[cfg(feature = "wgsl-in")]
136pub(crate) struct DiagnosticFilterMap(FastIndexMap<FilterableTriggeringRule, (Severity, Span)>);
137
138#[cfg(feature = "wgsl-in")]
139impl DiagnosticFilterMap {
140    pub(crate) fn new() -> Self {
141        Self::default()
142    }
143
144    pub(crate) fn add(
146        &mut self,
147        diagnostic_filter: DiagnosticFilter,
148        span: Span,
149        should_conflict_on_full_duplicate: ShouldConflictOnFullDuplicate,
150    ) -> Result<(), ConflictingDiagnosticRuleError> {
151        use indexmap::map::Entry;
152
153        let &mut Self(ref mut diagnostic_filters) = self;
154        let DiagnosticFilter {
155            new_severity,
156            triggering_rule,
157        } = diagnostic_filter;
158
159        match diagnostic_filters.entry(triggering_rule.clone()) {
160            Entry::Vacant(entry) => {
161                entry.insert((new_severity, span));
162            }
163            Entry::Occupied(entry) => {
164                let &(first_severity, first_span) = entry.get();
165                let should_conflict_on_full_duplicate = match should_conflict_on_full_duplicate {
166                    ShouldConflictOnFullDuplicate::Yes => true,
167                    ShouldConflictOnFullDuplicate::No => false,
168                };
169                if first_severity != new_severity || should_conflict_on_full_duplicate {
170                    return Err(ConflictingDiagnosticRuleError {
171                        triggering_rule_spans: [first_span, span],
172                    });
173                }
174            }
175        }
176        Ok(())
177    }
178
179    pub(crate) fn is_empty(&self) -> bool {
181        let &Self(ref map) = self;
182        map.is_empty()
183    }
184
185    pub(crate) fn spans(&self) -> impl Iterator<Item = Span> + '_ {
187        let &Self(ref map) = self;
188        map.iter().map(|(_, &(_, span))| span)
189    }
190}
191
192#[cfg(feature = "wgsl-in")]
193impl IntoIterator for DiagnosticFilterMap {
194    type Item = (FilterableTriggeringRule, (Severity, Span));
195
196    type IntoIter = indexmap::map::IntoIter<FilterableTriggeringRule, (Severity, Span)>;
197
198    fn into_iter(self) -> Self::IntoIter {
199        let Self(this) = self;
200        this.into_iter()
201    }
202}
203
204#[cfg(feature = "wgsl-in")]
206#[derive(Clone, Debug)]
207pub(crate) struct ConflictingDiagnosticRuleError {
208    pub triggering_rule_spans: [Span; 2],
209}
210
211#[derive(Clone, Debug)]
238#[cfg_attr(feature = "serialize", derive(Serialize))]
239#[cfg_attr(feature = "deserialize", derive(Deserialize))]
240#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
241pub struct DiagnosticFilterNode {
242    pub inner: DiagnosticFilter,
243    pub parent: Option<Handle<DiagnosticFilterNode>>,
244}
245
246impl DiagnosticFilterNode {
247    pub(crate) fn search(
253        node: Option<Handle<Self>>,
254        arena: &Arena<Self>,
255        triggering_rule: StandardFilterableTriggeringRule,
256    ) -> Severity {
257        let mut next = node;
258        while let Some(handle) = next {
259            let node = &arena[handle];
260            let &Self { ref inner, parent } = node;
261            let &DiagnosticFilter {
262                triggering_rule: ref rule,
263                new_severity,
264            } = inner;
265
266            if rule == &FilterableTriggeringRule::Standard(triggering_rule) {
267                return new_severity;
268            }
269
270            next = parent;
271        }
272        triggering_rule.default_severity()
273    }
274}