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 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 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}