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