1#[cfg(feature = "wgsl-in")]
4use crate::Span;
5use crate::{Arena, Handle};
6#[cfg(feature = "arbitrary")]
7use arbitrary::Arbitrary;
8#[cfg(feature = "wgsl-in")]
9use indexmap::IndexMap;
10#[cfg(feature = "deserialize")]
11use serde::Deserialize;
12#[cfg(feature = "serialize")]
13use serde::Serialize;
14
15#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
19#[cfg_attr(feature = "serialize", derive(Serialize))]
20#[cfg_attr(feature = "deserialize", derive(Deserialize))]
21#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
22pub enum Severity {
23 Off,
24 Info,
25 Warning,
26 Error,
27}
28
29impl Severity {
30 pub(crate) fn report_diag<E>(
36 self,
37 err: E,
38 log_handler: impl FnOnce(E, log::Level),
39 ) -> Result<(), E> {
40 let log_level = match self {
41 Severity::Off => return Ok(()),
42
43 Severity::Info => log::Level::Info,
45 Severity::Warning => log::Level::Warn,
46
47 Severity::Error => return Err(err),
48 };
49 log_handler(err, log_level);
50 Ok(())
51 }
52}
53
54#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
58#[cfg_attr(feature = "serialize", derive(Serialize))]
59#[cfg_attr(feature = "deserialize", derive(Deserialize))]
60#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
61pub enum FilterableTriggeringRule {
62 Standard(StandardFilterableTriggeringRule),
63 Unknown(Box<str>),
64 User(Box<[Box<str>; 2]>),
65}
66
67#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
71#[cfg_attr(feature = "serialize", derive(Serialize))]
72#[cfg_attr(feature = "deserialize", derive(Deserialize))]
73#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
74pub enum StandardFilterableTriggeringRule {
75 DerivativeUniformity,
76}
77
78impl StandardFilterableTriggeringRule {
79 pub(crate) const fn default_severity(self) -> Severity {
84 match self {
85 Self::DerivativeUniformity => Severity::Error,
86 }
87 }
88}
89
90#[derive(Clone, Debug)]
94#[cfg_attr(feature = "serialize", derive(Serialize))]
95#[cfg_attr(feature = "deserialize", derive(Deserialize))]
96#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
97pub struct DiagnosticFilter {
98 pub new_severity: Severity,
99 pub triggering_rule: FilterableTriggeringRule,
100}
101
102#[cfg(feature = "wgsl-in")]
107pub(crate) enum ShouldConflictOnFullDuplicate {
108 Yes,
110 No,
112}
113
114#[derive(Clone, Debug, Default)]
132#[cfg(feature = "wgsl-in")]
133pub(crate) struct DiagnosticFilterMap(IndexMap<FilterableTriggeringRule, (Severity, Span)>);
134
135#[cfg(feature = "wgsl-in")]
136impl DiagnosticFilterMap {
137 pub(crate) fn new() -> Self {
138 Self::default()
139 }
140
141 pub(crate) fn add(
143 &mut self,
144 diagnostic_filter: DiagnosticFilter,
145 span: Span,
146 should_conflict_on_full_duplicate: ShouldConflictOnFullDuplicate,
147 ) -> Result<(), ConflictingDiagnosticRuleError> {
148 use indexmap::map::Entry;
149
150 let &mut Self(ref mut diagnostic_filters) = self;
151 let DiagnosticFilter {
152 new_severity,
153 triggering_rule,
154 } = diagnostic_filter;
155
156 match diagnostic_filters.entry(triggering_rule.clone()) {
157 Entry::Vacant(entry) => {
158 entry.insert((new_severity, span));
159 }
160 Entry::Occupied(entry) => {
161 let &(first_severity, first_span) = entry.get();
162 let should_conflict_on_full_duplicate = match should_conflict_on_full_duplicate {
163 ShouldConflictOnFullDuplicate::Yes => true,
164 ShouldConflictOnFullDuplicate::No => false,
165 };
166 if first_severity != new_severity || should_conflict_on_full_duplicate {
167 return Err(ConflictingDiagnosticRuleError {
168 triggering_rule_spans: [first_span, span],
169 });
170 }
171 }
172 }
173 Ok(())
174 }
175
176 pub(crate) fn is_empty(&self) -> bool {
178 let &Self(ref map) = self;
179 map.is_empty()
180 }
181
182 pub(crate) fn spans(&self) -> impl Iterator<Item = Span> + '_ {
184 let &Self(ref map) = self;
185 map.iter().map(|(_, &(_, span))| span)
186 }
187}
188
189#[cfg(feature = "wgsl-in")]
190impl IntoIterator for DiagnosticFilterMap {
191 type Item = (FilterableTriggeringRule, (Severity, Span));
192
193 type IntoIter = indexmap::map::IntoIter<FilterableTriggeringRule, (Severity, Span)>;
194
195 fn into_iter(self) -> Self::IntoIter {
196 let Self(this) = self;
197 this.into_iter()
198 }
199}
200
201#[cfg(feature = "wgsl-in")]
203#[derive(Clone, Debug)]
204pub(crate) struct ConflictingDiagnosticRuleError {
205 pub triggering_rule_spans: [Span; 2],
206}
207
208#[derive(Clone, Debug)]
235#[cfg_attr(feature = "serialize", derive(Serialize))]
236#[cfg_attr(feature = "deserialize", derive(Deserialize))]
237#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
238pub struct DiagnosticFilterNode {
239 pub inner: DiagnosticFilter,
240 pub parent: Option<Handle<DiagnosticFilterNode>>,
241}
242
243impl DiagnosticFilterNode {
244 pub(crate) fn search(
250 node: Option<Handle<Self>>,
251 arena: &Arena<Self>,
252 triggering_rule: StandardFilterableTriggeringRule,
253 ) -> Severity {
254 let mut next = node;
255 while let Some(handle) = next {
256 let node = &arena[handle];
257 let &Self { ref inner, parent } = node;
258 let &DiagnosticFilter {
259 triggering_rule: ref rule,
260 new_severity,
261 } = inner;
262
263 if rule == &FilterableTriggeringRule::Standard(triggering_rule) {
264 return new_severity;
265 }
266
267 next = parent;
268 }
269 triggering_rule.default_severity()
270 }
271}