1use alloc::{
2 borrow::Cow,
3 string::{String, ToString},
4 vec,
5 vec::Vec,
6};
7
8use codespan_reporting::diagnostic::{Diagnostic, Label};
9use codespan_reporting::files::SimpleFile;
10use codespan_reporting::term;
11use pp_rs::token::PreprocessorError;
12use thiserror::Error;
13
14use super::token::TokenValue;
15#[cfg(feature = "stderr")]
16use crate::error::ErrorWrite;
17use crate::{error::replace_control_chars, SourceLocation};
18use crate::{proc::ConstantEvaluatorError, Span};
19
20fn join_with_comma(list: &[ExpectedToken]) -> String {
21 let mut string = "".to_string();
22 for (i, val) in list.iter().enumerate() {
23 string.push_str(&val.to_string());
24 match i {
25 i if i == list.len() - 1 => {}
26 i if i == list.len() - 2 => string.push_str(" or "),
27 _ => string.push_str(", "),
28 }
29 }
30 string
31}
32
33#[derive(Clone, Debug, PartialEq)]
35pub enum ExpectedToken {
36 Token(TokenValue),
38 TypeName,
40 Identifier,
42 IntLiteral,
44 FloatLiteral,
46 BoolLiteral,
48 Eof,
50}
51impl From<TokenValue> for ExpectedToken {
52 fn from(token: TokenValue) -> Self {
53 ExpectedToken::Token(token)
54 }
55}
56impl core::fmt::Display for ExpectedToken {
57 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58 match *self {
59 ExpectedToken::Token(ref token) => write!(f, "{token:?}"),
60 ExpectedToken::TypeName => write!(f, "a type"),
61 ExpectedToken::Identifier => write!(f, "identifier"),
62 ExpectedToken::IntLiteral => write!(f, "integer literal"),
63 ExpectedToken::FloatLiteral => write!(f, "float literal"),
64 ExpectedToken::BoolLiteral => write!(f, "bool literal"),
65 ExpectedToken::Eof => write!(f, "end of file"),
66 }
67 }
68}
69
70#[derive(Clone, Debug, Error)]
72#[cfg_attr(test, derive(PartialEq))]
73pub enum ErrorKind {
74 #[error("Unexpected end of file")]
76 EndOfFile,
77 #[error("Invalid profile: {0}")]
79 InvalidProfile(String),
80 #[error("Invalid version: {0}")]
82 InvalidVersion(u64),
83 #[error("Expected {expected_tokens}, found {found_token:?}", found_token = .0, expected_tokens = join_with_comma(.1))]
87 InvalidToken(TokenValue, Vec<ExpectedToken>),
88 #[error("Not implemented: {0}")]
93 NotImplemented(&'static str),
94 #[error("Unknown variable: {0}")]
96 UnknownVariable(String),
97 #[error("Unknown type: {0}")]
99 UnknownType(String),
100 #[error("Unknown field: {0}")]
102 UnknownField(String),
103 #[error("Unknown layout qualifier: {0}")]
109 UnknownLayoutQualifier(String),
110 #[error("unsupported matrix of the form matCx2 (in this case mat{columns}x2) in std140 block layout. See https://github.com/gfx-rs/wgpu/issues/4375")]
115 UnsupportedMatrixWithTwoRowsInStd140 { columns: u8 },
116 #[error("unsupported matrix of the form f16matCxR (in this case f16mat{columns}x{rows}) in std140 block layout. See https://github.com/gfx-rs/wgpu/issues/4375")]
121 UnsupportedF16MatrixInStd140 { columns: u8, rows: u8 },
122 #[error("Variable already declared: {0}")]
124 VariableAlreadyDeclared(String),
125 #[error("{0}")]
127 SemanticError(Cow<'static, str>),
128 #[error("{0:?}")]
130 PreprocessorError(PreprocessorError),
131 #[error("Internal error: {0}")]
135 InternalError(&'static str),
136}
137
138impl From<ConstantEvaluatorError> for ErrorKind {
139 fn from(err: ConstantEvaluatorError) -> Self {
140 ErrorKind::SemanticError(err.to_string().into())
141 }
142}
143
144#[derive(Clone, Debug, Error)]
146#[error("{kind}")]
147#[cfg_attr(test, derive(PartialEq))]
148pub struct Error {
149 pub kind: ErrorKind,
151 pub meta: Span,
153}
154
155impl Error {
156 pub fn location(&self, source: &str) -> Option<SourceLocation> {
158 Some(self.meta.location(source))
159 }
160}
161
162#[derive(Clone, Debug)]
164#[cfg_attr(test, derive(PartialEq))]
165pub struct ParseErrors {
166 pub errors: Vec<Error>,
167}
168
169impl ParseErrors {
170 #[cfg(feature = "stderr")]
171 pub fn emit_to_writer(&self, writer: &mut impl ErrorWrite, source: &str) {
172 self.emit_to_writer_with_path(writer, source, "glsl");
173 }
174
175 #[cfg(feature = "stderr")]
176 pub fn emit_to_writer_with_path(&self, writer: &mut impl ErrorWrite, source: &str, path: &str) {
177 let path = path.to_string();
178 let files = SimpleFile::new(path, replace_control_chars(source));
179 let config = term::Config::default();
180
181 for err in &self.errors {
182 let diagnostic = Self::make_diagnostic(err);
183 crate::error::emit_to_writer(writer, &config, &files, &diagnostic)
184 .expect("cannot write error");
185 }
186 }
187
188 #[cfg(feature = "stderr")]
190 pub fn emit_to_stderr(&self, source: &str) {
191 self.emit_to_stderr_with_path(source, "glsl")
192 }
193
194 #[cfg(feature = "stderr")]
196 pub fn emit_to_stderr_with_path(&self, source: &str, path: &str) {
197 cfg_if::cfg_if! {
198 if #[cfg(feature = "termcolor")] {
199 let writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Auto);
200 self.emit_to_writer_with_path(&mut writer.lock(), source, path);
201 } else {
202 let writer = std::io::stderr();
203 self.emit_to_writer_with_path(&mut writer.lock(), source, path);
204 }
205 }
206 }
207
208 pub fn emit_to_string(&self, source: &str) -> String {
209 self.emit_to_string_with_path(source, "glsl")
210 }
211
212 pub fn emit_to_string_with_path(&self, source: &str, path: &str) -> String {
213 let path = path.to_string();
214 let files = SimpleFile::new(path, replace_control_chars(source));
215 let config = term::Config::default();
216
217 let mut writer = crate::error::DiagnosticBuffer::new();
218 for err in &self.errors {
219 let diagnostic = Self::make_diagnostic(err);
220 writer
221 .emit_to_self(&config, &files, &diagnostic)
222 .expect("cannot write error");
223 }
224 writer.into_string()
225 }
226
227 fn make_diagnostic(err: &Error) -> Diagnostic<()> {
228 let mut diagnostic = Diagnostic::error().with_message(err.kind.to_string());
229 if let Some(range) = err.meta.to_range() {
230 diagnostic = diagnostic.with_labels(vec![Label::primary((), range)]);
231 }
232 diagnostic
233 }
234}
235
236impl core::fmt::Display for ParseErrors {
237 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
238 self.errors.iter().try_for_each(|e| write!(f, "{e:?}"))
239 }
240}
241
242impl core::error::Error for ParseErrors {}
243
244impl From<Vec<Error>> for ParseErrors {
245 fn from(errors: Vec<Error>) -> Self {
246 Self { errors }
247 }
248}