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 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    // TODO(spapini): Add tokens from the recovery set to the message.
31    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}