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    /// A Css / Scss root declaration outside the css-in-js parse mode
93    /// (README "Acceptance"). Not emitted in Less.
94    TopLevelDeclaration,
95    /// CSS Syntax §5.5.6: a declaration value hit a top-level `{}` block,
96    /// so the statement is not a declaration;
97    /// the dispatcher re-consumes it as an `UnknownQualifiedRule`.
98    BlockInDeclarationValue,
99
100    // Spec parse errors that CSS Syntax recovers from at EOF / newline. The AST
101    // and recovery stay conformant; these are recorded in `recoverable_errors()`
102    // so downstream consumers can tell recovered-from-broken input apart from
103    // valid CSS. See <https://drafts.csswg.org/css-syntax-3/>.
104    /// A `{}`-block left open at end of file (§5.4.4); `UnterminatedString`
105    /// (already defined) covers the EOF-terminated string (§4.3.5).
106    EofInBlock,
107    /// A string terminated by a newline instead of its quote — a
108    /// `<bad-string-token>` (§4.3.5).
109    BadString,
110    /// A `(`/`[` group left open at end of file.
111    UnclosedParen,
112}
113
114impl Display for ErrorKind {
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        match self {
117            Self::Unexpected(expected, actual) => {
118                write!(f, "expect token `{expected}`, but found `{actual}`")
119            }
120            Self::ExpectOneOf(expected, actual) => {
121                if let [init @ .., last] = &expected[..] {
122                    let joined = init
123                        .iter()
124                        .map(|token| format!("`{token}`"))
125                        .collect::<Vec<_>>()
126                        .join(", ");
127                    write!(f, "expect one of {joined} or `{last}`, but found `{actual}`",)
128                } else {
129                    panic!("the number of expected tokens must be at least 2")
130                }
131            }
132
133            Self::UnknownToken => write!(f, "unknown token"),
134            Self::InvalidNumber => write!(f, "invalid number"),
135            Self::InvalidEscape => write!(f, "invalid escape"),
136            Self::InvalidHash => write!(f, "invalid hash token"),
137            Self::ExpectRightBraceForLessVar => write!(f, "`}}` for Less variable is expected"),
138            Self::UnexpectedLinebreak => write!(f, "unexpected linebreak"),
139            Self::UnexpectedEof => write!(f, "unexpected end of file"),
140            Self::UnterminatedString => write!(f, "unterminated string"),
141
142            Self::ExpectRule => write!(f, "CSS rule is expected"),
143            Self::UnexpectedWhitespace => write!(f, "unexpected whitespace"),
144            Self::UnexpectedWhitespaceOrComments => write!(f, "unexpected whitespace or comments"),
145            Self::ExpectSimpleSelector => write!(f, "simple selector is expected"),
146            Self::ExpectTypeSelector => write!(f, "type selector is expected"),
147            Self::ExpectIdSelector => write!(f, "ID selector is expected"),
148            Self::ExpectWqName => write!(f, "WqName is expected"),
149            Self::ExpectAttributeSelectorMatcher => {
150                write!(f, "attribute selector matcher is expected")
151            }
152            Self::ExpectAttributeSelectorValue => write!(f, "attribute selector value is expected"),
153            Self::ExpectComponentValue => write!(f, "component value is expected"),
154            Self::ExpectSassExpression => write!(f, "Sass expression is expected"),
155            Self::ExpectDedentOrEof => write!(f, "dedentation or end of file is expected"),
156            Self::ExpectString => write!(f, "string is expected"),
157            Self::ExpectUrl => write!(f, "URL is expected"),
158            Self::InvalidUrl => write!(f, "invalid URL"),
159            Self::UnexpectedTemplateInCss => write!(f, "template isn't allowed in CSS"),
160            Self::ExpectMediaFeatureComparison => write!(f, "media feature comparison is expected"),
161            Self::ExpectMediaAnd => write!(f, "media query `and` is expected"),
162            Self::ExpectMediaOr => write!(f, "media query `or` is expected"),
163            Self::ExpectMediaNot => write!(f, "media query `not` is expected"),
164            Self::ExpectContainerConditionAnd => write!(f, "container condition `and` is expected"),
165            Self::ExpectContainerConditionOr => write!(f, "container condition `or` is expected"),
166            Self::ExpectContainerConditionNot => write!(f, "container condition `not` is expected"),
167            Self::ExpectStyleConditionAnd => write!(f, "style condition `and` is expected"),
168            Self::ExpectStyleConditionOr => write!(f, "style condition `or` is expected"),
169            Self::ExpectStyleConditionNot => write!(f, "style condition `not` is expected"),
170            Self::ExpectStyleQuery => write!(f, "style query is expected"),
171            Self::ExpectSassKeyword(keyword) => write!(f, "Sass keyword `{keyword}` is expected"),
172            Self::InvalidAnPlusB => write!(f, "invalid An+B syntax"),
173            Self::ExpectInteger => write!(f, "an integer is expected"),
174            Self::ExpectUnsignedInteger => write!(f, "unsigned integer is expected"),
175            Self::ExpectImportantAnnotation => write!(f, "`!important` is expected"),
176            Self::ExpectSassUseNamespace => {
177                write!(f, "`*` or ident for Sass namespace is expected")
178            }
179            Self::InvalidUnicodeRange => write!(f, "invalid unicode range"),
180            Self::UnexpectedSassElseAtRule => write!(f, "Sass `@else` at-rule is disallowed here"),
181            Self::ExpectSassAtRootWithOrWithout => {
182                write!(f, "Sass identifier `with` or `without` is expected")
183            }
184            Self::ExpectNthOf => write!(f, "`of` is expected"),
185            Self::ExpectKeyframeBlock => write!(f, "keyframe block is expected"),
186            Self::MixedDelimiterKindInLessMixin => write!(
187                f,
188                "using both `;` and `,` as delimiters in the same Less mixin is disallowed"
189            ),
190            Self::ExpectLessKeyword(keyword) => write!(f, "Less keyword `{keyword}` is expected"),
191            Self::ExpectLessExtendRule => write!(f, "Less extend rule is expected"),
192            Self::ExpectScopeTo => write!(f, "keyword `to` of `@scope` at-rule is expected"),
193
194            // An internal rollback marker for `try_parse`; whoever surfaces a
195            // rolled-back error concretizes it first. Print a generic message
196            // rather than panic in case one ever leaks.
197            Self::TryParseError => write!(f, "syntax error"),
198            Self::CSSWideKeywordDisallowed => {
199                write!(f, "using CSS wide keyword as identifier is disallowed")
200            }
201            Self::MediaTypeKeywordDisallowed(keyword) => {
202                write!(f, "keyword `{keyword}` as media type is disallowed")
203            }
204            Self::UnknownKeyframeSelectorIdent => write!(f, "unknown keyframe selector"),
205            Self::InvalidRatioDenominator => write!(f, "ratio denominator is invalid"),
206            Self::ExpectMediaFeatureName => write!(f, "media feature name is expected"),
207            Self::ExpectDashedIdent => write!(f, "dashed identifier is expected"),
208            Self::InvalidIdSelectorName => write!(f, "invalid ID selector name"),
209            Self::ReturnOutsideFunction => write!(f, "`@return` is disallowed outside function"),
210            Self::MaxCodePointExceeded => {
211                write!(f, "unicode range end value exceeds max allowed code point")
212            }
213            Self::UnicodeRangeStartGreaterThanEnd => {
214                write!(f, "unicode range start value can't greater than end value")
215            }
216            Self::UnexpectedNthMatcher => {
217                write!(f, "elements matcher is allowed in `:nth-child` and `:nth-last-child` only")
218            }
219            Self::InvalidSassFlagName(flag) => write!(f, "invalid Sass flag name `{flag}`"),
220            Self::UnexpectedSassFlag(flag) => write!(f, "Sass flag `!{flag}` is disallowed"),
221            Self::DuplicatedSassFlag(flag) => write!(f, "duplicated Sass flag `!{flag}`"),
222            Self::LessGuardOnMultipleComplexSelectors => {
223                write!(f, "Less guards are only allowed on a single complex selector")
224            }
225            Self::UnexpectedLessMixinCall => write!(f, "Less mixin call is disallowed"),
226            Self::UnexpectedSimpleBlock => write!(f, "simple block is disallowed"),
227            Self::TopLevelDeclaration => write!(f, "declaration at top level is disallowed"),
228            Self::BlockInDeclarationValue => {
229                write!(f, "a top-level `{{}}` block is disallowed in a declaration value")
230            }
231
232            Self::EofInBlock => write!(f, "unclosed block before end of file"),
233            Self::BadString => write!(f, "unterminated string before line break"),
234            Self::UnclosedParen => write!(f, "unclosed parenthesis before end of file"),
235        }
236    }
237}
238
239pub type PResult<T> = Result<T, Error>;