mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-25 00:03:29 +00:00
feat(wgsl-in): parse diagnostic(…)
directives (with unimpl. err.)
This commit is contained in:
parent
6934f5ae91
commit
8c13d8ff56
@ -40,6 +40,12 @@ Bottom level categories:
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
### Naga
|
||||||
|
|
||||||
|
- Parse `diagnostic(…)` directives, but don't implement any triggering rules yet. By @ErichDonGubler in [#6456](https://github.com/gfx-rs/wgpu/pull/6456).
|
||||||
|
|
||||||
## 23.0.0 (2024-10-25)
|
## 23.0.0 (2024-10-25)
|
||||||
|
|
||||||
### Themes of this release
|
### Themes of this release
|
||||||
|
97
naga/src/diagnostic_filter.rs
Normal file
97
naga/src/diagnostic_filter.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
//! [`DiagnosticFilter`]s and supporting functionality.
|
||||||
|
|
||||||
|
/// A severity set on a [`DiagnosticFilter`].
|
||||||
|
///
|
||||||
|
/// <https://www.w3.org/TR/WGSL/#diagnostic-severity>
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub enum Severity {
|
||||||
|
Off,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Severity {
|
||||||
|
const ERROR: &'static str = "error";
|
||||||
|
const WARNING: &'static str = "warning";
|
||||||
|
const INFO: &'static str = "info";
|
||||||
|
const OFF: &'static str = "off";
|
||||||
|
|
||||||
|
/// Convert from a sentinel word in WGSL into its associated [`Severity`], if possible.
|
||||||
|
pub fn from_ident(s: &str) -> Option<Self> {
|
||||||
|
Some(match s {
|
||||||
|
Self::ERROR => Self::Error,
|
||||||
|
Self::WARNING => Self::Warning,
|
||||||
|
Self::INFO => Self::Info,
|
||||||
|
Self::OFF => Self::Off,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether this severity is [`Self::Error`].
|
||||||
|
///
|
||||||
|
/// Naga does not yet support diagnostic items at lesser severities than
|
||||||
|
/// [`Severity::Error`]. When this is implemented, this method should be deleted, and the
|
||||||
|
/// severity should be used directly for reporting diagnostics.
|
||||||
|
#[cfg(feature = "wgsl-in")]
|
||||||
|
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(()),
|
||||||
|
|
||||||
|
// NOTE: These severities are not yet reported.
|
||||||
|
Severity::Info => log::Level::Info,
|
||||||
|
Severity::Warning => log::Level::Warn,
|
||||||
|
|
||||||
|
Severity::Error => return Err(err),
|
||||||
|
};
|
||||||
|
log_handler(err, log_level);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A filterable triggering rule in a [`DiagnosticFilter`].
|
||||||
|
///
|
||||||
|
/// <https://www.w3.org/TR/WGSL/#filterable-triggering-rules>
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub enum FilterableTriggeringRule {
|
||||||
|
DerivativeUniformity,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilterableTriggeringRule {
|
||||||
|
const DERIVATIVE_UNIFORMITY: &'static str = "derivative_uniformity";
|
||||||
|
|
||||||
|
/// Convert from a sentinel word in WGSL into its associated [`FilterableTriggeringRule`], if possible.
|
||||||
|
pub fn from_ident(s: &str) -> Option<Self> {
|
||||||
|
Some(match s {
|
||||||
|
Self::DERIVATIVE_UNIFORMITY => Self::DerivativeUniformity,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps this [`FilterableTriggeringRule`] into the sentinel word associated with it in WGSL.
|
||||||
|
pub const fn to_ident(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::DerivativeUniformity => Self::DERIVATIVE_UNIFORMITY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "wgsl-in")]
|
||||||
|
pub(crate) const fn tracking_issue_num(self) -> u16 {
|
||||||
|
match self {
|
||||||
|
FilterableTriggeringRule::DerivativeUniformity => 5320,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A filter that modifies how diagnostics are emitted for shaders.
|
||||||
|
///
|
||||||
|
/// <https://www.w3.org/TR/WGSL/#diagnostic-filter>
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DiagnosticFilter {
|
||||||
|
pub new_severity: Severity,
|
||||||
|
pub triggering_rule: FilterableTriggeringRule,
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::diagnostic_filter::FilterableTriggeringRule;
|
||||||
use crate::front::wgsl::parse::directive::enable_extension::{
|
use crate::front::wgsl::parse::directive::enable_extension::{
|
||||||
EnableExtension, UnimplementedEnableExtension,
|
EnableExtension, UnimplementedEnableExtension,
|
||||||
};
|
};
|
||||||
@ -194,6 +195,7 @@ pub(crate) enum Error<'a> {
|
|||||||
UnknownConservativeDepth(Span),
|
UnknownConservativeDepth(Span),
|
||||||
UnknownEnableExtension(Span, &'a str),
|
UnknownEnableExtension(Span, &'a str),
|
||||||
UnknownLanguageExtension(Span, &'a str),
|
UnknownLanguageExtension(Span, &'a str),
|
||||||
|
UnknownDiagnosticRuleName(Span),
|
||||||
SizeAttributeTooLow(Span, u32),
|
SizeAttributeTooLow(Span, u32),
|
||||||
AlignAttributeTooLow(Span, Alignment),
|
AlignAttributeTooLow(Span, Alignment),
|
||||||
NonPowerOfTwoAlignAttribute(Span),
|
NonPowerOfTwoAlignAttribute(Span),
|
||||||
@ -295,6 +297,13 @@ pub(crate) enum Error<'a> {
|
|||||||
kind: UnimplementedLanguageExtension,
|
kind: UnimplementedLanguageExtension,
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
|
DiagnosticInvalidSeverity {
|
||||||
|
severity_control_name_span: Span,
|
||||||
|
},
|
||||||
|
DiagnosticNotYetImplemented {
|
||||||
|
triggering_rule: FilterableTriggeringRule,
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -562,6 +571,15 @@ impl<'a> Error<'a> {
|
|||||||
)
|
)
|
||||||
.into()],
|
.into()],
|
||||||
},
|
},
|
||||||
|
Error::UnknownDiagnosticRuleName(span) => ParseError {
|
||||||
|
message: format!("unknown `diagnostic(…)` rule name `{}`", &source[span]),
|
||||||
|
labels: vec![(span, "not a valid diagnostic rule name".into())],
|
||||||
|
notes: vec![concat!(
|
||||||
|
"See available trigger rules at ",
|
||||||
|
"<https://www.w3.org/TR/WGSL/#filterable-triggering-rules>."
|
||||||
|
)
|
||||||
|
.into()],
|
||||||
|
},
|
||||||
Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {
|
Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {
|
||||||
message: format!("struct member size must be at least {min_size}"),
|
message: format!("struct member size must be at least {min_size}"),
|
||||||
labels: vec![(bad_span, format!("must be at least {min_size}").into())],
|
labels: vec![(bad_span, format!("must be at least {min_size}").into())],
|
||||||
@ -1008,6 +1026,38 @@ impl<'a> Error<'a> {
|
|||||||
kind.tracking_issue_num()
|
kind.tracking_issue_num()
|
||||||
)],
|
)],
|
||||||
},
|
},
|
||||||
|
Error::DiagnosticInvalidSeverity {
|
||||||
|
severity_control_name_span,
|
||||||
|
} => ParseError {
|
||||||
|
message: "invalid `diagnostic(…)` severity".into(),
|
||||||
|
labels: vec![(
|
||||||
|
severity_control_name_span,
|
||||||
|
"not a valid severity level".into(),
|
||||||
|
)],
|
||||||
|
notes: vec![concat!(
|
||||||
|
"See available severities at ",
|
||||||
|
"<https://www.w3.org/TR/WGSL/#diagnostic-severity>."
|
||||||
|
)
|
||||||
|
.into()],
|
||||||
|
},
|
||||||
|
Error::DiagnosticNotYetImplemented {
|
||||||
|
triggering_rule,
|
||||||
|
span,
|
||||||
|
} => ParseError {
|
||||||
|
message: format!(
|
||||||
|
"the `{}` diagnostic filter is not yet supported",
|
||||||
|
triggering_rule.to_ident()
|
||||||
|
),
|
||||||
|
labels: vec![(span, "".into())],
|
||||||
|
notes: vec![format!(
|
||||||
|
concat!(
|
||||||
|
"Let Naga maintainers know that you ran into this at ",
|
||||||
|
"<https://github.com/gfx-rs/wgpu/issues/{}>, ",
|
||||||
|
"so they can prioritize it!"
|
||||||
|
),
|
||||||
|
triggering_rule.tracking_issue_num()
|
||||||
|
)],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ pub(crate) mod language_extension;
|
|||||||
/// A parsed sentinel word indicating the type of directive to be parsed next.
|
/// A parsed sentinel word indicating the type of directive to be parsed next.
|
||||||
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
|
||||||
pub(crate) enum DirectiveKind {
|
pub(crate) enum DirectiveKind {
|
||||||
|
/// A [`crate::diagnostic_filter`].
|
||||||
|
Diagnostic,
|
||||||
/// An [`enable_extension`].
|
/// An [`enable_extension`].
|
||||||
Enable,
|
Enable,
|
||||||
/// A [`language_extension`].
|
/// A [`language_extension`].
|
||||||
@ -23,7 +25,7 @@ impl DirectiveKind {
|
|||||||
/// Convert from a sentinel word in WGSL into its associated [`DirectiveKind`], if possible.
|
/// Convert from a sentinel word in WGSL into its associated [`DirectiveKind`], if possible.
|
||||||
pub fn from_ident(s: &str) -> Option<Self> {
|
pub fn from_ident(s: &str) -> Option<Self> {
|
||||||
Some(match s {
|
Some(match s {
|
||||||
Self::DIAGNOSTIC => Self::Unimplemented(UnimplementedDirectiveKind::Diagnostic),
|
Self::DIAGNOSTIC => Self::Diagnostic,
|
||||||
Self::ENABLE => Self::Enable,
|
Self::ENABLE => Self::Enable,
|
||||||
Self::REQUIRES => Self::Requires,
|
Self::REQUIRES => Self::Requires,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
@ -33,11 +35,10 @@ impl DirectiveKind {
|
|||||||
/// Maps this [`DirectiveKind`] into the sentinel word associated with it in WGSL.
|
/// Maps this [`DirectiveKind`] into the sentinel word associated with it in WGSL.
|
||||||
pub const fn to_ident(self) -> &'static str {
|
pub const fn to_ident(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Diagnostic => Self::DIAGNOSTIC,
|
||||||
Self::Enable => Self::ENABLE,
|
Self::Enable => Self::ENABLE,
|
||||||
Self::Requires => Self::REQUIRES,
|
Self::Requires => Self::REQUIRES,
|
||||||
Self::Unimplemented(kind) => match kind {
|
Self::Unimplemented(kind) => match kind {},
|
||||||
UnimplementedDirectiveKind::Diagnostic => Self::DIAGNOSTIC,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ impl DirectiveKind {
|
|||||||
fn iter() -> impl Iterator<Item = Self> {
|
fn iter() -> impl Iterator<Item = Self> {
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
[Self::Enable, Self::Requires]
|
[Self::Diagnostic, Self::Enable, Self::Requires]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(UnimplementedDirectiveKind::iter().map(Self::Unimplemented))
|
.chain(UnimplementedDirectiveKind::iter().map(Self::Unimplemented))
|
||||||
}
|
}
|
||||||
@ -54,15 +55,25 @@ impl DirectiveKind {
|
|||||||
/// A [`DirectiveKind`] that is not yet implemented. See [`DirectiveKind::Unimplemented`].
|
/// A [`DirectiveKind`] that is not yet implemented. See [`DirectiveKind::Unimplemented`].
|
||||||
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
|
||||||
#[cfg_attr(test, derive(strum::EnumIter))]
|
#[cfg_attr(test, derive(strum::EnumIter))]
|
||||||
pub(crate) enum UnimplementedDirectiveKind {
|
pub(crate) enum UnimplementedDirectiveKind {}
|
||||||
Diagnostic,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnimplementedDirectiveKind {
|
impl UnimplementedDirectiveKind {
|
||||||
pub const fn tracking_issue_num(self) -> u16 {
|
pub const fn tracking_issue_num(self) -> u16 {
|
||||||
match self {
|
match self {}
|
||||||
Self::Diagnostic => 5320,
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl crate::diagnostic_filter::Severity {
|
||||||
|
#[cfg(feature = "wgsl-in")]
|
||||||
|
pub(crate) fn report_wgsl_parse_diag<'a>(
|
||||||
|
self,
|
||||||
|
err: crate::front::wgsl::error::Error<'a>,
|
||||||
|
source: &str,
|
||||||
|
) -> Result<(), crate::front::wgsl::error::Error<'a>> {
|
||||||
|
self.report_diag(err, |e, level| {
|
||||||
|
let e = e.as_parse_error(source);
|
||||||
|
log::log!(level, "{}", e.emit_to_string(source));
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,25 +86,12 @@ mod test {
|
|||||||
use super::{DirectiveKind, UnimplementedDirectiveKind};
|
use super::{DirectiveKind, UnimplementedDirectiveKind};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::never_loop, unreachable_code, unused_variables)]
|
||||||
fn unimplemented_directives() {
|
fn unimplemented_directives() {
|
||||||
for unsupported_shader in UnimplementedDirectiveKind::iter() {
|
for unsupported_shader in UnimplementedDirectiveKind::iter() {
|
||||||
let shader;
|
let shader;
|
||||||
let expected_msg;
|
let expected_msg;
|
||||||
match unsupported_shader {
|
match unsupported_shader {};
|
||||||
UnimplementedDirectiveKind::Diagnostic => {
|
|
||||||
shader = "diagnostic(off,derivative_uniformity);";
|
|
||||||
expected_msg = "\
|
|
||||||
error: the `diagnostic` directive is not yet implemented
|
|
||||||
┌─ wgsl:1:1
|
|
||||||
│
|
|
||||||
1 │ diagnostic(off,derivative_uniformity);
|
|
||||||
│ ^^^^^^^^^^ this global directive is standard, but not yet implemented
|
|
||||||
│
|
|
||||||
= note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/5320>, so they can prioritize it!
|
|
||||||
|
|
||||||
";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_parse_err(shader, expected_msg);
|
assert_parse_err(shader, expected_msg);
|
||||||
}
|
}
|
||||||
@ -105,7 +103,7 @@ error: the `diagnostic` directive is not yet implemented
|
|||||||
let directive;
|
let directive;
|
||||||
let expected_msg;
|
let expected_msg;
|
||||||
match unsupported_shader {
|
match unsupported_shader {
|
||||||
DirectiveKind::Unimplemented(UnimplementedDirectiveKind::Diagnostic) => {
|
DirectiveKind::Diagnostic => {
|
||||||
directive = "diagnostic(off,derivative_uniformity)";
|
directive = "diagnostic(off,derivative_uniformity)";
|
||||||
expected_msg = "\
|
expected_msg = "\
|
||||||
error: expected global declaration, but found a global directive
|
error: expected global declaration, but found a global directive
|
||||||
@ -144,6 +142,7 @@ error: expected global declaration, but found a global directive
|
|||||||
|
|
||||||
";
|
";
|
||||||
}
|
}
|
||||||
|
DirectiveKind::Unimplemented(kind) => match kind {},
|
||||||
}
|
}
|
||||||
|
|
||||||
let shader = format!(
|
let shader = format!(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::diagnostic_filter::{self, DiagnosticFilter, FilterableTriggeringRule};
|
||||||
use crate::front::wgsl::error::{Error, ExpectedToken};
|
use crate::front::wgsl::error::{Error, ExpectedToken};
|
||||||
use crate::front::wgsl::parse::directive::enable_extension::{
|
use crate::front::wgsl::parse::directive::enable_extension::{
|
||||||
EnableExtension, EnableExtensions, UnimplementedEnableExtension,
|
EnableExtension, EnableExtensions, UnimplementedEnableExtension,
|
||||||
@ -2529,6 +2530,17 @@ impl Parser {
|
|||||||
self.push_rule_span(Rule::Directive, &mut lexer);
|
self.push_rule_span(Rule::Directive, &mut lexer);
|
||||||
let _ = lexer.next_ident_with_span().unwrap();
|
let _ = lexer.next_ident_with_span().unwrap();
|
||||||
match kind {
|
match kind {
|
||||||
|
DirectiveKind::Diagnostic => {
|
||||||
|
if let Some(diagnostic_filter) = self.diagnostic_filter(&mut lexer)? {
|
||||||
|
let triggering_rule = diagnostic_filter.triggering_rule;
|
||||||
|
let span = self.peek_rule_span(&lexer);
|
||||||
|
Err(Error::DiagnosticNotYetImplemented {
|
||||||
|
triggering_rule,
|
||||||
|
span,
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
lexer.expect(Token::Separator(';'))?;
|
||||||
|
}
|
||||||
DirectiveKind::Enable => {
|
DirectiveKind::Enable => {
|
||||||
self.directive_ident_list(&mut lexer, |ident, span| {
|
self.directive_ident_list(&mut lexer, |ident, span| {
|
||||||
let kind = EnableExtension::from_ident(ident, span)?;
|
let kind = EnableExtension::from_ident(ident, span)?;
|
||||||
@ -2614,4 +2626,55 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
Ok(brace_nesting_level + 1)
|
Ok(brace_nesting_level + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn diagnostic_filter<'a>(
|
||||||
|
&self,
|
||||||
|
lexer: &mut Lexer<'a>,
|
||||||
|
) -> Result<Option<DiagnosticFilter>, Error<'a>> {
|
||||||
|
lexer.expect(Token::Paren('('))?;
|
||||||
|
|
||||||
|
let (severity_control_name, severity_control_name_span) = lexer.next_ident_with_span()?;
|
||||||
|
let new_severity = diagnostic_filter::Severity::from_ident(severity_control_name).ok_or(
|
||||||
|
Error::DiagnosticInvalidSeverity {
|
||||||
|
severity_control_name_span,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
lexer.expect(Token::Separator(','))?;
|
||||||
|
|
||||||
|
let (diagnostic_name_token, diagnostic_name_token_span) = lexer.next_ident_with_span()?;
|
||||||
|
let diagnostic_rule_name = if lexer.skip(Token::Separator('.')) {
|
||||||
|
// Don't try to validate these name tokens on two tokens, which is conventionally used
|
||||||
|
// for third-party tooling.
|
||||||
|
lexer.next_ident_with_span()?;
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(diagnostic_name_token)
|
||||||
|
};
|
||||||
|
let diagnostic_rule_name_span = diagnostic_name_token_span;
|
||||||
|
|
||||||
|
let filter = diagnostic_rule_name
|
||||||
|
.and_then(|name| {
|
||||||
|
FilterableTriggeringRule::from_ident(name)
|
||||||
|
.map(Ok)
|
||||||
|
.or_else(|| {
|
||||||
|
diagnostic_filter::Severity::Warning
|
||||||
|
.report_wgsl_parse_diag(
|
||||||
|
Error::UnknownDiagnosticRuleName(diagnostic_rule_name_span),
|
||||||
|
lexer.source,
|
||||||
|
)
|
||||||
|
.err()
|
||||||
|
.map(Err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.transpose()?
|
||||||
|
.map(|triggering_rule| DiagnosticFilter {
|
||||||
|
new_severity,
|
||||||
|
triggering_rule,
|
||||||
|
});
|
||||||
|
lexer.skip(Token::Separator(','));
|
||||||
|
lexer.expect(Token::Paren(')'))?;
|
||||||
|
|
||||||
|
Ok(filter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,6 +255,7 @@ pub mod back;
|
|||||||
mod block;
|
mod block;
|
||||||
#[cfg(feature = "compact")]
|
#[cfg(feature = "compact")]
|
||||||
pub mod compact;
|
pub mod compact;
|
||||||
|
pub mod diagnostic_filter;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod front;
|
pub mod front;
|
||||||
pub mod keywords;
|
pub mod keywords;
|
||||||
|
Loading…
Reference in New Issue
Block a user