Skip to main content

oxc_css_parser/parser/
selector.rs

1use super::Parser;
2use crate::{
3    Parse, Syntax, arena_vec,
4    ast::*,
5    bump, eat,
6    error::{Error, ErrorKind, PResult},
7    expect, expect_without_ws_or_comments, peek,
8    pos::{Span, Spanned},
9    tokenizer::{Token, TokenWithSpan, token},
10    util,
11};
12
13// https://www.w3.org/TR/css-syntax-3/#the-anb-type
14impl<'a> Parse<'a> for AnPlusB {
15    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
16        match peek!(input) {
17            TokenWithSpan { token: Token::Dimension(..), .. } => {
18                let (token::Dimension { value, unit }, span) = expect!(input, Dimension);
19                let value_span = Span { start: span.start, end: span.start + value.raw.len() };
20                let unit_name = unit.name();
21                if unit_name.eq_ignore_ascii_case("n") {
22                    match &peek!(input).token {
23                        // syntax: <n-dimension> ['+' | '-'] <signless-integer>
24                        // examples: '1n + 1', '1n - 1', '1n+ 1'
25                        sign @ Token::Plus(..) | sign @ Token::Minus(..) => {
26                            let sign = if let Token::Plus(..) = sign { 1 } else { -1 };
27                            bump!(input);
28                            let (number, number_span) = expect_unsigned_int(input)?;
29                            let span = Span { start: span.start, end: number_span.end };
30                            Ok(AnPlusB {
31                                a: value
32                                    .try_into()
33                                    .map_err(|kind| Error { kind, span: value_span })?,
34                                b: sign
35                                    * i32::try_from(number)
36                                        .map_err(|kind| Error { kind, span: number_span })?,
37                                span,
38                            })
39                        }
40
41                        // syntax: <n-dimension> <signed-integer>
42                        // examples: '1n +1', '1n -1'
43                        Token::Number(..) => {
44                            let (number, number_span) = expect!(input, Number);
45                            let span = Span { start: span.start, end: number_span.end };
46                            Ok(AnPlusB {
47                                a: value
48                                    .try_into()
49                                    .map_err(|kind| Error { kind, span: value_span })?,
50                                b: number
51                                    .try_into()
52                                    .map_err(|kind| Error { kind, span: number_span })?,
53                                span,
54                            })
55                        }
56
57                        // syntax: <n-dimension>
58                        // examples: '1n'
59                        _ => Ok(AnPlusB {
60                            a: value.try_into().map_err(|kind| Error { kind, span: value_span })?,
61                            b: 0,
62                            span,
63                        }),
64                    }
65                } else if unit_name.eq_ignore_ascii_case("n-") {
66                    // syntax: <ndash-dimension> <signless-integer>
67                    // examples: '1n- 1'
68                    let (number, number_span) = expect_unsigned_int(input)?;
69                    let span = Span { start: span.start, end: number_span.end };
70                    Ok(AnPlusB {
71                        a: value.try_into().map_err(|kind| Error { kind, span: value_span })?,
72                        b: -i32::try_from(number)
73                            .map_err(|kind| Error { kind, span: number_span })?,
74                        span,
75                    })
76                } else if let Some(digits) = unit_name.strip_prefix("n-") {
77                    // syntax: <ndashdigit-dimension>
78                    // examples: '1n-1'
79                    if digits.chars().any(|c| !c.is_ascii_digit()) {
80                        return Err(Error {
81                            kind: ErrorKind::ExpectUnsignedInteger,
82                            span: Span { start: span.start + value.raw.len() + 2, end: span.end },
83                        });
84                    }
85                    let b = digits.parse::<i32>().map_err(|_| Error {
86                        kind: ErrorKind::ExpectInteger,
87                        span: Span { start: span.start + value.raw.len() + 2, end: span.end },
88                    })?;
89                    Ok(AnPlusB {
90                        a: value.try_into().map_err(|kind| Error { kind, span: value_span })?,
91                        b: -b,
92                        span,
93                    })
94                } else {
95                    Err(Error { kind: ErrorKind::InvalidAnPlusB, span })
96                }
97            }
98
99            TokenWithSpan { token: Token::Plus(..), .. } => {
100                let plus_span = bump!(input).span;
101                let (ident, ident_span) = expect_without_ws_or_comments!(input, Ident);
102                let ident_name = ident.name();
103                if ident_name.eq_ignore_ascii_case("n") {
104                    match &peek!(input).token {
105                        // syntax: +n ['+' | '-'] <signless-integer>
106                        // examples: '+n + 1', '+n - 1', '+n+ 1'
107                        sign @ Token::Plus(..) | sign @ Token::Minus(..) => {
108                            let sign = if let Token::Plus(..) = sign { 1 } else { -1 };
109                            bump!(input);
110                            let (number, number_span) = expect_unsigned_int(input)?;
111                            let span = Span { start: plus_span.start, end: number_span.end };
112                            Ok(AnPlusB {
113                                a: 1,
114                                b: sign
115                                    * i32::try_from(number)
116                                        .map_err(|kind| Error { kind, span: number_span })?,
117                                span,
118                            })
119                        }
120
121                        // syntax: +n <signed-integer>
122                        // examples: '+n +1', '+n -1'
123                        Token::Number(..) => {
124                            let (number, number_span) = expect!(input, Number);
125                            let span = Span { start: plus_span.start, end: number_span.end };
126                            Ok(AnPlusB {
127                                a: 1,
128                                b: number
129                                    .try_into()
130                                    .map_err(|kind| Error { kind, span: number_span })?,
131                                span,
132                            })
133                        }
134
135                        // syntax: +n
136                        _ => Ok(AnPlusB {
137                            a: 1,
138                            b: 0,
139                            span: Span { start: plus_span.start, end: ident_span.end },
140                        }),
141                    }
142                } else if ident_name.eq_ignore_ascii_case("n-") {
143                    // syntax: +n- <signless-integer>
144                    // examples: '+n- 1'
145                    let (number, number_span) = expect_unsigned_int(input)?;
146                    let span = Span { start: plus_span.start, end: number_span.end };
147                    Ok(AnPlusB {
148                        a: 1,
149                        b: -i32::try_from(number)
150                            .map_err(|kind| Error { kind, span: number_span })?,
151                        span,
152                    })
153                } else if let Some(digits) = ident_name.strip_prefix("n-") {
154                    // syntax: +<ndashdigit-ident>
155                    // examples: '+n-1'
156                    if digits.chars().any(|c| !c.is_ascii_digit()) {
157                        return Err(Error {
158                            kind: ErrorKind::ExpectUnsignedInteger,
159                            span: Span { start: ident_span.start + 2, end: ident_span.end },
160                        });
161                    }
162                    let b = digits.parse::<i32>().map_err(|_| Error {
163                        kind: ErrorKind::ExpectInteger,
164                        span: Span { start: ident_span.start + 2, end: ident_span.end },
165                    })?;
166                    Ok(AnPlusB {
167                        a: 1,
168                        b: -b,
169                        span: Span { start: plus_span.start, end: ident_span.end },
170                    })
171                } else {
172                    Err(Error {
173                        kind: ErrorKind::InvalidAnPlusB,
174                        span: Span { start: plus_span.start, end: ident_span.end },
175                    })
176                }
177            }
178
179            TokenWithSpan { token: Token::Ident(..), .. } => {
180                let (ident, ident_span) = expect!(input, Ident);
181                let ident_name = ident.name();
182                if ident_name.eq_ignore_ascii_case("n") {
183                    match &peek!(input).token {
184                        // syntax: n ['+' | '-'] <signless-integer>
185                        // examples: 'n + 1', 'n - 1', 'n+ 1'
186                        sign @ Token::Plus(..) | sign @ Token::Minus(..) => {
187                            let sign = if let Token::Plus(..) = sign { 1 } else { -1 };
188                            bump!(input);
189                            let (number, number_span) = expect_unsigned_int(input)?;
190                            let span = Span { start: ident_span.start, end: number_span.end };
191                            Ok(AnPlusB {
192                                a: 1,
193                                b: sign
194                                    * i32::try_from(number)
195                                        .map_err(|kind| Error { kind, span: number_span })?,
196                                span,
197                            })
198                        }
199
200                        // syntax: n <signed-integer>
201                        // examples: 'n +1', 'n -1'
202                        Token::Number(..) => {
203                            let (number, number_span) = expect!(input, Number);
204                            let span = Span { start: ident_span.start, end: number_span.end };
205                            Ok(AnPlusB {
206                                a: 1,
207                                b: number
208                                    .try_into()
209                                    .map_err(|kind| Error { kind, span: number_span })?,
210                                span,
211                            })
212                        }
213
214                        // syntax: n
215                        _ => Ok(AnPlusB { a: 1, b: 0, span: ident_span }),
216                    }
217                } else if ident_name.eq_ignore_ascii_case("n-") {
218                    // syntax: n- <signless-integer>
219                    // examples: 'n- 1'
220                    let (number, number_span) = expect_unsigned_int(input)?;
221                    let span = Span { start: ident_span.start, end: number_span.end };
222                    Ok(AnPlusB {
223                        a: 1,
224                        b: -i32::try_from(number)
225                            .map_err(|kind| Error { kind, span: number_span })?,
226                        span,
227                    })
228                } else if let Some(digits) = ident_name.strip_prefix("n-") {
229                    // syntax: <ndashdigit-ident>
230                    // examples: 'n-1'
231                    if digits.chars().any(|c| !c.is_ascii_digit()) {
232                        return Err(Error {
233                            kind: ErrorKind::ExpectUnsignedInteger,
234                            span: Span { start: ident_span.start + 2, end: ident_span.end },
235                        });
236                    }
237                    let b = digits.parse::<i32>().map_err(|_| Error {
238                        kind: ErrorKind::ExpectInteger,
239                        span: Span { start: ident_span.start + 2, end: ident_span.end },
240                    })?;
241                    Ok(AnPlusB { a: 1, b: -b, span: ident_span })
242                } else if ident_name.eq_ignore_ascii_case("-n") {
243                    match &peek!(input).token {
244                        // syntax: -n ['+' | '-'] <signless-integer>
245                        // examples: '-n + 1', '-n - 1', '-n+ 1'
246                        sign @ Token::Plus(..) | sign @ Token::Minus(..) => {
247                            let sign = if let Token::Plus(..) = sign { 1 } else { -1 };
248                            bump!(input);
249                            let (number, number_span) = expect_unsigned_int(input)?;
250                            let span = Span { start: ident_span.start, end: number_span.end };
251                            Ok(AnPlusB {
252                                a: -1,
253                                b: sign
254                                    * i32::try_from(number)
255                                        .map_err(|kind| Error { kind, span: number_span })?,
256                                span,
257                            })
258                        }
259
260                        // syntax: -n <signed-integer>
261                        // examples: '-n +1', '-n -1'
262                        Token::Number(..) => {
263                            let (number, number_span) = expect!(input, Number);
264                            let span = Span { start: ident_span.start, end: number_span.end };
265                            Ok(AnPlusB {
266                                a: -1,
267                                b: number
268                                    .try_into()
269                                    .map_err(|kind| Error { kind, span: number_span })?,
270                                span,
271                            })
272                        }
273
274                        // syntax: -n
275                        _ => Ok(AnPlusB { a: -1, b: 0, span: ident_span }),
276                    }
277                } else if ident_name.eq_ignore_ascii_case("-n-") {
278                    // syntax: -n- <signless-integer>
279                    // examples: '-n- 1'
280                    let (number, number_span) = expect_unsigned_int(input)?;
281                    let span = Span { start: ident_span.start, end: number_span.end };
282                    Ok(AnPlusB {
283                        a: -1,
284                        b: -i32::try_from(number)
285                            .map_err(|kind| Error { kind, span: number_span })?,
286                        span,
287                    })
288                } else if let Some(digits) = ident_name.strip_prefix("-n-") {
289                    // syntax: -n-<ndashdigit-ident>
290                    // examples: '-n-1'
291                    if digits.chars().any(|c| !c.is_ascii_digit()) {
292                        return Err(Error {
293                            kind: ErrorKind::ExpectUnsignedInteger,
294                            span: Span { start: ident_span.start + 3, end: ident_span.end },
295                        });
296                    }
297                    let b = digits.parse::<i32>().map_err(|_| Error {
298                        kind: ErrorKind::ExpectInteger,
299                        span: Span { start: ident_span.start + 3, end: ident_span.end },
300                    })?;
301                    Ok(AnPlusB { a: -1, b: -b, span: ident_span })
302                } else {
303                    Err(Error { kind: ErrorKind::InvalidAnPlusB, span: ident_span })
304                }
305            }
306
307            TokenWithSpan { span, .. } => {
308                Err(Error { kind: ErrorKind::InvalidAnPlusB, span: span.clone() })
309            }
310        }
311    }
312}
313
314impl<'a> Parse<'a> for AttributeSelector<'a> {
315    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
316        let start = expect!(input, LBracket).1.start;
317
318        let name = match peek!(input) {
319            TokenWithSpan {
320                token: Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..),
321                ..
322            } => {
323                let ident = input.parse::<InterpolableIdent>()?;
324                let ident_span = ident.span();
325                if let Some((_, bar_token_span)) = eat!(input, Bar) {
326                    let name = input.parse::<InterpolableIdent>()?;
327                    let name_span = name.span();
328
329                    let start = ident_span.start;
330                    let end = name_span.end;
331                    WqName {
332                        name,
333                        prefix: Some(NsPrefix {
334                            kind: Some(NsPrefixKind::Ident(ident)),
335                            span: Span { start, end: bar_token_span.end },
336                        }),
337                        span: Span { start, end },
338                    }
339                } else {
340                    let span = ident_span.clone();
341                    WqName { name: ident, prefix: None, span }
342                }
343            }
344            TokenWithSpan { token: Token::Asterisk(..), .. } => {
345                let asterisk_span = bump!(input).span;
346                let bar_token_span = expect!(input, Bar).1;
347                let name = input.parse::<InterpolableIdent>()?;
348
349                let start = asterisk_span.start;
350                let end = name.span().end;
351                WqName {
352                    name,
353                    prefix: Some(NsPrefix {
354                        kind: Some(NsPrefixKind::Universal(NsPrefixUniversal {
355                            span: asterisk_span,
356                        })),
357                        span: Span { start, end: bar_token_span.end },
358                    }),
359                    span: Span { start, end },
360                }
361            }
362            TokenWithSpan { token: Token::Bar(..), .. } => {
363                let bar_token_span = bump!(input).span;
364                let name = input.parse::<InterpolableIdent>()?;
365
366                let start = bar_token_span.start;
367                let end = name.span().end;
368                WqName {
369                    name,
370                    prefix: Some(NsPrefix {
371                        kind: None,
372                        span: Span { start, end: bar_token_span.end },
373                    }),
374                    span: Span { start, end },
375                }
376            }
377            TokenWithSpan { span, .. } => {
378                return Err(Error { kind: ErrorKind::ExpectWqName, span: span.clone() });
379            }
380        };
381
382        let matcher = match peek!(input) {
383            TokenWithSpan { token: Token::RBracket(..), .. } => None,
384            TokenWithSpan { token: Token::Equal(..), .. } => Some(AttributeSelectorMatcher {
385                kind: AttributeSelectorMatcherKind::Exact,
386                span: bump!(input).span,
387            }),
388            TokenWithSpan { token: Token::TildeEqual(..), .. } => Some(AttributeSelectorMatcher {
389                kind: AttributeSelectorMatcherKind::MatchWord,
390                span: bump!(input).span,
391            }),
392            TokenWithSpan { token: Token::BarEqual(..), .. } => Some(AttributeSelectorMatcher {
393                kind: AttributeSelectorMatcherKind::ExactOrPrefixThenHyphen,
394                span: bump!(input).span,
395            }),
396            TokenWithSpan { token: Token::CaretEqual(..), .. } => Some(AttributeSelectorMatcher {
397                kind: AttributeSelectorMatcherKind::Prefix,
398                span: bump!(input).span,
399            }),
400            TokenWithSpan { token: Token::DollarEqual(..), .. } => Some(AttributeSelectorMatcher {
401                kind: AttributeSelectorMatcherKind::Suffix,
402                span: bump!(input).span,
403            }),
404            TokenWithSpan { token: Token::AsteriskEqual(..), .. } => {
405                Some(AttributeSelectorMatcher {
406                    kind: AttributeSelectorMatcherKind::Substring,
407                    span: bump!(input).span,
408                })
409            }
410            TokenWithSpan { span, .. } => {
411                return Err(Error {
412                    kind: ErrorKind::ExpectAttributeSelectorMatcher,
413                    span: span.clone(),
414                });
415            }
416        };
417
418        let value = if matcher.is_some() {
419            match peek!(input) {
420                TokenWithSpan {
421                    token:
422                        Token::Ident(..)
423                        | Token::HashLBrace(..)
424                        | Token::AtLBraceVar(..)
425                        | Token::Placeholder(..),
426                    ..
427                } => Some(AttributeSelectorValue::Ident(input.parse()?)),
428                TokenWithSpan { token: Token::Str(..) | Token::StrTemplate(..), .. } => {
429                    Some(AttributeSelectorValue::Str(input.parse()?))
430                }
431                TokenWithSpan { token: Token::Percentage(..), .. }
432                    if input.syntax == Syntax::Less =>
433                {
434                    Some(AttributeSelectorValue::Percentage(input.parse()?))
435                }
436                TokenWithSpan { token: Token::Tilde(..), .. } if input.syntax == Syntax::Less => {
437                    Some(AttributeSelectorValue::LessEscapedStr(input.parse()?))
438                }
439                TokenWithSpan { token: Token::RBracket(..), span } => {
440                    input.recoverable_errors.push(Error {
441                        kind: ErrorKind::ExpectAttributeSelectorValue,
442                        span: span.clone(),
443                    });
444                    None
445                }
446                token_with_span => {
447                    return Err(Error {
448                        kind: ErrorKind::ExpectAttributeSelectorValue,
449                        span: token_with_span.span.clone(),
450                    });
451                }
452            }
453        } else {
454            None
455        };
456
457        let modifier = if value.is_some() {
458            match &peek!(input).token {
459                Token::Ident(..) | Token::HashLBrace(..) => {
460                    let ident = input.parse::<InterpolableIdent>()?;
461                    let span = ident.span().clone();
462                    Some(AttributeSelectorModifier { ident, span })
463                }
464                _ => None,
465            }
466        } else {
467            None
468        };
469
470        let end = expect!(input, RBracket).1.end;
471        Ok(AttributeSelector { name, matcher, value, modifier, span: Span { start, end } })
472    }
473}
474
475impl<'a> Parse<'a> for ClassSelector<'a> {
476    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
477        let (_, dot_span) = expect!(input, Dot);
478        let start = dot_span.start;
479        let end;
480        // Detect an adjacent placeholder without `peek!`: `peek!` skips
481        // whitespace and caches a token, which would both break the no-ws rule
482        // (the name must immediately follow the dot) and trip the empty-cache
483        // assertion in the `expect_without_ws_or_comments!` fallback. `scan_placeholder`
484        // returns `None` (leaving the tokenizer untouched) unless a placeholder
485        // begins exactly here, so the fallback paths run with an empty cache.
486        let placeholder = if input.options.template_placeholder.is_some() {
487            input.tokenizer.scan_placeholder()
488        } else {
489            None
490        };
491        let name = if let Some(TokenWithSpan { token: Token::Placeholder(placeholder), span }) =
492            placeholder
493        {
494            end = span.end;
495            InterpolableIdent::Placeholder((placeholder, span).into())
496        } else if input.syntax == Syntax::Css {
497            let (ident, ident_span) = expect_without_ws_or_comments!(input, Ident);
498            end = ident_span.end;
499            InterpolableIdent::Literal(input.ident(ident, ident_span))
500        } else {
501            let ident = input.parse::<InterpolableIdent>()?;
502            let ident_span = ident.span();
503            util::assert_no_ws_or_comment(&dot_span, ident_span)?;
504            end = ident_span.end;
505            ident
506        };
507
508        Ok(ClassSelector { name, span: Span { start, end } })
509    }
510}
511
512impl<'a> Parse<'a> for ComplexSelector<'a> {
513    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
514        let mut children = input.vec_with_capacity(3);
515
516        let (span, first, mut is_previous_combinator) = if let Token::GreaterThan(..)
517        | Token::Plus(..)
518        | Token::Tilde(..)
519        | Token::BarBar(..) = peek!(input).token
520        {
521            let end = input.tokenizer.current_offset();
522            if let Some(combinator) = input.parse_combinator(end)? {
523                (combinator.span.clone(), ComplexSelectorChild::Combinator(combinator), true)
524            } else {
525                return Err(Error {
526                    kind: ErrorKind::ExpectSimpleSelector,
527                    span: bump!(input).span,
528                });
529            }
530        } else {
531            let compound_selector = input.parse::<CompoundSelector>()?;
532            (
533                compound_selector.span.clone(),
534                ComplexSelectorChild::CompoundSelector(compound_selector),
535                false,
536            )
537        };
538        let Span { start, mut end } = span;
539
540        children.push(first);
541        let is_less = input.syntax == Syntax::Less;
542        while !matches!(
543            peek!(input).token,
544            Token::LBrace(..) | Token::Indent(..) | Token::Linebreak(..)
545        ) {
546            if is_previous_combinator {
547                let compound_selector = input.parse::<CompoundSelector>()?;
548                end = compound_selector.span.end;
549                children.push(ComplexSelectorChild::CompoundSelector(compound_selector));
550            } else if let Some(combinator) = input.parse_combinator(end)? {
551                if is_less && combinator.kind == CombinatorKind::Descendant {
552                    match &peek!(input).token {
553                        Token::Ident(ident) if ident.raw == "when" => break,
554                        _ => {}
555                    }
556                }
557                children.push(ComplexSelectorChild::Combinator(combinator));
558            } else {
559                break;
560            }
561            is_previous_combinator = !is_previous_combinator;
562        }
563
564        Ok(ComplexSelector { children, span: Span { start, end } })
565    }
566}
567
568impl<'a> Parse<'a> for CompoundSelector<'a> {
569    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
570        let first = input.parse::<SimpleSelector>()?;
571        let first_span = first.span();
572        let start = first_span.start;
573        let mut end = first_span.end;
574
575        let mut children = input.vec_with_capacity(2);
576        children.push(first);
577        loop {
578            use token::*;
579            match peek!(input) {
580                TokenWithSpan {
581                    token:
582                        Token::Dot(..)
583                        | Token::Hash(..)
584                        | Token::NumberSign(..)
585                        | Token::LBracket(..)
586                        | Token::Colon(..)
587                        | Token::ColonColon(..)
588                        | Token::Ident(..)
589                        | Token::Asterisk(..)
590                        | Token::HashLBrace(..)
591                        | Token::Bar(..)
592                        | Token::Ampersand(..)
593                        | Token::AtLBraceVar(..),
594                    span,
595                } if !util::has_ws(input.source, end, span.start) => {
596                    let child = input.parse::<SimpleSelector>()?;
597                    end = child.span().end;
598                    children.push(child);
599                }
600                TokenWithSpan { token: Token::Percent(..), span }
601                    if matches!(input.syntax, Syntax::Scss | Syntax::Sass)
602                        && !util::has_ws(input.source, end, span.start) =>
603                {
604                    let child = input.parse::<SimpleSelector>()?;
605                    end = child.span().end;
606                    children.push(child);
607                }
608                TokenWithSpan { token: Token::Placeholder(..), span }
609                    if !util::has_ws(input.source, end, span.start) =>
610                {
611                    let child = input.parse::<SimpleSelector>()?;
612                    end = child.span().end;
613                    children.push(child);
614                }
615                _ => break,
616            }
617        }
618
619        Ok(CompoundSelector { children, span: Span { start, end } })
620    }
621}
622
623impl<'a> Parse<'a> for CompoundSelectorList<'a> {
624    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
625        let first = input.parse::<CompoundSelector>()?;
626        let mut span = first.span.clone();
627
628        let mut selectors = arena_vec!(input; first);
629        let mut comma_spans = arena_vec!(input);
630        while let Some((_, comma_span)) = eat!(input, Comma) {
631            comma_spans.push(comma_span);
632            selectors.push(input.parse()?);
633        }
634
635        // SAFETY: it has at least one element.
636        span.end = unsafe {
637            let index = selectors.len() - 1;
638            selectors.get_unchecked(index).span().end
639        };
640        Ok(CompoundSelectorList { selectors, comma_spans, span })
641    }
642}
643
644impl<'a> Parse<'a> for IdSelector<'a> {
645    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
646        match bump!(input) {
647            TokenWithSpan { token: Token::Hash(token), span } => {
648                let first_span = Span { start: span.start + 1, end: span.end };
649                let raw = token.raw;
650                if raw.starts_with(|c: char| c.is_ascii_digit())
651                    || matches!(raw.as_bytes(), [b'-'] | [b'-', b'0'..=b'9', ..])
652                {
653                    input
654                        .recoverable_errors
655                        .push(Error { kind: ErrorKind::InvalidIdSelectorName, span: span.clone() });
656                }
657                let value = if token.escaped {
658                    util::handle_escape_in(raw, input.allocator())
659                } else {
660                    raw
661                };
662                let first = Ident { name: value, raw: token.raw, span: first_span };
663                let name = match peek!(input) {
664                    TokenWithSpan { token: Token::HashLBrace(..), span }
665                        if matches!(input.syntax, Syntax::Scss | Syntax::Sass)
666                            && first.span.end == span.start =>
667                    {
668                        match input.parse()? {
669                            InterpolableIdent::SassInterpolated(mut interpolation) => {
670                                interpolation.elements.insert(
671                                    0,
672                                    SassInterpolatedIdentElement::Static(
673                                        InterpolableIdentStaticPart {
674                                            value: first.name,
675                                            raw: first.raw,
676                                            span: first.span,
677                                        },
678                                    ),
679                                );
680                                InterpolableIdent::SassInterpolated(interpolation)
681                            }
682                            _ => unreachable!(),
683                        }
684                    }
685                    _ => InterpolableIdent::Literal(first),
686                };
687                let span = Span { start: span.start, end: name.span().end };
688                Ok(IdSelector { name, span })
689            }
690            TokenWithSpan { token: Token::NumberSign(..), span } => {
691                let name = input.parse::<InterpolableIdent>()?;
692                let span = Span { start: span.start, end: name.span().end };
693                Ok(IdSelector { name, span })
694            }
695            TokenWithSpan { span, .. } => Err(Error { kind: ErrorKind::ExpectIdSelector, span }),
696        }
697    }
698}
699
700impl<'a> Parse<'a> for LanguageRange<'a> {
701    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
702        match &peek!(input).token {
703            Token::Str(..) | Token::StrTemplate(..) => input.parse().map(LanguageRange::Str),
704            _ => input.parse().map(LanguageRange::Ident),
705        }
706    }
707}
708
709impl<'a> Parse<'a> for LanguageRangeList<'a> {
710    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
711        let first = input.parse::<LanguageRange>()?;
712        let mut span = first.span().clone();
713
714        let mut ranges = arena_vec!(input; first);
715        let mut comma_spans = arena_vec!(input);
716        while let Some((_, comma_span)) = eat!(input, Comma) {
717            comma_spans.push(comma_span);
718            ranges.push(input.parse()?);
719        }
720        debug_assert_eq!(comma_spans.len() + 1, ranges.len());
721
722        if let Some(end) = ranges.last() {
723            span.end = end.span().end;
724        }
725        Ok(LanguageRangeList { ranges, comma_spans, span })
726    }
727}
728
729impl<'a> Parse<'a> for NestingSelector<'a> {
730    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
731        let (_, mut span) = expect!(input, Ampersand);
732        let suffix = match input.syntax {
733            Syntax::Css => {
734                if let Some((ident, ident_span)) = input.tokenizer.scan_ident_template()? {
735                    span.end = ident_span.end;
736                    Some(InterpolableIdent::Literal(input.ident(ident, ident_span)))
737                } else {
738                    None
739                }
740            }
741            Syntax::Scss | Syntax::Sass => {
742                let start = span.end;
743                let elements = input.parse_sass_interpolated_ident_rest(&mut span.end)?;
744                if elements.is_empty() {
745                    None
746                } else {
747                    Some(InterpolableIdent::SassInterpolated(SassInterpolatedIdent {
748                        elements,
749                        span: Span { start, end: span.end },
750                    }))
751                }
752            }
753            Syntax::Less => {
754                let start = span.end;
755                let elements = input.parse_less_interpolated_ident_rest(&mut span.end)?;
756                if elements.is_empty() {
757                    None
758                } else {
759                    Some(InterpolableIdent::LessInterpolated(LessInterpolatedIdent {
760                        elements,
761                        span: Span { start, end: span.end },
762                    }))
763                }
764            }
765        };
766        Ok(NestingSelector { suffix, span })
767    }
768}
769
770// https://drafts.csswg.org/selectors-4/#the-nth-child-pseudo
771impl<'a> Parse<'a> for Nth<'a> {
772    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
773        let index = input.parse::<NthIndex>()?;
774        let mut span = index.span().clone();
775        let matcher = match &peek!(input).token {
776            Token::Ident(ident) if ident.name().eq_ignore_ascii_case("of") => {
777                let matcher = input.parse::<NthMatcher>()?;
778                span.end = matcher.span.end;
779                Some(matcher)
780            }
781            _ => None,
782        };
783
784        Ok(Nth { index, matcher, span })
785    }
786}
787
788impl<'a> Parse<'a> for NthIndex<'a> {
789    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
790        match &peek!(input).token {
791            Token::Ident(ident) => {
792                let name = ident.name();
793                if name.eq_ignore_ascii_case("odd") {
794                    input.parse().map(NthIndex::Odd)
795                } else if name.eq_ignore_ascii_case("even") {
796                    input.parse().map(NthIndex::Even)
797                } else {
798                    input.parse().map(NthIndex::AnPlusB)
799                }
800            }
801            Token::Number(..) => {
802                let number = input.parse::<Number>()?;
803                if number.value.fract() == 0.0 {
804                    Ok(NthIndex::Integer(number))
805                } else {
806                    Err(Error { kind: ErrorKind::ExpectInteger, span: number.span })
807                }
808            }
809            _ => input.parse().map(NthIndex::AnPlusB),
810        }
811    }
812}
813
814impl<'a> Parse<'a> for NthMatcher<'a> {
815    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
816        let (ident, mut span) = expect!(input, Ident);
817        if !ident.name().eq_ignore_ascii_case("of") {
818            return Err(Error { kind: ErrorKind::ExpectNthOf, span });
819        }
820
821        let selector = if matches!(&peek!(input).token, Token::RParen(..)) {
822            None
823        } else {
824            let selector = input.parse::<SelectorList>()?;
825            span.end = selector.span.end;
826            Some(selector)
827        };
828
829        Ok(NthMatcher { selector, span })
830    }
831}
832
833impl<'a> Parse<'a> for PseudoClassSelector<'a> {
834    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
835        let (_, colon_span) = expect!(input, Colon);
836        let name = input.parse::<InterpolableIdent>()?;
837        let name_span = name.span();
838        util::assert_no_ws(input.source, &colon_span, name_span)?;
839
840        let mut end = name_span.end;
841
842        let arg = match peek!(input) {
843            TokenWithSpan { token: Token::LParen(..), span: l_paren } if l_paren.start == end => {
844                let l_paren = l_paren.clone();
845                bump!(input);
846                let kind = match &name {
847                    InterpolableIdent::Literal(Ident { name, .. })
848                        if name.eq_ignore_ascii_case("nth-child")
849                            || name.eq_ignore_ascii_case("nth-last-child") =>
850                    {
851                        if input.syntax == Syntax::Css {
852                            input.parse().map(PseudoClassSelectorArgKind::Nth)?
853                        } else if let Ok(nth) = input.try_parse(Nth::parse) {
854                            PseudoClassSelectorArgKind::Nth(nth)
855                        } else {
856                            input
857                                .parse_tokens_in_parens()
858                                .map(PseudoClassSelectorArgKind::TokenSeq)?
859                        }
860                    }
861                    InterpolableIdent::Literal(Ident { name, .. })
862                        if name.eq_ignore_ascii_case("nth-of-type")
863                            || name.eq_ignore_ascii_case("nth-last-of-type")
864                            || name.eq_ignore_ascii_case("nth-col")
865                            || name.eq_ignore_ascii_case("nth-last-col") =>
866                    'pseudo_arg: {
867                        let nth = if input.syntax == Syntax::Css {
868                            input.parse()?
869                        } else if let Ok(nth) = input.try_parse(Nth::parse) {
870                            nth
871                        } else {
872                            break 'pseudo_arg input
873                                .parse_tokens_in_parens()
874                                .map(PseudoClassSelectorArgKind::TokenSeq)?;
875                        };
876                        if let Some(NthMatcher { span, .. }) = &nth.matcher {
877                            input.recoverable_errors.push(Error {
878                                kind: ErrorKind::UnexpectedNthMatcher,
879                                span: span.clone(),
880                            });
881                        }
882                        PseudoClassSelectorArgKind::Nth(nth)
883                    }
884                    InterpolableIdent::Literal(Ident { name, .. })
885                        if name.eq_ignore_ascii_case("not")
886                            || name.eq_ignore_ascii_case("is")
887                            || name.eq_ignore_ascii_case("where")
888                            || name.eq_ignore_ascii_case("matches")
889                            || name.eq_ignore_ascii_case("global") =>
890                    {
891                        input.parse().map(PseudoClassSelectorArgKind::SelectorList)?
892                    }
893                    InterpolableIdent::Literal(Ident { name, .. })
894                        if name.eq_ignore_ascii_case("has") =>
895                    {
896                        input.parse().map(PseudoClassSelectorArgKind::RelativeSelectorList)?
897                    }
898                    InterpolableIdent::Literal(Ident { name, .. })
899                        if name.eq_ignore_ascii_case("dir") =>
900                    {
901                        input.parse().map(PseudoClassSelectorArgKind::Ident)?
902                    }
903                    InterpolableIdent::Literal(Ident { name, .. })
904                        if name.eq_ignore_ascii_case("lang") =>
905                    {
906                        input.parse().map(PseudoClassSelectorArgKind::LanguageRangeList)?
907                    }
908                    InterpolableIdent::Literal(Ident { name, .. })
909                        if name.eq_ignore_ascii_case("-moz-any")
910                            || name.eq_ignore_ascii_case("-webkit-any")
911                            || name.eq_ignore_ascii_case("current")
912                            || name.eq_ignore_ascii_case("past")
913                            || name.eq_ignore_ascii_case("future") =>
914                    {
915                        input.parse().map(PseudoClassSelectorArgKind::CompoundSelectorList)?
916                    }
917                    InterpolableIdent::Literal(Ident { name, .. })
918                        if name.eq_ignore_ascii_case("host")
919                            || name.eq_ignore_ascii_case("host-context") =>
920                    {
921                        input.parse().map(PseudoClassSelectorArgKind::CompoundSelector)?
922                    }
923                    InterpolableIdent::Literal(Ident { name, .. })
924                        if input.syntax == Syntax::Less && *name == "extend" =>
925                    {
926                        input.parse().map(PseudoClassSelectorArgKind::LessExtendList)?
927                    }
928                    _ => {
929                        input.parse_tokens_in_parens().map(PseudoClassSelectorArgKind::TokenSeq)?
930                    }
931                };
932
933                let r_paren = expect!(input, RParen).1;
934                end = r_paren.end;
935                let span = Span { start: l_paren.start, end: r_paren.end };
936                Some(PseudoClassSelectorArg { kind, l_paren, r_paren, span })
937            }
938            _ => None,
939        };
940
941        let span = Span { start: colon_span.start, end };
942        Ok(PseudoClassSelector { name, arg, span })
943    }
944}
945
946impl<'a> Parse<'a> for PseudoElementSelector<'a> {
947    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
948        let (_, colon_colon_span) = expect!(input, ColonColon);
949        let mut end;
950        let name = if input.syntax == Syntax::Css {
951            let (ident, ident_span) = expect!(input, Ident);
952            end = ident_span.end;
953            util::assert_no_ws(input.source, &colon_colon_span, &ident_span)?;
954            InterpolableIdent::Literal(input.ident(ident, ident_span))
955        } else {
956            let name = input.parse::<InterpolableIdent>()?;
957            let name_span = name.span();
958            end = name_span.end;
959            util::assert_no_ws(input.source, &colon_colon_span, name_span)?;
960            name
961        };
962
963        let arg = match peek!(input) {
964            TokenWithSpan { token: Token::LParen(..), span: l_paren } if l_paren.start == end => {
965                let l_paren = l_paren.clone();
966                bump!(input);
967                let kind = match &name {
968                    InterpolableIdent::Literal(Ident { name, .. })
969                        if name.eq_ignore_ascii_case("part") =>
970                    {
971                        input.parse().map(PseudoElementSelectorArgKind::Ident)?
972                    }
973                    InterpolableIdent::Literal(Ident { name, .. })
974                        if name.eq_ignore_ascii_case("cue")
975                            || name.eq_ignore_ascii_case("cue-region")
976                            || name.eq_ignore_ascii_case("slotted") =>
977                    {
978                        input.parse().map(PseudoElementSelectorArgKind::CompoundSelector)?
979                    }
980                    _ => input
981                        .parse_tokens_in_parens()
982                        .map(PseudoElementSelectorArgKind::TokenSeq)?,
983                };
984
985                let r_paren = expect!(input, RParen).1;
986                end = r_paren.end;
987                let span = Span { start: l_paren.start, end: r_paren.end };
988                Some(PseudoElementSelectorArg { kind, l_paren, r_paren, span })
989            }
990            _ => None,
991        };
992
993        let span = Span { start: colon_colon_span.start, end };
994        Ok(PseudoElementSelector { name, arg, span })
995    }
996}
997
998impl<'a> Parse<'a> for RelativeSelector<'a> {
999    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1000        let pos = input.tokenizer.current_offset();
1001        let combinator = match input.parse_combinator(pos)? {
1002            Some(Combinator { kind: CombinatorKind::Descendant, .. }) => None,
1003            combinator => combinator,
1004        };
1005        let complex_selector = input.parse::<ComplexSelector>()?;
1006        let mut span = complex_selector.span.clone();
1007        if let Some(combinator) = &combinator {
1008            span.start = combinator.span.start;
1009        }
1010        Ok(RelativeSelector { combinator, complex_selector, span })
1011    }
1012}
1013
1014impl<'a> Parse<'a> for RelativeSelectorList<'a> {
1015    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1016        let first = input.parse::<RelativeSelector>()?;
1017        let mut span = first.span.clone();
1018
1019        let mut selectors = arena_vec!(input; first);
1020        let mut comma_spans = arena_vec!(input);
1021        while let Some((_, comma_span)) = eat!(input, Comma) {
1022            comma_spans.push(comma_span);
1023            selectors.push(input.parse()?);
1024        }
1025
1026        // SAFETY: it has at least one element.
1027        span.end = unsafe {
1028            let index = selectors.len() - 1;
1029            selectors.get_unchecked(index).span().end
1030        };
1031        Ok(RelativeSelectorList { selectors, comma_spans, span })
1032    }
1033}
1034
1035impl<'a> Parse<'a> for SelectorList<'a> {
1036    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1037        let first = input.parse::<ComplexSelector>()?;
1038        let mut span = first.span.clone();
1039
1040        let mut selectors = input.vec_with_capacity(2);
1041        selectors.push(first);
1042        let mut comma_spans = arena_vec!(input);
1043
1044        let is_css = input.syntax == Syntax::Css;
1045        while let Some((_, comma_span)) = eat!(input, Comma) {
1046            span.end = comma_span.end;
1047            comma_spans.push(comma_span);
1048            if !is_css
1049                && matches!(
1050                    peek!(input).token,
1051                    Token::LBrace(..) | Token::Indent(..) | Token::Linebreak(..)
1052                )
1053            {
1054                break;
1055            }
1056
1057            let selector = input.parse::<ComplexSelector>()?;
1058            span.end = selector.span.end;
1059            selectors.push(selector);
1060        }
1061
1062        debug_assert!(if is_css {
1063            selectors.len() - comma_spans.len() == 1
1064        } else {
1065            selectors.len() - comma_spans.len() <= 1
1066        });
1067
1068        Ok(SelectorList { selectors, comma_spans, span })
1069    }
1070}
1071
1072// https://www.w3.org/TR/selectors-4/#ref-for-typedef-simple-selector
1073impl<'a> Parse<'a> for SimpleSelector<'a> {
1074    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1075        match peek!(input) {
1076            TokenWithSpan { token: Token::Dot(..), .. } => input.parse().map(SimpleSelector::Class),
1077            TokenWithSpan { token: Token::Hash(..) | Token::NumberSign(..), .. } => {
1078                input.parse().map(SimpleSelector::Id)
1079            }
1080            TokenWithSpan { token: Token::LBracket(..), .. } => {
1081                input.parse().map(SimpleSelector::Attribute)
1082            }
1083            TokenWithSpan { token: Token::Colon(..), .. } => {
1084                input.parse().map(SimpleSelector::PseudoClass)
1085            }
1086            TokenWithSpan { token: Token::ColonColon(..), .. } => {
1087                input.parse().map(SimpleSelector::PseudoElement)
1088            }
1089            TokenWithSpan {
1090                token:
1091                    Token::Ident(..)
1092                    | Token::Asterisk(..)
1093                    | Token::HashLBrace(..)
1094                    | Token::Bar(..)
1095                    | Token::AtLBraceVar(..),
1096                ..
1097            } => input.parse().map(SimpleSelector::Type),
1098            TokenWithSpan { token: Token::Ampersand(..), .. } => {
1099                input.parse().map(SimpleSelector::Nesting)
1100            }
1101            TokenWithSpan { token: Token::Percent(..), .. }
1102                if matches!(input.syntax, Syntax::Scss | Syntax::Sass) =>
1103            {
1104                input.parse().map(SimpleSelector::SassPlaceholder)
1105            }
1106            TokenWithSpan { token: Token::Placeholder(..), .. } => {
1107                let name = input.parse::<InterpolableIdent>()?;
1108                let span = name.span().clone();
1109                Ok(SimpleSelector::Type(TypeSelector::TagName(TagNameSelector {
1110                    name: WqName { name, prefix: None, span: span.clone() },
1111                    span,
1112                })))
1113            }
1114            token_with_span => Err(Error {
1115                kind: ErrorKind::ExpectSimpleSelector,
1116                span: token_with_span.span.clone(),
1117            }),
1118        }
1119    }
1120}
1121
1122impl<'a> Parse<'a> for TypeSelector<'a> {
1123    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
1124        enum IdentOrAsterisk<'a> {
1125            Ident(InterpolableIdent<'a>),
1126            Asterisk(Span),
1127        }
1128
1129        let ident_or_asterisk = match &peek!(input).token {
1130            Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..) => {
1131                input.parse().map(IdentOrAsterisk::Ident).map(Some)?
1132            }
1133            Token::Asterisk(..) => Some(IdentOrAsterisk::Asterisk(bump!(input).span)),
1134            Token::Bar(..) => None,
1135            _ => unreachable!(),
1136        };
1137
1138        match peek!(input) {
1139            TokenWithSpan { token: Token::Bar(..), span }
1140                if ident_or_asterisk
1141                    .as_ref()
1142                    .map(|t| match t {
1143                        IdentOrAsterisk::Ident(ident) => {
1144                            !util::has_ws(input.source, ident.span().end, span.start)
1145                        }
1146                        IdentOrAsterisk::Asterisk(asterisk_span) => {
1147                            !util::has_ws(input.source, asterisk_span.end, span.start)
1148                        }
1149                    })
1150                    .unwrap_or(true) =>
1151            {
1152                let bar_token_span = bump!(input).span;
1153
1154                let prefix = match ident_or_asterisk {
1155                    Some(IdentOrAsterisk::Ident(ident)) => {
1156                        let mut span = ident.span().clone();
1157                        span.end = bar_token_span.end;
1158                        NsPrefix { kind: Some(NsPrefixKind::Ident(ident)), span }
1159                    }
1160                    Some(IdentOrAsterisk::Asterisk(asterisk_span)) => {
1161                        let mut span = asterisk_span.clone();
1162                        span.end = bar_token_span.end;
1163                        NsPrefix {
1164                            kind: Some(NsPrefixKind::Universal(NsPrefixUniversal {
1165                                span: asterisk_span,
1166                            })),
1167                            span,
1168                        }
1169                    }
1170                    None => NsPrefix { kind: None, span: bar_token_span },
1171                };
1172
1173                match peek!(input) {
1174                    TokenWithSpan { token: Token::Ident(..) | Token::HashLBrace(..), .. } => {
1175                        let name = input.parse::<InterpolableIdent>()?;
1176                        let name_span = name.span();
1177                        util::assert_no_ws(input.source, &prefix.span, name_span)?;
1178                        let span = Span { start: prefix.span.start, end: name_span.end };
1179                        Ok(TypeSelector::TagName(TagNameSelector {
1180                            name: WqName { name, prefix: Some(prefix), span: span.clone() },
1181                            span,
1182                        }))
1183                    }
1184                    TokenWithSpan { token: Token::Asterisk(..), .. } => {
1185                        let asterisk_span = bump!(input).span;
1186                        util::assert_no_ws(input.source, &prefix.span, &asterisk_span)?;
1187                        let span = Span { start: prefix.span.start, end: asterisk_span.end };
1188                        Ok(TypeSelector::Universal(UniversalSelector {
1189                            prefix: Some(prefix),
1190                            span,
1191                        }))
1192                    }
1193                    TokenWithSpan { span, .. } => {
1194                        Err(Error { kind: ErrorKind::ExpectTypeSelector, span: span.clone() })
1195                    }
1196                }
1197            }
1198
1199            _ => match ident_or_asterisk {
1200                Some(IdentOrAsterisk::Ident(ident)) => {
1201                    let span = ident.span().clone();
1202                    Ok(TypeSelector::TagName(TagNameSelector {
1203                        name: WqName { name: ident, prefix: None, span: span.clone() },
1204                        span,
1205                    }))
1206                }
1207                Some(IdentOrAsterisk::Asterisk(span)) => {
1208                    Ok(TypeSelector::Universal(UniversalSelector { prefix: None, span }))
1209                }
1210                None => unreachable!(),
1211            },
1212        }
1213    }
1214}
1215
1216impl<'a> Parser<'a> {
1217    fn parse_combinator(&mut self, pos: usize) -> PResult<Option<Combinator>> {
1218        match peek!(self) {
1219            TokenWithSpan {
1220                token:
1221                    Token::Ident(..)
1222                    | Token::Dot(..)
1223                    | Token::Hash(..)
1224                    | Token::Colon(..)
1225                    | Token::ColonColon(..)
1226                    | Token::LBracket(..)
1227                    | Token::Asterisk(..)
1228                    | Token::Ampersand(..)
1229                    | Token::Bar(..) // selector like `|type` (with <ns-prefix>)
1230                    | Token::AtLBraceVar(..)
1231                    | Token::NumberSign(..)
1232                    | Token::HashLBrace(..)
1233                    | Token::Placeholder(..), // `${a} ${b}` descendant
1234                span,
1235            } if pos < span.start => Ok(Some(Combinator {
1236                kind: CombinatorKind::Descendant,
1237                span: Span {
1238                    start: pos,
1239                    end: span.start,
1240                },
1241            })),
1242            TokenWithSpan {
1243                token: Token::GreaterThan(..),
1244                ..
1245            } => Ok(Some(Combinator {
1246                kind: CombinatorKind::Child,
1247                span: bump!(self).span,
1248            })),
1249            TokenWithSpan {
1250                token: Token::Plus(..),
1251                ..
1252            } => Ok(Some(Combinator {
1253                kind: CombinatorKind::NextSibling,
1254                span: bump!(self).span,
1255            })),
1256            TokenWithSpan {
1257                token: Token::Tilde(..),
1258                ..
1259            } => Ok(Some(Combinator {
1260                kind: CombinatorKind::LaterSibling,
1261                span: bump!(self).span,
1262            })),
1263            TokenWithSpan {
1264                token: Token::BarBar(..),
1265                ..
1266            } => Ok(Some(Combinator {
1267                kind: CombinatorKind::Column,
1268                span: bump!(self).span,
1269            })),
1270            _ => Ok(None),
1271        }
1272    }
1273}
1274
1275fn expect_unsigned_int<'a>(input: &mut Parser<'a>) -> PResult<(token::Number<'a>, Span)> {
1276    let (number, span) = expect!(input, Number);
1277    if number.raw.chars().any(|c| !c.is_ascii_digit()) {
1278        Err(Error { kind: ErrorKind::ExpectUnsignedInteger, span })
1279    } else {
1280        Ok((number, span))
1281    }
1282}