glsl_lang_pp/processor/
event.rs

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        // SAFETY: OutputToken are always constructed from unescaped text
365        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    // TODO: Remove this pub(crate)
396    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}