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}