1use std::path::PathBuf;
2
3use derive_more::From;
4
5use lang_util::{position::NodeSpan, FileId, SmolStr, TextRange};
6
7use crate::{
8 exts::names::ExtNameAtom,
9 parser::{self, SyntaxKind},
10 types::{path::ParsedPath, type_names::TypeNameAtom},
11 util::TokenText,
12};
13
14use super::{
15 expand::ExpandLocation,
16 nodes::{self, Directive, ExtensionName},
17};
18
19mod send;
20pub use send::*;
21
22pub type ProcessingError = lang_util::located::Located<ProcessingErrorKind>;
23
24#[derive(Debug, Clone, PartialEq, Eq, From)]
25pub enum ProcessingErrorKind {
26 ExtraEndIf,
27 ExtraElse,
28 ExtraElif,
29 ProtectedDefine {
30 ident: SmolStr,
31 is_undef: bool,
32 },
33 ErrorDirective {
34 message: String,
35 },
36 UnterminatedMacroInvocation {
37 ident: SmolStr,
38 },
39 UnexpectedDirective {
40 ident: SmolStr,
41 node: SendNode,
42 },
43 MismatchedArguments {
44 ident: SmolStr,
45 expected: usize,
46 actual: usize,
47 },
48 IncludeNotSupported,
49 IncludeNotFound {
50 path: ParsedPath,
51 },
52 InvalidTokenPaste {
53 token: Option<SmolStr>,
54 },
55 CppStyleLineNotSupported,
56 DirectiveVersion(nodes::VersionError),
57 DirectiveExtension(nodes::ExtensionError),
58 DirectiveDefine(nodes::DefineError),
59 DirectiveIfDef(nodes::IfDefError),
60 #[from(ignore)]
61 DirectiveIfNDef(nodes::IfDefError),
62 DirectiveIf(nodes::IfError),
63 DirectiveElif(nodes::ElifError),
64 DirectiveElse(nodes::ElseError),
65 DirectiveEndIf(nodes::EndIfError),
66 #[from(ignore)]
67 DirectiveUndef(nodes::IfDefError),
68 DirectiveError(nodes::ErrorError),
69 DirectiveInclude(nodes::IncludeError),
70 DirectiveLine(nodes::LineError),
71 DirectivePragma(nodes::PragmaError),
72}
73
74impl std::error::Error for ProcessingErrorKind {}
75
76impl std::fmt::Display for ProcessingErrorKind {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 match self {
79 ProcessingErrorKind::ExtraEndIf => {
80 write!(f, "unmatched #endif")
81 }
82 ProcessingErrorKind::ExtraElse => {
83 write!(f, "unmatched #else")
84 }
85 ProcessingErrorKind::ExtraElif => {
86 write!(f, "unmatched #elif")
87 }
88 ProcessingErrorKind::ProtectedDefine { ident, is_undef } => {
89 let directive = if *is_undef { "undef" } else { "define" };
90
91 if ident.starts_with("GL_") {
92 write!(
93 f,
94 "'#{}' : names beginning with \"GL_\" can't be (un)defined: {}",
95 directive, ident
96 )
97 } else {
98 write!(
99 f,
100 "'#{}' : predefined names can't be (un)defined: {}",
101 directive, ident
102 )
103 }
104 }
105 ProcessingErrorKind::ErrorDirective { message } => {
106 write!(f, "'#error' : {}", message)
107 }
108 ProcessingErrorKind::UnterminatedMacroInvocation { ident } => {
109 write!(f, "'macro expansion' : end of input in macro {}", ident)
110 }
111 ProcessingErrorKind::UnexpectedDirective { ident, node } => {
112 write!(f, "'macro expansion' : unexpected directive while scanning for macro invocation {} argument list: \"{}\"", ident, node.text())
113 }
114 ProcessingErrorKind::MismatchedArguments {
115 ident,
116 expected,
117 actual,
118 } => {
119 write!(f, "'macro expansion' : wrong number of arguments in input of macro {} : expected {}, got {}", ident, expected, actual)
120 }
121 ProcessingErrorKind::IncludeNotSupported => {
122 write!(f, "'#include' : required extension not requested: GL_GOOGLE_include_directive or GL_ARB_shading_language_include")
123 }
124 ProcessingErrorKind::IncludeNotFound { path } => {
125 write!(f, "'#include' : could not find file for {}", path)
126 }
127 ProcessingErrorKind::InvalidTokenPaste { token } => {
128 if let Some(token) = token {
129 if token.ends_with(" ##") {
130 write!(f, "'##' : invalid use of paste operator")
131 } else {
132 write!(f, "'##' : invalid pasted token : {}", token)
133 }
134 } else {
135 write!(f, "'##' : invalid use of paste operator")
136 }
137 }
138 ProcessingErrorKind::CppStyleLineNotSupported => {
139 write!(f, "'#line' : required extension not requested: GL_GOOGLE_cpp_style_line_directive")
140 }
141 ProcessingErrorKind::DirectiveVersion(inner) => {
142 write!(f, "'#version' : {}", inner)
143 }
144 ProcessingErrorKind::DirectiveExtension(inner) => {
145 write!(f, "'#extension' : {}", inner)
146 }
147 ProcessingErrorKind::DirectiveDefine(inner) => {
148 write!(f, "'#define' : {}", inner)
149 }
150 ProcessingErrorKind::DirectiveIfDef(inner) => {
151 write!(f, "'#ifdef' : {}", inner)
152 }
153 ProcessingErrorKind::DirectiveIfNDef(inner) => {
154 write!(f, "'#ifndef' : {}", inner)
155 }
156 ProcessingErrorKind::DirectiveIf(inner) => {
157 write!(f, "'#if' : {}", inner)
158 }
159 ProcessingErrorKind::DirectiveElif(inner) => {
160 write!(f, "'#elif' : {}", inner)
161 }
162 ProcessingErrorKind::DirectiveElse(inner) => {
163 write!(f, "'#else' : {}", inner)
164 }
165 ProcessingErrorKind::DirectiveEndIf(inner) => {
166 write!(f, "'#endif' : {}", inner)
167 }
168 ProcessingErrorKind::DirectiveUndef(inner) => {
169 write!(f, "'#undef' : {}", inner)
170 }
171 ProcessingErrorKind::DirectiveError(inner) => {
172 write!(f, "'#error' : {}", inner)
173 }
174 ProcessingErrorKind::DirectiveInclude(inner) => {
175 write!(f, "'#include' : {}", inner)
176 }
177 ProcessingErrorKind::DirectiveLine(inner) => {
178 write!(f, "'#line' : {}", inner)
179 }
180 ProcessingErrorKind::DirectivePragma(inner) => {
181 write!(f, "'#pragma' : {}", inner)
182 }
183 }
184 }
185}
186
187pub type Error = lang_util::located::Located<ErrorKind>;
188
189#[derive(Debug, Clone, PartialEq, Eq, derive_more::From)]
190pub enum ErrorKind {
191 Parse(parser::ErrorKind),
192 Processing(ProcessingErrorKind),
193 WarnExtUse {
194 extension: ExtNameAtom,
195 name: Option<TypeNameAtom>,
196 raw_line: u32,
197 pos: NodeSpan,
198 },
199 UnsupportedExt {
200 extension: ExtensionName,
201 raw_line: u32,
202 pos: NodeSpan,
203 },
204}
205
206impl std::error::Error for ErrorKind {}
207
208impl std::fmt::Display for ErrorKind {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 match self {
211 ErrorKind::Parse(parse) => write!(f, "{}", parse),
212 ErrorKind::Processing(processing) => write!(f, "{}", processing),
213 ErrorKind::WarnExtUse { extension, .. } => write!(f, "warning use of '{}'", extension),
214 ErrorKind::UnsupportedExt { extension, .. } => {
215 write!(f, "extension not supported: {}", extension)
216 }
217 }
218 }
219}
220
221impl ErrorKind {
222 pub fn unsupported_ext(
223 extension: ExtensionName,
224 pos: NodeSpan,
225 location: &ExpandLocation,
226 ) -> Self {
227 let raw_line = location.offset_to_raw_line_and_col(pos.start().offset).0;
228 Self::UnsupportedExt {
229 extension,
230 raw_line,
231 pos,
232 }
233 }
234
235 pub fn warn_ext_use(
236 extension: ExtNameAtom,
237 name: Option<TypeNameAtom>,
238 pos: NodeSpan,
239 location: &ExpandLocation,
240 ) -> Self {
241 let raw_line = location.offset_to_raw_line_and_col(pos.start().offset).0;
242 Self::WarnExtUse {
243 extension,
244 name,
245 raw_line,
246 pos,
247 }
248 }
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, From)]
252pub enum DirectiveKind {
253 Empty(nodes::Empty),
254 Version(nodes::Version),
255 Extension(nodes::Extension),
256 Define(nodes::Define),
257 IfDef(nodes::IfDef),
258 IfNDef(nodes::IfNDef),
259 If(nodes::If),
260 Elif(nodes::Elif),
261 Else(nodes::Else),
262 EndIf(nodes::EndIf),
263 Undef(nodes::Undef),
264 Error(nodes::Error),
265 Include(nodes::Include),
266 Line(nodes::Line),
267 Pragma(nodes::Pragma),
268 Invalid(nodes::Invalid),
269}
270
271pub trait TokenLike: Clone {
272 fn kind(&self) -> SyntaxKind;
273 fn text(&self) -> TokenText;
274 fn text_range(&self) -> NodeSpan;
275}
276
277impl TokenLike for (parser::SyntaxToken, FileId) {
278 fn kind(&self) -> SyntaxKind {
279 self.0.kind()
280 }
281
282 fn text(&self) -> TokenText {
283 TokenText::raw(self.0.text())
284 }
285
286 fn text_range(&self) -> NodeSpan {
287 NodeSpan::new(self.1, self.0.text_range())
288 }
289}
290
291impl TokenLike for (&parser::SyntaxToken, FileId) {
292 fn kind(&self) -> SyntaxKind {
293 self.0.kind()
294 }
295
296 fn text(&self) -> TokenText {
297 TokenText::raw(self.0.text())
298 }
299
300 fn text_range(&self) -> NodeSpan {
301 NodeSpan::new(self.1, self.0.text_range())
302 }
303}
304
305#[derive(Clone, PartialEq, Eq)]
306pub struct OutputToken {
307 kind: SyntaxKind,
308 text: SmolStr,
309 source_range: NodeSpan,
310}
311
312impl OutputToken {
313 pub fn new(kind: SyntaxKind, text: TokenText, source_range: NodeSpan) -> Self {
314 Self {
315 kind,
316 text: text.into(),
317 source_range,
318 }
319 }
320
321 pub fn new_error(source_range: NodeSpan) -> Self {
322 Self {
323 kind: SyntaxKind::ERROR,
324 text: SmolStr::default(),
325 source_range,
326 }
327 }
328
329 pub fn from_token(value: &impl TokenLike) -> Self {
330 Self::new(value.kind(), value.text(), value.text_range())
331 }
332
333 pub fn kind(&self) -> SyntaxKind {
334 self.kind
335 }
336
337 pub fn text(&self) -> &str {
338 self.text.as_str()
339 }
340
341 pub fn text_range(&self) -> NodeSpan {
342 self.source_range
343 }
344}
345
346impl From<(parser::SyntaxToken, FileId)> for OutputToken {
347 fn from(value: (parser::SyntaxToken, FileId)) -> Self {
348 Self::from_token(&value)
349 }
350}
351
352impl From<(&parser::SyntaxToken, FileId)> for OutputToken {
353 fn from(value: (&parser::SyntaxToken, FileId)) -> Self {
354 Self::from_token(&value)
355 }
356}
357
358impl TokenLike for OutputToken {
359 fn kind(&self) -> SyntaxKind {
360 self.kind
361 }
362
363 fn text(&self) -> TokenText {
364 unsafe { TokenText::unescaped(self.text.as_str()) }
366 }
367
368 fn text_range(&self) -> NodeSpan {
369 self.source_range
370 }
371}
372
373impl std::fmt::Debug for OutputToken {
374 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
375 write!(f, "{:?}@{:?}", self.kind, self.source_range)?;
376
377 if self.text.len() < 25 {
378 return write!(f, " {:?}", self.text);
379 }
380
381 let text = self.text();
382 for idx in 21..25 {
383 if text.is_char_boundary(idx) {
384 let text = format!("{} ...", &text[..idx]);
385 return write!(f, " {:?}", text);
386 }
387 }
388
389 unreachable!()
390 }
391}
392
393#[derive(Debug, Clone, PartialEq, Eq)]
394pub struct EventDirective {
395 pub(crate) node: parser::SyntaxNode,
397 kind: DirectiveKind,
398 errors: Vec<Error>,
399 source_id: FileId,
400}
401
402impl EventDirective {
403 pub fn kind(&self) -> &DirectiveKind {
404 &self.kind
405 }
406
407 pub fn errors(&self) -> &[Error] {
408 &self.errors
409 }
410
411 pub fn text_range(&self) -> NodeSpan {
412 NodeSpan::new(self.source_id, self.node.text_range())
413 }
414
415 pub fn into_errors(self) -> Vec<Error> {
416 self.errors
417 }
418}
419
420impl std::fmt::Display for EventDirective {
421 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422 write!(f, "{}", self.node.text())
423 }
424}
425
426#[derive(Debug, PartialEq, Eq, From)]
427pub enum Event {
428 Error {
429 error: Error,
430 masked: bool,
431 },
432 EnterFile {
433 file_id: FileId,
434 path: PathBuf,
435 canonical_path: PathBuf,
436 },
437 Token {
438 token: OutputToken,
439 masked: bool,
440 },
441 Directive {
442 directive: EventDirective,
443 masked: bool,
444 },
445}
446
447impl Event {
448 pub fn enter_file(file_id: FileId) -> Self {
449 Self::EnterFile {
450 file_id,
451 path: Default::default(),
452 canonical_path: Default::default(),
453 }
454 }
455
456 pub fn token<T: TokenLike>(token: T, masked: bool) -> Self {
457 Self::Token {
458 token: OutputToken::from_token(&token),
459 masked,
460 }
461 }
462
463 pub fn directive<D: Into<DirectiveKind>>(d: Directive<D>, masked: bool) -> Self {
464 let (source_id, inner, node) = d.into_inner();
465 Self::Directive {
466 directive: EventDirective {
467 node,
468 kind: inner.into(),
469 errors: vec![],
470 source_id,
471 },
472 masked,
473 }
474 }
475
476 pub fn directive_errors<D: Into<DirectiveKind>>(
477 d: Directive<D>,
478 masked: bool,
479 errors: impl IntoIterator<Item = impl Into<ErrorKind>>,
480 location: &ExpandLocation,
481 ) -> Self {
482 let (source_id, inner, node) = d.into_inner();
483 let pos = node.text_range();
484
485 Self::Directive {
486 directive: EventDirective {
487 node,
488 kind: inner.into(),
489 errors: errors
490 .into_iter()
491 .map(|error| {
492 Error::builder()
493 .pos(pos)
494 .resolve_file(location)
495 .finish(error.into())
496 })
497 .collect(),
498 source_id,
499 },
500 masked,
501 }
502 }
503
504 pub fn directive_error<E: Into<ProcessingErrorKind>>(
505 (error, node): (E, parser::SyntaxNode),
506 location: &ExpandLocation,
507 masked: bool,
508 ) -> Self {
509 Self::error(error.into(), node.text_range(), location, masked)
510 }
511
512 pub fn error<T: Into<ErrorKind>>(
513 e: T,
514 pos: impl Into<TextRange>,
515 location: &ExpandLocation,
516 masked: bool,
517 ) -> Self {
518 Self::Error {
519 error: Error::builder()
520 .pos(pos)
521 .resolve_file(location)
522 .finish(e.into()),
523 masked,
524 }
525 }
526
527 pub fn map_error<T: Into<ErrorKind>>(e: lang_util::located::Located<T>, masked: bool) -> Self {
528 Self::Error {
529 error: e.map(Into::into),
530 masked,
531 }
532 }
533
534 pub fn is_token(&self) -> bool {
535 matches!(self, Event::Token { .. })
536 }
537
538 pub fn as_token(&self) -> Option<&OutputToken> {
539 if let Event::Token { token, .. } = self {
540 Some(token)
541 } else {
542 None
543 }
544 }
545
546 pub fn into_token(self) -> Option<OutputToken> {
547 if let Event::Token { token, .. } = self {
548 Some(token)
549 } else {
550 None
551 }
552 }
553}
554
555impl From<OutputToken> for Event {
556 fn from(token: OutputToken) -> Self {
557 Self::Token {
558 token,
559 masked: false,
560 }
561 }
562}
563
564#[cfg(test)]
565mod tests {
566 fn assert_send<T: Send>() {}
567
568 #[test]
569 fn test_error_send() {
570 assert_send::<super::SendEvent>();
571 assert_send::<super::Error>();
572 }
573}