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;
15use crate::{error::replace_control_chars, SourceLocation};
16use crate::{error::ErrorWrite, proc::ConstantEvaluatorError, Span};
17
18fn join_with_comma(list: &[ExpectedToken]) -> String {
19 let mut string = "".to_string();
20 for (i, val) in list.iter().enumerate() {
21 string.push_str(&val.to_string());
22 match i {
23 i if i == list.len() - 1 => {}
24 i if i == list.len() - 2 => string.push_str(" or "),
25 _ => string.push_str(", "),
26 }
27 }
28 string
29}
30
31#[derive(Clone, Debug, PartialEq)]
33pub enum ExpectedToken {
34 Token(TokenValue),
36 TypeName,
38 Identifier,
40 IntLiteral,
42 FloatLiteral,
44 BoolLiteral,
46 Eof,
48}
49impl From<TokenValue> for ExpectedToken {
50 fn from(token: TokenValue) -> Self {
51 ExpectedToken::Token(token)
52 }
53}
54impl core::fmt::Display for ExpectedToken {
55 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56 match *self {
57 ExpectedToken::Token(ref token) => write!(f, "{token:?}"),
58 ExpectedToken::TypeName => write!(f, "a type"),
59 ExpectedToken::Identifier => write!(f, "identifier"),
60 ExpectedToken::IntLiteral => write!(f, "integer literal"),
61 ExpectedToken::FloatLiteral => write!(f, "float literal"),
62 ExpectedToken::BoolLiteral => write!(f, "bool literal"),
63 ExpectedToken::Eof => write!(f, "end of file"),
64 }
65 }
66}
67
68#[derive(Clone, Debug, Error)]
70#[cfg_attr(test, derive(PartialEq))]
71pub enum ErrorKind {
72 #[error("Unexpected end of file")]
74 EndOfFile,
75 #[error("Invalid profile: {0}")]
77 InvalidProfile(String),
78 #[error("Invalid version: {0}")]
80 InvalidVersion(u64),
81 #[error("Expected {expected_tokens}, found {found_token:?}", found_token = .0, expected_tokens = join_with_comma(.1))]
85 InvalidToken(TokenValue, Vec<ExpectedToken>),
86 #[error("Not implemented: {0}")]
91 NotImplemented(&'static str),
92 #[error("Unknown variable: {0}")]
94 UnknownVariable(String),
95 #[error("Unknown type: {0}")]
97 UnknownType(String),
98 #[error("Unknown field: {0}")]
100 UnknownField(String),
101 #[error("Unknown layout qualifier: {0}")]
107 UnknownLayoutQualifier(String),
108 #[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")]
113 UnsupportedMatrixWithTwoRowsInStd140 { columns: u8 },
114 #[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")]
119 UnsupportedF16MatrixInStd140 { columns: u8, rows: u8 },
120 #[error("Variable already declared: {0}")]
122 VariableAlreadyDeclared(String),
123 #[error("{0}")]
125 SemanticError(Cow<'static, str>),
126 #[error("{0:?}")]
128 PreprocessorError(PreprocessorError),
129 #[error("Internal error: {0}")]
133 InternalError(&'static str),
134}
135
136impl From<ConstantEvaluatorError> for ErrorKind {
137 fn from(err: ConstantEvaluatorError) -> Self {
138 ErrorKind::SemanticError(err.to_string().into())
139 }
140}
141
142#[derive(Clone, Debug, Error)]
144#[error("{kind}")]
145#[cfg_attr(test, derive(PartialEq))]
146pub struct Error {
147 pub kind: ErrorKind,
149 pub meta: Span,
151}
152
153impl Error {
154 pub fn location(&self, source: &str) -> Option<SourceLocation> {
156 Some(self.meta.location(source))
157 }
158}
159
160#[derive(Clone, Debug)]
162#[cfg_attr(test, derive(PartialEq))]
163pub struct ParseErrors {
164 pub errors: Vec<Error>,
165}
166
167impl ParseErrors {
168 pub fn emit_to_writer(&self, writer: &mut impl ErrorWrite, source: &str) {
169 self.emit_to_writer_with_path(writer, source, "glsl");
170 }
171
172 pub fn emit_to_writer_with_path(&self, writer: &mut impl ErrorWrite, source: &str, path: &str) {
173 let path = path.to_string();
174 let files = SimpleFile::new(path, replace_control_chars(source));
175 let config = term::Config::default();
176
177 for err in &self.errors {
178 let mut diagnostic = Diagnostic::error().with_message(err.kind.to_string());
179
180 if let Some(range) = err.meta.to_range() {
181 diagnostic = diagnostic.with_labels(vec![Label::primary((), range)]);
182 }
183
184 term::emit(writer, &config, &files, &diagnostic).expect("cannot write error");
185 }
186 }
187
188 pub fn emit_to_string(&self, source: &str) -> String {
189 let mut writer = crate::error::DiagnosticBuffer::new();
190 self.emit_to_writer(writer.inner_mut(), source);
191 writer.into_string()
192 }
193}
194
195impl core::fmt::Display for ParseErrors {
196 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
197 self.errors.iter().try_for_each(|e| write!(f, "{e:?}"))
198 }
199}
200
201impl core::error::Error for ParseErrors {
202 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
203 None
204 }
205}
206
207impl From<Vec<Error>> for ParseErrors {
208 fn from(errors: Vec<Error>) -> Self {
209 Self { errors }
210 }
211}