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    MissingMacroRuleParamKind,
109    InvalidParamKindInMacroExpansion,
110    InvalidParamKindInMacroRule,
111    ExpectedInToken,
112    ItemInlineMacroWithoutBang { identifier: SmolStr, bracket_type: SyntaxKind },
113    ReservedIdentifier { identifier: SmolStr },
114    UnderscoreNotAllowedAsIdentifier,
115    MissingLiteralSuffix,
116    InvalidNumericLiteralValue,
117    IllegalStringEscaping,
118    ShortStringMustBeAscii,
119    StringMustBeAscii,
120    UnterminatedShortString,
121    UnterminatedString,
122    VisibilityWithoutItem,
123    AttributesWithoutItem,
124    AttributesWithoutTraitItem,
125    AttributesWithoutImplItem,
126    AttributesWithoutStatement,
127    DisallowedTrailingSeparatorOr,
128    ConsecutiveMathOperators { first_op: SyntaxKind, second_op: SyntaxKind },
129    ExpectedSemicolonOrBody,
130    LowPrecedenceOperatorInIfLet { op: SyntaxKind },
131}
132impl DiagnosticEntry for ParserDiagnostic {
133    type DbType = dyn FilesGroup;
134
135    fn format(&self, _db: &dyn FilesGroup) -> String {
136        match &self.kind {
137            ParserDiagnosticKind::InvalidParamKindInMacroExpansion => {
138                "Parameter kinds are not allowed in macro expansion.".to_string()
139            }
140            ParserDiagnosticKind::InvalidParamKindInMacroRule => {
141                "Macro parameter must have a kind.".to_string()
142            }
143            ParserDiagnosticKind::SkippedElement { element_name } => {
144                format!("Skipped tokens. Expected: {element_name}.")
145            }
146            ParserDiagnosticKind::MissingToken(kind) => {
147                format!("Missing token {}.", self.kind_to_string(*kind))
148            }
149            ParserDiagnosticKind::MissingExpression => {
150                "Missing tokens. Expected an expression.".to_string()
151            }
152            ParserDiagnosticKind::MissingPathSegment => {
153                "Missing tokens. Expected a path segment.".to_string()
154            }
155            ParserDiagnosticKind::MissingTypeClause => {
156                "Unexpected token, expected ':' followed by a type.".to_string()
157            }
158            ParserDiagnosticKind::MissingTypeExpression => {
159                "Missing tokens. Expected a type expression.".to_string()
160            }
161            ParserDiagnosticKind::MissingWrappedArgList => "Missing tokens. Expected an argument \
162                                                            list wrapped in either parentheses, \
163                                                            brackets, or braces."
164                .to_string(),
165            ParserDiagnosticKind::MissingPattern => {
166                "Missing tokens. Expected a pattern.".to_string()
167            }
168            ParserDiagnosticKind::MissingMacroRuleParamKind => {
169                "Missing tokens. Expected a macro rule parameter kind.".to_string()
170            }
171            ParserDiagnosticKind::ExpectedInToken => {
172                "Missing identifier token, expected 'in'.".to_string()
173            }
174            ParserDiagnosticKind::ItemInlineMacroWithoutBang { identifier, bracket_type } => {
175                let (left, right) = match bracket_type {
176                    SyntaxKind::TerminalLParen => ("(", ")"),
177                    SyntaxKind::TerminalLBrack => ("[", "]"),
178                    SyntaxKind::TerminalLBrace => ("{", "}"),
179                    _ => ("", ""),
180                };
181                format!(
182                    "Expected a '!' after the identifier '{identifier}' to start an inline macro.
183Did you mean to write `{identifier}!{left}...{right}'?",
184                )
185            }
186            ParserDiagnosticKind::ReservedIdentifier { identifier } => {
187                format!("'{identifier}' is a reserved identifier.")
188            }
189            ParserDiagnosticKind::UnderscoreNotAllowedAsIdentifier => {
190                "An underscore ('_') is not allowed as an identifier in this context.".to_string()
191            }
192            ParserDiagnosticKind::MissingLiteralSuffix => "Missing literal suffix.".to_string(),
193            ParserDiagnosticKind::InvalidNumericLiteralValue => {
194                "Literal is not a valid number.".to_string()
195            }
196            ParserDiagnosticKind::IllegalStringEscaping => "Invalid string escaping.".to_string(),
197            ParserDiagnosticKind::ShortStringMustBeAscii => {
198                "Short string literals can only include ASCII characters.".into()
199            }
200            ParserDiagnosticKind::StringMustBeAscii => {
201                "String literals can only include ASCII characters.".into()
202            }
203            ParserDiagnosticKind::UnterminatedShortString => {
204                "Unterminated short string literal.".into()
205            }
206            ParserDiagnosticKind::UnterminatedString => "Unterminated string literal.".into(),
207            ParserDiagnosticKind::VisibilityWithoutItem => {
208                "Missing tokens. Expected an item after visibility.".to_string()
209            }
210            ParserDiagnosticKind::AttributesWithoutItem => {
211                "Missing tokens. Expected an item after attributes.".to_string()
212            }
213            ParserDiagnosticKind::AttributesWithoutTraitItem => {
214                "Missing tokens. Expected a trait item after attributes.".to_string()
215            }
216            ParserDiagnosticKind::AttributesWithoutImplItem => {
217                "Missing tokens. Expected an impl item after attributes.".to_string()
218            }
219            ParserDiagnosticKind::AttributesWithoutStatement => {
220                "Missing tokens. Expected a statement after attributes.".to_string()
221            }
222            ParserDiagnosticKind::DisallowedTrailingSeparatorOr => {
223                "A trailing `|` is not allowed in an or-pattern.".to_string()
224            }
225            ParserDiagnosticKind::ConsecutiveMathOperators { first_op, second_op } => {
226                format!(
227                    "Consecutive comparison operators are not allowed: {} followed by {}",
228                    self.kind_to_string(*first_op),
229                    self.kind_to_string(*second_op)
230                )
231            }
232            ParserDiagnosticKind::ExpectedSemicolonOrBody => {
233                "Expected either ';' or '{' after module name. Use ';' for an external module \
234                 declaration or '{' for a module with a body."
235                    .to_string()
236            }
237            ParserDiagnosticKind::LowPrecedenceOperatorInIfLet { op } => {
238                format!(
239                    "Operator {} is not allowed in let chains. Consider wrapping the expression \
240                     in parentheses.",
241                    self.kind_to_string(*op)
242                )
243            }
244        }
245    }
246
247    fn location(&self, _db: &dyn FilesGroup) -> cairo_lang_diagnostics::DiagnosticLocation {
248        cairo_lang_diagnostics::DiagnosticLocation { file_id: self.file_id, span: self.span }
249    }
250
251    fn is_same_kind(&self, other: &Self) -> bool {
252        other.kind == self.kind
253    }
254}