1use cairo_lang_diagnostics::{DiagnosticEntry, error_code};
2use cairo_lang_filesystem::ids::{FileId, SpanInFile};
3use cairo_lang_filesystem::span::TextSpan;
4use cairo_lang_syntax::node::kind::SyntaxKind;
5use salsa::Database;
6
7#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
8pub struct ParserDiagnostic<'a> {
9 pub file_id: FileId<'a>,
10 pub span: TextSpan,
11 pub kind: ParserDiagnosticKind,
12}
13impl<'a> ParserDiagnostic<'a> {
14 fn kind_to_string(&self, kind: SyntaxKind) -> String {
16 format!(
17 "'{}'",
18 match kind {
19 SyntaxKind::TerminalAnd => "&",
20 SyntaxKind::TerminalAndAnd => "&&",
21 SyntaxKind::TerminalArrow => "->",
22 SyntaxKind::TerminalAs => "as",
23 SyntaxKind::TerminalAt => "@",
24 SyntaxKind::TerminalBitNot => "~",
25 SyntaxKind::TerminalBreak => "break",
26 SyntaxKind::TerminalColon => ":",
27 SyntaxKind::TerminalColonColon => "::",
28 SyntaxKind::TerminalComma => ",",
29 SyntaxKind::TerminalConst => "const",
30 SyntaxKind::TerminalContinue => "continue",
31 SyntaxKind::TerminalDiv => "/",
32 SyntaxKind::TerminalDivEq => "/=",
33 SyntaxKind::TerminalDot => ".",
34 SyntaxKind::TerminalDotDot => "..",
35 SyntaxKind::TerminalDotDotEq => "..=",
36 SyntaxKind::TerminalElse => "else",
37 SyntaxKind::TerminalEnum => "enum",
38 SyntaxKind::TerminalEq => "=",
39 SyntaxKind::TerminalEqEq => "==",
40 SyntaxKind::TerminalExtern => "extern",
41 SyntaxKind::TerminalFalse => "false",
42 SyntaxKind::TerminalFor => "for",
43 SyntaxKind::TerminalFunction => "fn",
44 SyntaxKind::TerminalGE => ">=",
45 SyntaxKind::TerminalGT => ">",
46 SyntaxKind::TerminalHash => "#",
47 SyntaxKind::TerminalIf => "if",
48 SyntaxKind::TerminalImpl => "impl",
49 SyntaxKind::TerminalImplicits => "implicits",
50 SyntaxKind::TerminalLBrace => "{",
51 SyntaxKind::TerminalLBrack => "[",
52 SyntaxKind::TerminalLE => "<=",
53 SyntaxKind::TerminalLParen => "(",
54 SyntaxKind::TerminalLT => "<",
55 SyntaxKind::TerminalLet => "let",
56 SyntaxKind::TerminalLoop => "loop",
57 SyntaxKind::TerminalMatch => "match",
58 SyntaxKind::TerminalMatchArrow => "=>",
59 SyntaxKind::TerminalMinus => "-",
60 SyntaxKind::TerminalMinusEq => "-=",
61 SyntaxKind::TerminalMod => "%",
62 SyntaxKind::TerminalModEq => "%=",
63 SyntaxKind::TerminalModule => "mod",
64 SyntaxKind::TerminalMul => "*",
65 SyntaxKind::TerminalMulEq => "*=",
66 SyntaxKind::TerminalMut => "mut",
67 SyntaxKind::TerminalNeq => "!=",
68 SyntaxKind::TerminalNoPanic => "nopanic",
69 SyntaxKind::TerminalNot => "!",
70 SyntaxKind::TerminalOf => "of",
71 SyntaxKind::TerminalOr => "|",
72 SyntaxKind::TerminalOrOr => "||",
73 SyntaxKind::TerminalPlus => "+",
74 SyntaxKind::TerminalPlusEq => "+=",
75 SyntaxKind::TerminalPub => "pub",
76 SyntaxKind::TerminalQuestionMark => "?",
77 SyntaxKind::TerminalRBrace => "}",
78 SyntaxKind::TerminalRBrack => "]",
79 SyntaxKind::TerminalRParen => ")",
80 SyntaxKind::TerminalRef => "ref",
81 SyntaxKind::TerminalReturn => "return",
82 SyntaxKind::TerminalSemicolon => ";",
83 SyntaxKind::TerminalStruct => "struct",
84 SyntaxKind::TerminalTrait => "trait",
85 SyntaxKind::TerminalTrue => "true",
86 SyntaxKind::TerminalType => "type",
87 SyntaxKind::TerminalUnderscore => "_",
88 SyntaxKind::TerminalUse => "use",
89 SyntaxKind::TerminalWhile => "while",
90 SyntaxKind::TerminalXor => "^",
91 _ => return format!("{kind:?}"),
92 }
93 )
94 }
95}
96#[derive(Clone, Debug, Eq, Hash, PartialEq)]
97pub enum ParserDiagnosticKind {
98 SkippedElement { element_name: String },
100 MissingToken(SyntaxKind),
101 MissingExpression,
102 MissingPathSegment,
103 MissingTypeClause,
104 MissingTypeExpression,
105 MissingWrappedArgList,
106 MissingPattern,
107 MissingMacroRuleParamKind,
108 InvalidParamKindInMacroExpansion,
109 InvalidParamKindInMacroRule,
110 ExpectedInToken,
111 ItemInlineMacroWithoutBang { identifier: String, bracket_type: SyntaxKind },
112 ReservedIdentifier { identifier: String },
113 UnderscoreNotAllowedAsIdentifier,
114 MissingLiteralSuffix,
115 InvalidNumericLiteralValue,
116 IllegalStringEscaping,
117 ShortStringMustBeAscii,
118 StringMustBeAscii,
119 UnterminatedShortString,
120 UnterminatedString,
121 VisibilityWithoutItem,
122 AttributesWithoutItem,
123 AttributesWithoutTraitItem,
124 AttributesWithoutImplItem,
125 AttributesWithoutStatement,
126 DisallowedTrailingSeparatorOr,
127 ConsecutiveMathOperators { first_op: SyntaxKind, second_op: SyntaxKind },
128 ExpectedSemicolonOrBody,
129 LowPrecedenceOperatorInIfLet { op: SyntaxKind },
130 MissingMacroRepetitionOperator,
131}
132
133impl<'a> DiagnosticEntry<'a> for ParserDiagnostic<'a> {
134 fn format(&self, _db: &'a dyn Database) -> String {
135 match &self.kind {
136 ParserDiagnosticKind::InvalidParamKindInMacroExpansion => {
137 "Parameter kinds are not allowed in macro expansion.".to_string()
138 }
139 ParserDiagnosticKind::InvalidParamKindInMacroRule => {
140 "Macro parameter must have a kind.".to_string()
141 }
142 ParserDiagnosticKind::SkippedElement { element_name } => {
143 format!("Skipped tokens. Expected: {element_name}.")
144 }
145 ParserDiagnosticKind::MissingToken(kind) => {
146 format!("Missing token {}.", self.kind_to_string(*kind))
147 }
148 ParserDiagnosticKind::MissingExpression => {
149 "Missing tokens. Expected an expression.".to_string()
150 }
151 ParserDiagnosticKind::MissingPathSegment => {
152 "Missing tokens. Expected a path segment.".to_string()
153 }
154 ParserDiagnosticKind::MissingTypeClause => {
155 "Unexpected token, expected ':' followed by a type.".to_string()
156 }
157 ParserDiagnosticKind::MissingTypeExpression => {
158 "Missing tokens. Expected a type expression.".to_string()
159 }
160 ParserDiagnosticKind::MissingWrappedArgList => "Missing tokens. Expected an argument \
161 list wrapped in either parentheses, \
162 brackets, or braces."
163 .to_string(),
164 ParserDiagnosticKind::MissingPattern => {
165 "Missing tokens. Expected a pattern.".to_string()
166 }
167 ParserDiagnosticKind::MissingMacroRuleParamKind => {
168 "Missing tokens. Expected a macro rule parameter kind.".to_string()
169 }
170 ParserDiagnosticKind::ExpectedInToken => {
171 "Missing identifier token, expected 'in'.".to_string()
172 }
173 ParserDiagnosticKind::ItemInlineMacroWithoutBang { identifier, bracket_type } => {
174 let (left, right) = match bracket_type {
175 SyntaxKind::TerminalLParen => ("(", ")"),
176 SyntaxKind::TerminalLBrack => ("[", "]"),
177 SyntaxKind::TerminalLBrace => ("{", "}"),
178 _ => ("", ""),
179 };
180 format!(
181 "Expected a '!' after the identifier '{identifier}' to start an inline macro.
182Did you mean to write `{identifier}!{left}...{right}'?",
183 )
184 }
185 ParserDiagnosticKind::ReservedIdentifier { identifier } => {
186 format!("'{identifier}' is a reserved identifier.")
187 }
188 ParserDiagnosticKind::UnderscoreNotAllowedAsIdentifier => {
189 "An underscore ('_') is not allowed as an identifier in this context.".to_string()
190 }
191 ParserDiagnosticKind::MissingLiteralSuffix => "Missing literal suffix.".to_string(),
192 ParserDiagnosticKind::InvalidNumericLiteralValue => {
193 "Literal is not a valid number.".to_string()
194 }
195 ParserDiagnosticKind::IllegalStringEscaping => "Invalid string escaping.".to_string(),
196 ParserDiagnosticKind::ShortStringMustBeAscii => {
197 "Short string literals can only include ASCII characters.".into()
198 }
199 ParserDiagnosticKind::StringMustBeAscii => {
200 "String literals can only include ASCII characters.".into()
201 }
202 ParserDiagnosticKind::UnterminatedShortString => {
203 "Unterminated short string literal.".into()
204 }
205 ParserDiagnosticKind::UnterminatedString => "Unterminated string literal.".into(),
206 ParserDiagnosticKind::VisibilityWithoutItem => {
207 "Missing tokens. Expected an item after visibility.".to_string()
208 }
209 ParserDiagnosticKind::AttributesWithoutItem => {
210 "Missing tokens. Expected an item after attributes.".to_string()
211 }
212 ParserDiagnosticKind::AttributesWithoutTraitItem => {
213 "Missing tokens. Expected a trait item after attributes.".to_string()
214 }
215 ParserDiagnosticKind::AttributesWithoutImplItem => {
216 "Missing tokens. Expected an impl item after attributes.".to_string()
217 }
218 ParserDiagnosticKind::AttributesWithoutStatement => {
219 "Missing tokens. Expected a statement after attributes.".to_string()
220 }
221 ParserDiagnosticKind::DisallowedTrailingSeparatorOr => {
222 "A trailing `|` is not allowed in an or-pattern.".to_string()
223 }
224 ParserDiagnosticKind::ConsecutiveMathOperators { first_op, second_op } => {
225 format!(
226 "Consecutive comparison operators are not allowed: {} followed by {}",
227 self.kind_to_string(*first_op),
228 self.kind_to_string(*second_op)
229 )
230 }
231 ParserDiagnosticKind::ExpectedSemicolonOrBody => {
232 "Expected either ';' or '{' after module name. Use ';' for an external module \
233 declaration or '{' for a module with a body."
234 .to_string()
235 }
236 ParserDiagnosticKind::LowPrecedenceOperatorInIfLet { op } => {
237 format!(
238 "Operator {} is not allowed in let chains. Consider wrapping the expression \
239 in parentheses.",
240 self.kind_to_string(*op)
241 )
242 }
243 ParserDiagnosticKind::MissingMacroRepetitionOperator => {
244 "Missing macro repetition operator. Expected `?`, `+`, or `*` after `$(...)`."
245 .to_string()
246 }
247 }
248 }
249
250 fn location(&self, _db: &'a dyn Database) -> SpanInFile<'a> {
251 SpanInFile { file_id: self.file_id, span: self.span }
252 }
253
254 fn error_code(&self) -> Option<cairo_lang_diagnostics::ErrorCode> {
255 Some(match &self.kind {
256 ParserDiagnosticKind::SkippedElement { .. } => error_code!(E1000),
257 ParserDiagnosticKind::MissingToken(_) => error_code!(E1001),
258 ParserDiagnosticKind::MissingExpression => error_code!(E1002),
259 ParserDiagnosticKind::MissingPathSegment => error_code!(E1003),
260 ParserDiagnosticKind::MissingTypeClause => error_code!(E1004),
261 ParserDiagnosticKind::MissingTypeExpression => error_code!(E1005),
262 ParserDiagnosticKind::MissingWrappedArgList => error_code!(E1006),
263 ParserDiagnosticKind::MissingPattern => error_code!(E1007),
264 ParserDiagnosticKind::MissingMacroRuleParamKind => error_code!(E1008),
265 ParserDiagnosticKind::InvalidParamKindInMacroExpansion => error_code!(E1009),
266 ParserDiagnosticKind::InvalidParamKindInMacroRule => error_code!(E1010),
267 ParserDiagnosticKind::ExpectedInToken => error_code!(E1011),
268 ParserDiagnosticKind::ItemInlineMacroWithoutBang { .. } => error_code!(E1012),
269 ParserDiagnosticKind::ReservedIdentifier { .. } => error_code!(E1013),
270 ParserDiagnosticKind::UnderscoreNotAllowedAsIdentifier => error_code!(E1014),
271 ParserDiagnosticKind::MissingLiteralSuffix => error_code!(E1015),
272 ParserDiagnosticKind::InvalidNumericLiteralValue => error_code!(E1016),
273 ParserDiagnosticKind::IllegalStringEscaping => error_code!(E1017),
274 ParserDiagnosticKind::ShortStringMustBeAscii => error_code!(E1018),
275 ParserDiagnosticKind::StringMustBeAscii => error_code!(E1019),
276 ParserDiagnosticKind::UnterminatedShortString => error_code!(E1020),
277 ParserDiagnosticKind::UnterminatedString => error_code!(E1021),
278 ParserDiagnosticKind::VisibilityWithoutItem => error_code!(E1022),
279 ParserDiagnosticKind::AttributesWithoutItem => error_code!(E1023),
280 ParserDiagnosticKind::AttributesWithoutTraitItem => error_code!(E1024),
281 ParserDiagnosticKind::AttributesWithoutImplItem => error_code!(E1025),
282 ParserDiagnosticKind::AttributesWithoutStatement => error_code!(E1026),
283 ParserDiagnosticKind::DisallowedTrailingSeparatorOr => error_code!(E1027),
284 ParserDiagnosticKind::ConsecutiveMathOperators { .. } => error_code!(E1028),
285 ParserDiagnosticKind::ExpectedSemicolonOrBody => error_code!(E1029),
286 ParserDiagnosticKind::LowPrecedenceOperatorInIfLet { .. } => error_code!(E1030),
287 ParserDiagnosticKind::MissingMacroRepetitionOperator => error_code!(E1031),
288 })
289 }
290
291 fn is_same_kind(&self, other: &Self) -> bool {
292 other.kind == self.kind
293 }
294}