cairo_lang_parser/
diagnostic.rs

1use cairo_lang_diagnostics::DiagnosticEntry;
2use cairo_lang_filesystem::db::FilesGroup;
3use cairo_lang_filesystem::ids::FileId;
4use cairo_lang_filesystem::span::TextSpan;
5use cairo_lang_syntax::node::kind::SyntaxKind;
6use smol_str::SmolStr;
7
8#[derive(Clone, Debug, Eq, Hash, PartialEq)]
9pub struct ParserDiagnostic {
10    pub file_id: FileId,
11    pub span: TextSpan,
12    pub kind: ParserDiagnosticKind,
13}
14impl ParserDiagnostic {
15    /// Converts a `SyntaxKind` to its corresponding operator string.
16    fn kind_to_string(&self, kind: SyntaxKind) -> String {
17        format!(
18            "'{}'",
19            match kind {
20                SyntaxKind::TerminalAnd => "&",
21                SyntaxKind::TerminalAndAnd => "&&",
22                SyntaxKind::TerminalArrow => "->",
23                SyntaxKind::TerminalAs => "as",
24                SyntaxKind::TerminalAt => "@",
25                SyntaxKind::TerminalBitNot => "~",
26                SyntaxKind::TerminalBreak => "break",
27                SyntaxKind::TerminalColon => ":",
28                SyntaxKind::TerminalColonColon => "::",
29                SyntaxKind::TerminalComma => ",",
30                SyntaxKind::TerminalConst => "const",
31                SyntaxKind::TerminalContinue => "continue",
32                SyntaxKind::TerminalDiv => "/",
33                SyntaxKind::TerminalDivEq => "/=",
34                SyntaxKind::TerminalDot => ".",
35                SyntaxKind::TerminalDotDot => "..",
36                SyntaxKind::TerminalDotDotEq => "..=",
37                SyntaxKind::TerminalElse => "else",
38                SyntaxKind::TerminalEnum => "enum",
39                SyntaxKind::TerminalEq => "=",
40                SyntaxKind::TerminalEqEq => "==",
41                SyntaxKind::TerminalExtern => "extern",
42                SyntaxKind::TerminalFalse => "false",
43                SyntaxKind::TerminalFor => "for",
44                SyntaxKind::TerminalFunction => "fn",
45                SyntaxKind::TerminalGE => ">=",
46                SyntaxKind::TerminalGT => ">",
47                SyntaxKind::TerminalHash => "#",
48                SyntaxKind::TerminalIf => "if",
49                SyntaxKind::TerminalImpl => "impl",
50                SyntaxKind::TerminalImplicits => "implicits",
51                SyntaxKind::TerminalLBrace => "{",
52                SyntaxKind::TerminalLBrack => "[",
53                SyntaxKind::TerminalLE => "<=",
54                SyntaxKind::TerminalLParen => "(",
55                SyntaxKind::TerminalLT => "<",
56                SyntaxKind::TerminalLet => "let",
57                SyntaxKind::TerminalLoop => "loop",
58                SyntaxKind::TerminalMatch => "match",
59                SyntaxKind::TerminalMatchArrow => "=>",
60                SyntaxKind::TerminalMinus => "-",
61                SyntaxKind::TerminalMinusEq => "-=",
62                SyntaxKind::TerminalMod => "%",
63                SyntaxKind::TerminalModEq => "%=",
64                SyntaxKind::TerminalModule => "mod",
65                SyntaxKind::TerminalMul => "*",
66                SyntaxKind::TerminalMulEq => "*=",
67                SyntaxKind::TerminalMut => "mut",
68                SyntaxKind::TerminalNeq => "!=",
69                SyntaxKind::TerminalNoPanic => "nopanic",
70                SyntaxKind::TerminalNot => "!",
71                SyntaxKind::TerminalOf => "of",
72                SyntaxKind::TerminalOr => "|",
73                SyntaxKind::TerminalOrOr => "||",
74                SyntaxKind::TerminalPlus => "+",
75                SyntaxKind::TerminalPlusEq => "+=",
76                SyntaxKind::TerminalPub => "pub",
77                SyntaxKind::TerminalQuestionMark => "?",
78                SyntaxKind::TerminalRBrace => "}",
79                SyntaxKind::TerminalRBrack => "]",
80                SyntaxKind::TerminalRParen => ")",
81                SyntaxKind::TerminalRef => "ref",
82                SyntaxKind::TerminalReturn => "return",
83                SyntaxKind::TerminalSemicolon => ";",
84                SyntaxKind::TerminalStruct => "struct",
85                SyntaxKind::TerminalTrait => "trait",
86                SyntaxKind::TerminalTrue => "true",
87                SyntaxKind::TerminalType => "type",
88                SyntaxKind::TerminalUnderscore => "_",
89                SyntaxKind::TerminalUse => "use",
90                SyntaxKind::TerminalWhile => "while",
91                SyntaxKind::TerminalXor => "^",
92                _ => return format!("{:?}", kind),
93            }
94        )
95    }
96}
97#[derive(Clone, Debug, Eq, Hash, PartialEq)]
98pub enum ParserDiagnosticKind {
99    // TODO(spapini): Add tokens from the recovery set to the message.
100    SkippedElement { element_name: SmolStr },
101    MissingToken(SyntaxKind),
102    MissingExpression,
103    MissingPathSegment,
104    MissingTypeClause,
105    MissingTypeExpression,
106    MissingWrappedArgList,
107    MissingPattern,
108    ExpectedInToken,
109    ItemInlineMacroWithoutBang { identifier: SmolStr, bracket_type: SyntaxKind },
110    ReservedIdentifier { identifier: SmolStr },
111    UnderscoreNotAllowedAsIdentifier,
112    MissingLiteralSuffix,
113    InvalidNumericLiteralValue,
114    IllegalStringEscaping,
115    ShortStringMustBeAscii,
116    StringMustBeAscii,
117    UnterminatedShortString,
118    UnterminatedString,
119    VisibilityWithoutItem,
120    AttributesWithoutItem,
121    AttributesWithoutTraitItem,
122    AttributesWithoutImplItem,
123    AttributesWithoutStatement,
124    DisallowedTrailingSeparatorOr,
125    ConsecutiveMathOperators { first_op: SyntaxKind, second_op: SyntaxKind },
126}
127impl DiagnosticEntry for ParserDiagnostic {
128    type DbType = dyn FilesGroup;
129
130    fn format(&self, _db: &dyn FilesGroup) -> String {
131        match &self.kind {
132            ParserDiagnosticKind::SkippedElement { element_name } => {
133                format!("Skipped tokens. Expected: {element_name}.")
134            }
135            ParserDiagnosticKind::MissingToken(kind) => {
136                format!("Missing token {}.", self.kind_to_string(*kind))
137            }
138            ParserDiagnosticKind::MissingExpression => {
139                "Missing tokens. Expected an expression.".to_string()
140            }
141            ParserDiagnosticKind::MissingPathSegment => {
142                "Missing tokens. Expected a path segment.".to_string()
143            }
144            ParserDiagnosticKind::MissingTypeClause => {
145                "Unexpected token, expected ':' followed by a type.".to_string()
146            }
147            ParserDiagnosticKind::MissingTypeExpression => {
148                "Missing tokens. Expected a type expression.".to_string()
149            }
150            ParserDiagnosticKind::MissingWrappedArgList => "Missing tokens. Expected an argument \
151                                                            list wrapped in either parentheses, \
152                                                            brackets, or braces."
153                .to_string(),
154            ParserDiagnosticKind::MissingPattern => {
155                "Missing tokens. Expected a pattern.".to_string()
156            }
157            ParserDiagnosticKind::ExpectedInToken => {
158                "Missing identifier token, expected 'in'.".to_string()
159            }
160            ParserDiagnosticKind::ItemInlineMacroWithoutBang { identifier, bracket_type } => {
161                let (left, right) = match bracket_type {
162                    SyntaxKind::TerminalLParen => ("(", ")"),
163                    SyntaxKind::TerminalLBrack => ("[", "]"),
164                    SyntaxKind::TerminalLBrace => ("{", "}"),
165                    _ => ("", ""),
166                };
167                format!(
168                    "Expected a '!' after the identifier '{identifier}' to start an inline macro.
169Did you mean to write `{identifier}!{left}...{right}'?",
170                )
171            }
172            ParserDiagnosticKind::ReservedIdentifier { identifier } => {
173                format!("'{identifier}' is a reserved identifier.")
174            }
175            ParserDiagnosticKind::UnderscoreNotAllowedAsIdentifier => {
176                "An underscore ('_') is not allowed as an identifier in this context.".to_string()
177            }
178            ParserDiagnosticKind::MissingLiteralSuffix => "Missing literal suffix.".to_string(),
179            ParserDiagnosticKind::InvalidNumericLiteralValue => {
180                "Literal is not a valid number.".to_string()
181            }
182            ParserDiagnosticKind::IllegalStringEscaping => "Invalid string escaping.".to_string(),
183            ParserDiagnosticKind::ShortStringMustBeAscii => {
184                "Short string literals can only include ASCII characters.".into()
185            }
186            ParserDiagnosticKind::StringMustBeAscii => {
187                "String literals can only include ASCII characters.".into()
188            }
189            ParserDiagnosticKind::UnterminatedShortString => {
190                "Unterminated short string literal.".into()
191            }
192            ParserDiagnosticKind::UnterminatedString => "Unterminated string literal.".into(),
193            ParserDiagnosticKind::VisibilityWithoutItem => {
194                "Missing tokens. Expected an item after visibility.".to_string()
195            }
196            ParserDiagnosticKind::AttributesWithoutItem => {
197                "Missing tokens. Expected an item after attributes.".to_string()
198            }
199            ParserDiagnosticKind::AttributesWithoutTraitItem => {
200                "Missing tokens. Expected a trait item after attributes.".to_string()
201            }
202            ParserDiagnosticKind::AttributesWithoutImplItem => {
203                "Missing tokens. Expected an impl item after attributes.".to_string()
204            }
205            ParserDiagnosticKind::AttributesWithoutStatement => {
206                "Missing tokens. Expected a statement after attributes.".to_string()
207            }
208            ParserDiagnosticKind::DisallowedTrailingSeparatorOr => {
209                "A trailing `|` is not allowed in an or-pattern.".to_string()
210            }
211            ParserDiagnosticKind::ConsecutiveMathOperators { first_op, second_op } => {
212                format!(
213                    "Consecutive comparison operators are not allowed: {} followed by {}",
214                    self.kind_to_string(*first_op),
215                    self.kind_to_string(*second_op)
216                )
217            }
218        }
219    }
220
221    fn location(&self, _db: &dyn FilesGroup) -> cairo_lang_diagnostics::DiagnosticLocation {
222        cairo_lang_diagnostics::DiagnosticLocation { file_id: self.file_id, span: self.span }
223    }
224
225    fn is_same_kind(&self, other: &Self) -> bool {
226        other.kind == self.kind
227    }
228}