Skip to main content

oxc_css_parser/
error.rs

1//! Error management.
2
3use crate::pos::Span;
4#[cfg(feature = "serialize")]
5use serde::Serialize;
6use std::fmt::Display;
7
8#[derive(Clone, Debug)]
9#[cfg_attr(feature = "serialize", derive(Serialize))]
10#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
11pub struct Error {
12    pub kind: ErrorKind,
13    pub span: Span,
14}
15
16#[derive(Clone, Debug)]
17#[cfg_attr(feature = "serialize", derive(Serialize))]
18pub enum ErrorKind {
19    Unexpected(/* expected */ &'static str, /* actual */ &'static str),
20    ExpectOneOf(/* expected */ Vec<&'static str>, /* actual */ &'static str),
21
22    UnknownToken,
23    InvalidNumber,
24    InvalidEscape,
25    InvalidHash,
26    ExpectRightBraceForLessVar,
27    UnexpectedLinebreak,
28    UnexpectedEof,
29    UnterminatedString,
30
31    ExpectRule,
32    UnexpectedWhitespace,
33    UnexpectedWhitespaceOrComments,
34    ExpectSimpleSelector,
35    ExpectTypeSelector,
36    ExpectIdSelector,
37    ExpectWqName,
38    ExpectAttributeSelectorMatcher,
39    ExpectAttributeSelectorValue,
40    ExpectComponentValue,
41    ExpectSassExpression,
42    ExpectDedentOrEof,
43    ExpectString,
44    ExpectUrl,
45    InvalidUrl,
46    UnexpectedTemplateInCss,
47    ExpectMediaFeatureComparison,
48    ExpectMediaAnd,
49    ExpectMediaOr,
50    ExpectMediaNot,
51    ExpectContainerConditionAnd,
52    ExpectContainerConditionOr,
53    ExpectContainerConditionNot,
54    ExpectStyleConditionAnd,
55    ExpectStyleConditionOr,
56    ExpectStyleConditionNot,
57    ExpectStyleQuery,
58    ExpectSassKeyword(&'static str),
59    InvalidAnPlusB,
60    ExpectInteger,
61    ExpectUnsignedInteger,
62    ExpectImportantAnnotation,
63    ExpectSassUseNamespace,
64    InvalidUnicodeRange,
65    UnexpectedSassElseAtRule,
66    ExpectSassAtRootWithOrWithout,
67    ExpectNthOf,
68    ExpectKeyframeBlock,
69    MixedDelimiterKindInLessMixin,
70    ExpectLessKeyword(&'static str),
71    ExpectLessExtendRule,
72    ExpectScopeTo,
73
74    TryParseError,
75    CSSWideKeywordDisallowed,
76    MediaTypeKeywordDisallowed(String),
77    UnknownKeyframeSelectorIdent,
78    InvalidRatioDenominator,
79    ExpectMediaFeatureName,
80    ExpectDashedIdent,
81    InvalidIdSelectorName,
82    ReturnOutsideFunction,
83    MaxCodePointExceeded,
84    UnicodeRangeStartGreaterThanEnd,
85    UnexpectedNthMatcher,
86    InvalidSassFlagName(String),
87    UnexpectedSassFlag(&'static str),
88    DuplicatedSassFlag(&'static str),
89    LessGuardOnMultipleComplexSelectors,
90    UnexpectedLessMixinCall,
91    UnexpectedSimpleBlock,
92    TopLevelDeclaration,
93    /// CSS Syntax §5.5.6: a declaration value hit a top-level `{}` block,
94    /// so the statement is not a declaration;
95    /// the dispatcher re-consumes it as an `UnknownQualifiedRule`.
96    BlockInDeclarationValue,
97
98    // Spec parse errors that CSS Syntax recovers from at EOF / newline. The AST
99    // and recovery stay conformant; these are recorded in `recoverable_errors()`
100    // so downstream consumers can tell recovered-from-broken input apart from
101    // valid CSS. See <https://drafts.csswg.org/css-syntax-3/>.
102    /// A `{}`-block left open at end of file (§5.4.4); `UnterminatedString`
103    /// (already defined) covers the EOF-terminated string (§4.3.5).
104    EofInBlock,
105    /// A string terminated by a newline instead of its quote — a
106    /// `<bad-string-token>` (§4.3.5).
107    BadString,
108    /// A `(`/`[` group left open at end of file.
109    UnclosedParen,
110}
111
112impl Display for ErrorKind {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        match self {
115            Self::Unexpected(expected, actual) => {
116                write!(f, "expect token `{expected}`, but found `{actual}`")
117            }
118            Self::ExpectOneOf(expected, actual) => {
119                if let [init @ .., last] = &expected[..] {
120                    let joined = init
121                        .iter()
122                        .map(|token| format!("`{token}`"))
123                        .collect::<Vec<_>>()
124                        .join(", ");
125                    write!(f, "expect one of {joined} or `{last}`, but found `{actual}`",)
126                } else {
127                    panic!("the number of expected tokens must be at least 2")
128                }
129            }
130
131            Self::UnknownToken => write!(f, "unknown token"),
132            Self::InvalidNumber => write!(f, "invalid number"),
133            Self::InvalidEscape => write!(f, "invalid escape"),
134            Self::InvalidHash => write!(f, "invalid hash token"),
135            Self::ExpectRightBraceForLessVar => write!(f, "`}}` for Less variable is expected"),
136            Self::UnexpectedLinebreak => write!(f, "unexpected linebreak"),
137            Self::UnexpectedEof => write!(f, "unexpected end of file"),
138            Self::UnterminatedString => write!(f, "unterminated string"),
139
140            Self::ExpectRule => write!(f, "CSS rule is expected"),
141            Self::UnexpectedWhitespace => write!(f, "unexpected whitespace"),
142            Self::UnexpectedWhitespaceOrComments => write!(f, "unexpected whitespace or comments"),
143            Self::ExpectSimpleSelector => write!(f, "simple selector is expected"),
144            Self::ExpectTypeSelector => write!(f, "type selector is expected"),
145            Self::ExpectIdSelector => write!(f, "ID selector is expected"),
146            Self::ExpectWqName => write!(f, "WqName is expected"),
147            Self::ExpectAttributeSelectorMatcher => {
148                write!(f, "attribute selector matcher is expected")
149            }
150            Self::ExpectAttributeSelectorValue => write!(f, "attribute selector value is expected"),
151            Self::ExpectComponentValue => write!(f, "component value is expected"),
152            Self::ExpectSassExpression => write!(f, "Sass expression is expected"),
153            Self::ExpectDedentOrEof => write!(f, "dedentation or end of file is expected"),
154            Self::ExpectString => write!(f, "string is expected"),
155            Self::ExpectUrl => write!(f, "URL is expected"),
156            Self::InvalidUrl => write!(f, "invalid URL"),
157            Self::UnexpectedTemplateInCss => write!(f, "template isn't allowed in CSS"),
158            Self::ExpectMediaFeatureComparison => write!(f, "media feature comparison is expected"),
159            Self::ExpectMediaAnd => write!(f, "media query `and` is expected"),
160            Self::ExpectMediaOr => write!(f, "media query `or` is expected"),
161            Self::ExpectMediaNot => write!(f, "media query `not` is expected"),
162            Self::ExpectContainerConditionAnd => write!(f, "container condition `and` is expected"),
163            Self::ExpectContainerConditionOr => write!(f, "container condition `or` is expected"),
164            Self::ExpectContainerConditionNot => write!(f, "container condition `not` is expected"),
165            Self::ExpectStyleConditionAnd => write!(f, "style condition `and` is expected"),
166            Self::ExpectStyleConditionOr => write!(f, "style condition `or` is expected"),
167            Self::ExpectStyleConditionNot => write!(f, "style condition `not` is expected"),
168            Self::ExpectStyleQuery => write!(f, "style query is expected"),
169            Self::ExpectSassKeyword(keyword) => write!(f, "Sass keyword `{keyword}` is expected"),
170            Self::InvalidAnPlusB => write!(f, "invalid An+B syntax"),
171            Self::ExpectInteger => write!(f, "an integer is expected"),
172            Self::ExpectUnsignedInteger => write!(f, "unsigned integer is expected"),
173            Self::ExpectImportantAnnotation => write!(f, "`!important` is expected"),
174            Self::ExpectSassUseNamespace => {
175                write!(f, "`*` or ident for Sass namespace is expected")
176            }
177            Self::InvalidUnicodeRange => write!(f, "invalid unicode range"),
178            Self::UnexpectedSassElseAtRule => write!(f, "Sass `@else` at-rule is disallowed here"),
179            Self::ExpectSassAtRootWithOrWithout => {
180                write!(f, "Sass identifier `with` or `without` is expected")
181            }
182            Self::ExpectNthOf => write!(f, "`of` is expected"),
183            Self::ExpectKeyframeBlock => write!(f, "keyframe block is expected"),
184            Self::MixedDelimiterKindInLessMixin => write!(
185                f,
186                "using both `;` and `,` as delimiters in the same Less mixin is disallowed"
187            ),
188            Self::ExpectLessKeyword(keyword) => write!(f, "Less keyword `{keyword}` is expected"),
189            Self::ExpectLessExtendRule => write!(f, "Less extend rule is expected"),
190            Self::ExpectScopeTo => write!(f, "keyword `to` of `@scope` at-rule is expected"),
191
192            // An internal rollback marker for `try_parse`; whoever surfaces a
193            // rolled-back error concretizes it first. Print a generic message
194            // rather than panic in case one ever leaks.
195            Self::TryParseError => write!(f, "syntax error"),
196            Self::CSSWideKeywordDisallowed => {
197                write!(f, "using CSS wide keyword as identifier is disallowed")
198            }
199            Self::MediaTypeKeywordDisallowed(keyword) => {
200                write!(f, "keyword `{keyword}` as media type is disallowed")
201            }
202            Self::UnknownKeyframeSelectorIdent => write!(f, "unknown keyframe selector"),
203            Self::InvalidRatioDenominator => write!(f, "ratio denominator is invalid"),
204            Self::ExpectMediaFeatureName => write!(f, "media feature name is expected"),
205            Self::ExpectDashedIdent => write!(f, "dashed identifier is expected"),
206            Self::InvalidIdSelectorName => write!(f, "invalid ID selector name"),
207            Self::ReturnOutsideFunction => write!(f, "`@return` is disallowed outside function"),
208            Self::MaxCodePointExceeded => {
209                write!(f, "unicode range end value exceeds max allowed code point")
210            }
211            Self::UnicodeRangeStartGreaterThanEnd => {
212                write!(f, "unicode range start value can't greater than end value")
213            }
214            Self::UnexpectedNthMatcher => {
215                write!(f, "elements matcher is allowed in `:nth-child` and `:nth-last-child` only")
216            }
217            Self::InvalidSassFlagName(flag) => write!(f, "invalid Sass flag name `{flag}`"),
218            Self::UnexpectedSassFlag(flag) => write!(f, "Sass flag `!{flag}` is disallowed"),
219            Self::DuplicatedSassFlag(flag) => write!(f, "duplicated Sass flag `!{flag}`"),
220            Self::LessGuardOnMultipleComplexSelectors => {
221                write!(f, "Less guards are only allowed on a single complex selector")
222            }
223            Self::UnexpectedLessMixinCall => write!(f, "Less mixin call is disallowed"),
224            Self::UnexpectedSimpleBlock => write!(f, "simple block is disallowed"),
225            Self::TopLevelDeclaration => write!(f, "declaration at top level is disallowed"),
226            Self::BlockInDeclarationValue => {
227                write!(f, "a top-level `{{}}` block is disallowed in a declaration value")
228            }
229
230            Self::EofInBlock => write!(f, "unclosed block before end of file"),
231            Self::BadString => write!(f, "unterminated string before line break"),
232            Self::UnclosedParen => write!(f, "unclosed parenthesis before end of file"),
233        }
234    }
235}
236
237pub type PResult<T> = Result<T, Error>;