cairo_lang_parser/
diagnostic.rs1use 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 operator_to_string(&self, kind: SyntaxKind) -> String {
17 match kind {
18 SyntaxKind::TerminalLT => "<".to_string(),
19 SyntaxKind::TerminalGT => ">".to_string(),
20 SyntaxKind::TerminalLE => "<=".to_string(),
21 SyntaxKind::TerminalGE => ">=".to_string(),
22 SyntaxKind::TerminalEqEq => "==".to_string(),
23 SyntaxKind::TerminalNeq => "!=".to_string(),
24 _ => format!("{:?}", kind),
25 }
26 }
27}
28#[derive(Clone, Debug, Eq, Hash, PartialEq)]
29pub enum ParserDiagnosticKind {
30 SkippedElement { element_name: SmolStr },
32 MissingToken(SyntaxKind),
33 MissingExpression,
34 MissingPathSegment,
35 MissingTypeClause,
36 MissingTypeExpression,
37 MissingWrappedArgList,
38 MissingPattern,
39 ExpectedInToken,
40 ItemInlineMacroWithoutBang { identifier: SmolStr, bracket_type: SyntaxKind },
41 ReservedIdentifier { identifier: SmolStr },
42 UnderscoreNotAllowedAsIdentifier,
43 MissingLiteralSuffix,
44 InvalidNumericLiteralValue,
45 IllegalStringEscaping,
46 ShortStringMustBeAscii,
47 StringMustBeAscii,
48 UnterminatedShortString,
49 UnterminatedString,
50 VisibilityWithoutItem,
51 AttributesWithoutItem,
52 AttributesWithoutTraitItem,
53 AttributesWithoutImplItem,
54 AttributesWithoutStatement,
55 DisallowedTrailingSeparatorOr,
56 ConsecutiveMathOperators { first_op: SyntaxKind, second_op: SyntaxKind },
57}
58impl DiagnosticEntry for ParserDiagnostic {
59 type DbType = dyn FilesGroup;
60
61 fn format(&self, _db: &dyn FilesGroup) -> String {
62 match &self.kind {
63 ParserDiagnosticKind::SkippedElement { element_name } => {
64 format!("Skipped tokens. Expected: {element_name}.")
65 }
66 ParserDiagnosticKind::MissingToken(kind) => {
67 format!("Missing token {kind:?}.")
68 }
69 ParserDiagnosticKind::MissingExpression => {
70 "Missing tokens. Expected an expression.".to_string()
71 }
72 ParserDiagnosticKind::MissingPathSegment => {
73 "Missing tokens. Expected a path segment.".to_string()
74 }
75 ParserDiagnosticKind::MissingTypeClause => {
76 "Unexpected token, expected ':' followed by a type.".to_string()
77 }
78 ParserDiagnosticKind::MissingTypeExpression => {
79 "Missing tokens. Expected a type expression.".to_string()
80 }
81 ParserDiagnosticKind::MissingWrappedArgList => "Missing tokens. Expected an argument \
82 list wrapped in either parentheses, \
83 brackets, or braces."
84 .to_string(),
85 ParserDiagnosticKind::MissingPattern => {
86 "Missing tokens. Expected a pattern.".to_string()
87 }
88 ParserDiagnosticKind::ExpectedInToken => {
89 "Missing identifier token, expected 'in'.".to_string()
90 }
91 ParserDiagnosticKind::ItemInlineMacroWithoutBang { identifier, bracket_type } => {
92 let (left, right) = match bracket_type {
93 SyntaxKind::TerminalLParen => ("(", ")"),
94 SyntaxKind::TerminalLBrack => ("[", "]"),
95 SyntaxKind::TerminalLBrace => ("{", "}"),
96 _ => ("", ""),
97 };
98 format!(
99 "Expected a '!' after the identifier '{identifier}' to start an inline macro.
100Did you mean to write `{identifier}!{left}...{right}'?",
101 )
102 }
103 ParserDiagnosticKind::ReservedIdentifier { identifier } => {
104 format!("'{identifier}' is a reserved identifier.")
105 }
106 ParserDiagnosticKind::UnderscoreNotAllowedAsIdentifier => {
107 "An underscore ('_') is not allowed as an identifier in this context.".to_string()
108 }
109 ParserDiagnosticKind::MissingLiteralSuffix => "Missing literal suffix.".to_string(),
110 ParserDiagnosticKind::InvalidNumericLiteralValue => {
111 "Literal is not a valid number.".to_string()
112 }
113 ParserDiagnosticKind::IllegalStringEscaping => "Invalid string escaping.".to_string(),
114 ParserDiagnosticKind::ShortStringMustBeAscii => {
115 "Short string literals can only include ASCII characters.".into()
116 }
117 ParserDiagnosticKind::StringMustBeAscii => {
118 "String literals can only include ASCII characters.".into()
119 }
120 ParserDiagnosticKind::UnterminatedShortString => {
121 "Unterminated short string literal.".into()
122 }
123 ParserDiagnosticKind::UnterminatedString => "Unterminated string literal.".into(),
124 ParserDiagnosticKind::VisibilityWithoutItem => {
125 "Missing tokens. Expected an item after visibility.".to_string()
126 }
127 ParserDiagnosticKind::AttributesWithoutItem => {
128 "Missing tokens. Expected an item after attributes.".to_string()
129 }
130 ParserDiagnosticKind::AttributesWithoutTraitItem => {
131 "Missing tokens. Expected a trait item after attributes.".to_string()
132 }
133 ParserDiagnosticKind::AttributesWithoutImplItem => {
134 "Missing tokens. Expected an impl item after attributes.".to_string()
135 }
136 ParserDiagnosticKind::AttributesWithoutStatement => {
137 "Missing tokens. Expected a statement after attributes.".to_string()
138 }
139 ParserDiagnosticKind::DisallowedTrailingSeparatorOr => {
140 "A trailing `|` is not allowed in an or-pattern.".to_string()
141 }
142 ParserDiagnosticKind::ConsecutiveMathOperators { first_op, second_op } => {
143 format!(
144 "Consecutive comparison operators are not allowed: '{}' followed by '{}'",
145 self.operator_to_string(*first_op),
146 self.operator_to_string(*second_op)
147 )
148 }
149 }
150 }
151
152 fn location(&self, _db: &dyn FilesGroup) -> cairo_lang_diagnostics::DiagnosticLocation {
153 cairo_lang_diagnostics::DiagnosticLocation { file_id: self.file_id, span: self.span }
154 }
155
156 fn is_same_kind(&self, other: &Self) -> bool {
157 other.kind == self.kind
158 }
159}