fluent_syntax/parser/
errors.rs

1use std::ops::Range;
2use thiserror::Error;
3
4/// Error containing information about an error encountered by the Fluent Parser.
5///
6/// Errors in Fluent Parser are non-fatal, and the syntax has been
7/// designed to allow for strong recovery.
8///
9/// In result [`ParserError`] is designed to point at the slice of
10/// the input that is most likely to be a complete fragment from after
11/// the end of a valid entry, to the start of the next valid entry, with
12/// the invalid syntax in the middle.
13///
14///
15/// # Example
16///
17/// ```
18/// use fluent_syntax::parser;
19/// use fluent_syntax::ast;
20///
21/// let ftl = r#"
22/// key1 = Value 1
23///
24/// g@Rb@ge = #2y ds
25///
26/// key2 = Value 2
27///
28/// "#;
29///
30/// let (resource, errors) = parser::parse_runtime(ftl)
31///     .expect_err("Resource should contain errors.");
32///
33/// assert_eq!(
34///     errors,
35///     vec![
36///         parser::ParserError {
37///             pos: 18..19,
38///             slice: Some(17..35),
39///             kind: parser::ErrorKind::ExpectedToken('=')
40///         }
41///     ]
42/// );
43///
44/// assert_eq!(
45///     resource.body[0],
46///     ast::Entry::Message(
47///         ast::Message {
48///             id: ast::Identifier {
49///                 name: "key1"
50///             },
51///             value: Some(ast::Pattern {
52///                 elements: vec![
53///                     ast::PatternElement::TextElement {
54///                         value: "Value 1"
55///                     },
56///                 ]
57///             }),
58///             attributes: vec![],
59///             comment: None,
60///         }
61///     ),
62/// );
63///
64/// assert_eq!(
65///     resource.body[1],
66///     ast::Entry::Junk {
67///         content: "g@Rb@ge = #2y ds\n\n"
68///     }
69/// );
70///
71/// assert_eq!(
72///     resource.body[2],
73///     ast::Entry::Message(
74///         ast::Message {
75///             id: ast::Identifier {
76///                 name: "key2"
77///             },
78///             value: Some(ast::Pattern {
79///                 elements: vec![
80///                     ast::PatternElement::TextElement {
81///                         value: "Value 2"
82///                     },
83///                 ]
84///             }),
85///             attributes: vec![],
86///             comment: None,
87///         }
88///     ),
89/// );
90/// ```
91///
92/// The information contained in the `ParserError` should allow the tooling
93/// to display rich contextual annotations of the error slice, using
94/// crates such as `annotate-snippers`.
95#[derive(Error, Debug, PartialEq, Eq, Clone)]
96#[error("{}", self.kind)]
97pub struct ParserError {
98    /// Precise location of where the parser encountered the error.
99    pub pos: Range<usize>,
100    /// Slice of the input from the end of the last valid entry to the beginning
101    /// of the next valid entry with the invalid syntax in the middle.
102    pub slice: Option<Range<usize>>,
103    /// The type of the error that the parser encountered.
104    pub kind: ErrorKind,
105}
106
107macro_rules! error {
108    ($kind:expr, $start:expr) => {{
109        Err(ParserError {
110            pos: $start..$start + 1,
111            slice: None,
112            kind: $kind,
113        })
114    }};
115    ($kind:expr, $start:expr, $end:expr) => {{
116        Err(ParserError {
117            pos: $start..$end,
118            slice: None,
119            kind: $kind,
120        })
121    }};
122}
123
124/// Kind of an error associated with the [`ParserError`].
125#[derive(Error, Debug, PartialEq, Eq, Clone)]
126pub enum ErrorKind {
127    #[error("Expected a token starting with \"{0}\"")]
128    ExpectedToken(char),
129    #[error("Expected one of \"{range}\"")]
130    ExpectedCharRange { range: String },
131    #[error("Expected a message field for \"{entry_id}\"")]
132    ExpectedMessageField { entry_id: String },
133    #[error("Expected a term field for \"{entry_id}\"")]
134    ExpectedTermField { entry_id: String },
135    #[error("Callee is not allowed here")]
136    ForbiddenCallee,
137    #[error("The select expression must have a default variant")]
138    MissingDefaultVariant,
139    #[error("Expected a value")]
140    MissingValue,
141    #[error("A select expression can only have one default variant")]
142    MultipleDefaultVariants,
143    #[error("Message references can't be used as a selector")]
144    MessageReferenceAsSelector,
145    #[error("Term references can't be used as a selector")]
146    TermReferenceAsSelector,
147    #[error("Message attributes can't be used as a selector")]
148    MessageAttributeAsSelector,
149    #[error("Term attributes can't be used as a selector")]
150    TermAttributeAsPlaceable,
151    #[error("Unterminated string literal")]
152    UnterminatedStringLiteral,
153    #[error("Positional arguments must come before named arguments")]
154    PositionalArgumentFollowsNamed,
155    #[error("The \"{0}\" argument appears twice")]
156    DuplicatedNamedArgument(String),
157    #[error("Unknown escape sequence")]
158    UnknownEscapeSequence(String),
159    #[error("Invalid unicode escape sequence, \"{0}\"")]
160    InvalidUnicodeEscapeSequence(String),
161    #[error("Unbalanced closing brace")]
162    UnbalancedClosingBrace,
163    #[error("Expected an inline expression")]
164    ExpectedInlineExpression,
165    #[error("Expected a simple expression as selector")]
166    ExpectedSimpleExpressionAsSelector,
167    #[error("Expected a string or number literal")]
168    ExpectedLiteral,
169}