shader_sense/validator/
naga.rs

1//! Validation for wgsl with Naga
2
3use naga::{
4    front::wgsl::{self, ParseError},
5    valid::{Capabilities, ValidationFlags},
6};
7use std::path::Path;
8
9use crate::{
10    position::{ShaderFileRange, ShaderPosition},
11    shader::{ShaderParams, ShaderStage},
12    shader_error::{ShaderDiagnostic, ShaderDiagnosticList, ShaderDiagnosticSeverity, ShaderError},
13};
14
15use super::validator::ValidatorImpl;
16
17pub struct Naga {}
18
19impl Naga {
20    pub fn new() -> Self {
21        Self {}
22    }
23    fn from_parse_err(err: ParseError, file_path: &Path, shader_content: &str) -> ShaderDiagnostic {
24        let error = err.emit_to_string(shader_content);
25        let loc = err.location(shader_content);
26        if let Some(loc) = loc {
27            ShaderDiagnostic {
28                severity: ShaderDiagnosticSeverity::Error,
29                error,
30                range: ShaderFileRange::new(
31                    file_path.into(),
32                    ShaderPosition::new(loc.line_number - 1, loc.line_position),
33                    ShaderPosition::new(loc.line_number - 1, loc.line_position),
34                ),
35            }
36        } else {
37            ShaderDiagnostic {
38                severity: ShaderDiagnosticSeverity::Error,
39                error,
40                range: ShaderFileRange::zero(file_path.into()),
41            }
42        }
43    }
44}
45impl ValidatorImpl for Naga {
46    fn validate_shader(
47        &self,
48        shader_content: &str,
49        file_path: &Path,
50        _params: &ShaderParams,
51        _include_callback: &mut dyn FnMut(&Path) -> Option<String>,
52    ) -> Result<ShaderDiagnosticList, ShaderError> {
53        let module = match wgsl::parse_str(shader_content)
54            .map_err(|err| Self::from_parse_err(err, file_path, shader_content))
55        {
56            Ok(module) => module,
57            Err(diag) => {
58                return Ok(ShaderDiagnosticList::from(diag));
59            }
60        };
61
62        let mut validator =
63            naga::valid::Validator::new(ValidationFlags::all(), Capabilities::all());
64        if let Err(error) = validator.validate(&module) {
65            let mut list = ShaderDiagnosticList::empty();
66            for (span, _) in error.spans() {
67                let loc = span.location(shader_content);
68                list.push(ShaderDiagnostic {
69                    severity: ShaderDiagnosticSeverity::Error,
70                    error: error.emit_to_string(""),
71                    range: ShaderFileRange::new(
72                        file_path.into(),
73                        ShaderPosition::new(loc.line_number - 1, loc.line_position),
74                        ShaderPosition::new(loc.line_number - 1, loc.line_position),
75                    ),
76                });
77            }
78            if list.is_empty() {
79                Err(ShaderError::InternalErr(
80                    error.emit_to_string(shader_content),
81                ))
82            } else {
83                Ok(list)
84            }
85        } else {
86            Ok(ShaderDiagnosticList::empty())
87        }
88    }
89    fn support(&self, shader_stage: ShaderStage) -> bool {
90        match shader_stage {
91            ShaderStage::Vertex | ShaderStage::Fragment | ShaderStage::Compute => true,
92            _ => false,
93        }
94    }
95}