lol_html/selectors_vm/
ast.rs

1use super::parser::{Selector, SelectorImplDescriptor};
2use hashbrown::HashSet;
3use selectors::attr::{AttrSelectorOperator, ParsedCaseSensitivity};
4use selectors::parser::{Combinator, Component, NthType};
5use std::fmt::{self, Debug, Formatter};
6use std::hash::Hash;
7
8#[derive(PartialEq, Eq, Debug, Copy, Clone)]
9pub(crate) struct NthChild {
10    step: i32,
11    offset: i32,
12}
13
14impl NthChild {
15    #[inline]
16    #[must_use]
17    pub const fn new(step: i32, offset: i32) -> Self {
18        Self { step, offset }
19    }
20
21    #[must_use]
22    pub const fn has_index(self, index: i32) -> bool {
23        let Self { offset, step } = self;
24        // wrap to prevent panic/abort. we won't wrap around anyway, even with a
25        // max offset value (i32::MAX) since index is always more than 0
26        let offsetted = index.wrapping_sub(offset);
27        if step == 0 {
28            offsetted == 0
29        } else if (offsetted < 0 && step > 0) || (offsetted > 0 && step < 0) {
30            false
31        } else {
32            // again, wrap the remainder op. overflow only occurs with
33            // i32::MIN / -1. while the step can be -1, the offsetted
34            // value will never be i32::MIN since this index is always
35            // more than 0
36            offsetted.wrapping_rem(step) == 0
37        }
38    }
39}
40
41#[derive(PartialEq, Eq, Debug)]
42pub(crate) enum OnTagNameExpr {
43    ExplicitAny,
44    Unmatchable,
45    LocalName(Box<str>),
46    NthChild(NthChild),
47    NthOfType(NthChild),
48}
49
50#[derive(Eq, PartialEq)]
51pub(crate) struct AttributeComparisonExpr {
52    pub name: Box<str>,
53    pub value: Box<str>,
54    pub case_sensitivity: ParsedCaseSensitivity,
55    pub operator: AttrSelectorOperator,
56}
57
58impl AttributeComparisonExpr {
59    #[inline]
60    #[must_use]
61    pub const fn new(
62        name: Box<str>,
63        value: Box<str>,
64        case_sensitivity: ParsedCaseSensitivity,
65        operator: AttrSelectorOperator,
66    ) -> Self {
67        Self {
68            name,
69            value,
70            case_sensitivity,
71            operator,
72        }
73    }
74}
75
76impl Debug for AttributeComparisonExpr {
77    #[cold]
78    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
79        f.debug_struct("AttributeExpr")
80            .field("name", &self.name)
81            .field("value", &self.value)
82            .field("case_sensitivity", &self.case_sensitivity)
83            .field(
84                "operator",
85                match self.operator {
86                    AttrSelectorOperator::Equal => &"AttrSelectorOperator::Equal",
87                    AttrSelectorOperator::Includes => &"AttrSelectorOperator::Includes",
88                    AttrSelectorOperator::DashMatch => &"AttrSelectorOperator::DashMatch",
89                    AttrSelectorOperator::Prefix => &"AttrSelectorOperator::Prefix",
90                    AttrSelectorOperator::Substring => &"AttrSelectorOperator::Substring",
91                    AttrSelectorOperator::Suffix => &"AttrSelectorOperator::Suffix",
92                },
93            )
94            .finish()
95    }
96}
97
98/// An attribute check when attributes are received and parsed.
99#[derive(PartialEq, Eq, Debug)]
100pub(crate) enum OnAttributesExpr {
101    Id(Box<str>),
102    Class(Box<str>),
103    AttributeExists(Box<str>),
104    AttributeComparisonExpr(AttributeComparisonExpr),
105}
106
107#[derive(PartialEq, Eq, Debug)]
108/// Conditions executed as part of a predicate, or an "expect" in pseudo instructions.
109/// These are executed in order of definition.
110enum Condition {
111    OnTagName(OnTagNameExpr),
112    OnAttributes(OnAttributesExpr),
113}
114
115impl From<&Component<SelectorImplDescriptor>> for Condition {
116    fn from(component: &Component<SelectorImplDescriptor>) -> Self {
117        match component {
118            Component::LocalName(n) => {
119                Self::OnTagName(OnTagNameExpr::LocalName(n.name.to_boxed_slice()))
120            }
121            Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => {
122                Self::OnTagName(OnTagNameExpr::ExplicitAny)
123            }
124            Component::ExplicitNoNamespace => Self::OnTagName(OnTagNameExpr::Unmatchable),
125            Component::ID(id) => Self::OnAttributes(OnAttributesExpr::Id(id.to_boxed_slice())),
126            Component::Class(c) => Self::OnAttributes(OnAttributesExpr::Class(c.to_boxed_slice())),
127            Component::AttributeInNoNamespaceExists { local_name, .. } => Self::OnAttributes(
128                OnAttributesExpr::AttributeExists(local_name.to_boxed_slice()),
129            ),
130            &Component::AttributeInNoNamespace {
131                ref local_name,
132                ref value,
133                operator,
134                case_sensitivity,
135            } => Self::OnAttributes(OnAttributesExpr::AttributeComparisonExpr(
136                AttributeComparisonExpr::new(
137                    local_name.to_boxed_slice(),
138                    value.to_boxed_slice(),
139                    case_sensitivity,
140                    operator,
141                ),
142            )),
143            Component::Nth(data) if data.ty == NthType::Child => {
144                Self::OnTagName(OnTagNameExpr::NthChild(NthChild::new(data.an_plus_b.0, data.an_plus_b.1)))
145            }
146            Component::Nth(data) if data.ty == NthType::OfType => {
147                Self::OnTagName(OnTagNameExpr::NthOfType(NthChild::new(data.an_plus_b.0, data.an_plus_b.1)))
148            }
149            // NOTE: the rest of the components are explicit namespace or
150            // pseudo class-related. Ideally none of them should appear in
151            // the parsed selector as we should bail earlier in the parser.
152            // Otherwise, we'll have AST in invalid state in case of error.
153            bad_selector => unreachable!(
154                "Unsupported selector components should be filtered out by the parser: {bad_selector:?}"
155            ),
156        }
157    }
158}
159
160#[derive(PartialEq, Eq, Debug)]
161pub(crate) struct Expr<E>
162where
163    E: PartialEq + Eq + Debug,
164{
165    pub simple_expr: E,
166    pub negation: bool,
167}
168
169impl<E> Expr<E>
170where
171    E: PartialEq + Eq + Debug,
172{
173    #[inline]
174    const fn new(simple_expr: E, negation: bool) -> Self {
175        Self {
176            simple_expr,
177            negation,
178        }
179    }
180}
181
182#[derive(PartialEq, Eq, Debug, Default)]
183pub(crate) struct Predicate {
184    pub on_tag_name_exprs: Vec<Expr<OnTagNameExpr>>,
185    pub on_attr_exprs: Vec<Expr<OnAttributesExpr>>,
186}
187
188#[inline]
189fn add_expr_to_list<E>(list: &mut Vec<Expr<E>>, expr: E, negation: bool)
190where
191    E: PartialEq + Eq + Debug,
192{
193    list.push(Expr::new(expr, negation));
194}
195
196impl Predicate {
197    #[inline]
198    fn add_component(&mut self, component: &Component<SelectorImplDescriptor>, negation: bool) {
199        match Condition::from(component) {
200            Condition::OnTagName(e) => add_expr_to_list(&mut self.on_tag_name_exprs, e, negation),
201            Condition::OnAttributes(e) => add_expr_to_list(&mut self.on_attr_exprs, e, negation),
202        }
203    }
204}
205
206#[derive(PartialEq, Eq, Debug)]
207pub(crate) struct AstNode<P>
208where
209    P: Hash + Eq,
210{
211    pub predicate: Predicate,
212    pub children: Vec<AstNode<P>>,
213    pub descendants: Vec<AstNode<P>>,
214    pub payload: HashSet<P>,
215}
216
217impl<P> AstNode<P>
218where
219    P: Hash + Eq,
220{
221    fn new(predicate: Predicate) -> Self {
222        Self {
223            predicate,
224            children: Vec::default(),
225            descendants: Vec::default(),
226            payload: HashSet::default(),
227        }
228    }
229}
230
231// exposed for selectors_ast tool
232#[derive(Default, PartialEq, Eq, Debug)]
233pub struct Ast<P>
234where
235    P: PartialEq + Eq + Copy + Debug + Hash,
236{
237    pub(crate) root: Vec<AstNode<P>>,
238    // NOTE: used to preallocate instruction vector during compilation.
239    pub(crate) cumulative_node_count: usize,
240}
241
242impl<P> Ast<P>
243where
244    P: PartialEq + Eq + Copy + Debug + Hash,
245{
246    #[inline]
247    fn host_expressions(
248        predicate: Predicate,
249        branches: &mut Vec<AstNode<P>>,
250        cumulative_node_count: &mut usize,
251    ) -> usize {
252        branches
253            .iter()
254            .enumerate()
255            .find(|(_, n)| n.predicate == predicate)
256            .map(|(i, _)| i)
257            .unwrap_or_else(|| {
258                branches.push(AstNode::new(predicate));
259                *cumulative_node_count += 1;
260
261                branches.len() - 1
262            })
263    }
264
265    pub fn add_selector(&mut self, selector: &Selector, payload: P) {
266        for selector_item in (selector.0).slice() {
267            let mut predicate = Predicate::default();
268            let mut branches = &mut self.root;
269
270            macro_rules! host_and_switch_branch_vec {
271                ($branches:ident) => {{
272                    let node_idx = Self::host_expressions(
273                        predicate,
274                        branches,
275                        &mut self.cumulative_node_count,
276                    );
277
278                    branches = &mut branches[node_idx].$branches;
279                    predicate = Predicate::default();
280                }};
281            }
282
283            for component in selector_item.iter_raw_parse_order_from(0) {
284                match component {
285                    Component::Combinator(Combinator::Child) => {
286                        host_and_switch_branch_vec!(children);
287                    }
288                    Component::Combinator(Combinator::Descendant) => {
289                        host_and_switch_branch_vec!(descendants);
290                    }
291                    Component::Negation(ss) => {
292                        ss.slice()
293                            .iter()
294                            .for_each(|s| s.iter().for_each(|c| predicate.add_component(c, true)));
295                    }
296                    _ => predicate.add_component(component, false),
297                }
298            }
299
300            let node_idx =
301                Self::host_expressions(predicate, branches, &mut self.cumulative_node_count);
302
303            branches[node_idx].payload.insert(payload);
304        }
305    }
306}
307
308#[cfg(test)]
309mod tests {
310    use super::*;
311    use crate::selectors_vm::SelectorError;
312
313    macro_rules! set {
314        ($($items:expr),*) => {
315            vec![$($items),*].into_iter().collect::<HashSet<_>>()
316        };
317    }
318
319    #[track_caller]
320    fn assert_ast(selectors: &[&str], expected: Ast<usize>) {
321        let mut ast = Ast::default();
322
323        for (idx, selector) in selectors.iter().enumerate() {
324            ast.add_selector(&selector.parse().unwrap(), idx);
325        }
326
327        assert_eq!(ast, expected);
328    }
329
330    #[track_caller]
331    fn assert_err(selector: &str, expected_err: SelectorError) {
332        assert_eq!(selector.parse::<Selector>().unwrap_err(), expected_err);
333    }
334
335    #[test]
336    fn simple_non_attr_expression() {
337        for (selector, expected) in [
338            (
339                "*",
340                Expr {
341                    simple_expr: OnTagNameExpr::ExplicitAny,
342                    negation: false,
343                },
344            ),
345            (
346                "div",
347                Expr {
348                    simple_expr: OnTagNameExpr::LocalName("div".into()),
349                    negation: false,
350                },
351            ),
352            (
353                ":not(div)",
354                Expr {
355                    simple_expr: OnTagNameExpr::LocalName("div".into()),
356                    negation: true,
357                },
358            ),
359        ] {
360            assert_ast(
361                &[selector],
362                Ast {
363                    root: vec![AstNode {
364                        predicate: Predicate {
365                            on_tag_name_exprs: vec![expected],
366                            ..Default::default()
367                        },
368                        children: vec![],
369                        descendants: vec![],
370                        payload: set![0],
371                    }],
372                    cumulative_node_count: 1,
373                },
374            );
375        }
376    }
377
378    #[test]
379    fn simple_attr_expression() {
380        for (selector, expected) in [
381            (
382                "#foo",
383                Expr {
384                    simple_expr: OnAttributesExpr::Id("foo".into()),
385                    negation: false,
386                },
387            ),
388            (
389                ".bar",
390                Expr {
391                    simple_expr: OnAttributesExpr::Class("bar".into()),
392                    negation: false,
393                },
394            ),
395            (
396                "[foo]",
397                Expr {
398                    simple_expr: OnAttributesExpr::AttributeExists("foo".into()),
399                    negation: false,
400                },
401            ),
402            (
403                r#"[foo="bar"]"#,
404                Expr {
405                    simple_expr: OnAttributesExpr::AttributeComparisonExpr(
406                        AttributeComparisonExpr {
407                            name: "foo".into(),
408                            value: "bar".into(),
409                            case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
410                            operator: AttrSelectorOperator::Equal,
411                        },
412                    ),
413                    negation: false,
414                },
415            ),
416            (
417                r#"[foo*=""]"#,
418                Expr {
419                    simple_expr: OnAttributesExpr::AttributeComparisonExpr(
420                        AttributeComparisonExpr {
421                            name: "foo".into(),
422                            value: "".into(),
423                            case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
424                            operator: AttrSelectorOperator::Substring,
425                        },
426                    ),
427                    negation: false,
428                },
429            ),
430            (
431                r#"[foo~="bar" i]"#,
432                Expr {
433                    simple_expr: OnAttributesExpr::AttributeComparisonExpr(
434                        AttributeComparisonExpr {
435                            name: "foo".into(),
436                            value: "bar".into(),
437                            case_sensitivity: ParsedCaseSensitivity::AsciiCaseInsensitive,
438                            operator: AttrSelectorOperator::Includes,
439                        },
440                    ),
441                    negation: false,
442                },
443            ),
444            (
445                r#"[foo|="bar" s]"#,
446                Expr {
447                    simple_expr: OnAttributesExpr::AttributeComparisonExpr(
448                        AttributeComparisonExpr {
449                            name: "foo".into(),
450                            value: "bar".into(),
451                            case_sensitivity: ParsedCaseSensitivity::ExplicitCaseSensitive,
452                            operator: AttrSelectorOperator::DashMatch,
453                        },
454                    ),
455                    negation: false,
456                },
457            ),
458            (
459                r#"[foo^="bar"]"#,
460                Expr {
461                    simple_expr: OnAttributesExpr::AttributeComparisonExpr(
462                        AttributeComparisonExpr {
463                            name: "foo".into(),
464                            value: "bar".into(),
465                            case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
466                            operator: AttrSelectorOperator::Prefix,
467                        },
468                    ),
469                    negation: false,
470                },
471            ),
472            (
473                r#"[foo*="bar"]"#,
474                Expr {
475                    simple_expr: OnAttributesExpr::AttributeComparisonExpr(
476                        AttributeComparisonExpr {
477                            name: "foo".into(),
478                            value: "bar".into(),
479                            case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
480                            operator: AttrSelectorOperator::Substring,
481                        },
482                    ),
483                    negation: false,
484                },
485            ),
486            (
487                r#"[foo$="bar"]"#,
488                Expr {
489                    simple_expr: OnAttributesExpr::AttributeComparisonExpr(
490                        AttributeComparisonExpr {
491                            name: "foo".into(),
492                            value: "bar".into(),
493                            case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
494                            operator: AttrSelectorOperator::Suffix,
495                        },
496                    ),
497                    negation: false,
498                },
499            ),
500            (
501                r#":not([foo$="bar"])"#,
502                Expr {
503                    simple_expr: OnAttributesExpr::AttributeComparisonExpr(
504                        AttributeComparisonExpr {
505                            name: "foo".into(),
506                            value: "bar".into(),
507                            case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
508                            operator: AttrSelectorOperator::Suffix,
509                        },
510                    ),
511                    negation: true,
512                },
513            ),
514        ] {
515            assert_ast(
516                &[selector],
517                Ast {
518                    root: vec![AstNode {
519                        predicate: Predicate {
520                            on_attr_exprs: vec![expected],
521                            ..Default::default()
522                        },
523                        children: vec![],
524                        descendants: vec![],
525                        payload: set![0],
526                    }],
527                    cumulative_node_count: 1,
528                },
529            );
530        }
531    }
532
533    #[test]
534    fn compound_selectors() {
535        assert_ast(
536            &["div.foo#bar:not([baz])"],
537            Ast {
538                root: vec![AstNode {
539                    predicate: Predicate {
540                        on_tag_name_exprs: vec![Expr {
541                            simple_expr: OnTagNameExpr::LocalName("div".into()),
542                            negation: false,
543                        }],
544                        on_attr_exprs: vec![
545                            Expr {
546                                simple_expr: OnAttributesExpr::AttributeExists("baz".into()),
547                                negation: true,
548                            },
549                            Expr {
550                                simple_expr: OnAttributesExpr::Id("bar".into()),
551                                negation: false,
552                            },
553                            Expr {
554                                simple_expr: OnAttributesExpr::Class("foo".into()),
555                                negation: false,
556                            },
557                        ],
558                    },
559                    children: vec![],
560                    descendants: vec![],
561                    payload: set![0],
562                }],
563                cumulative_node_count: 1,
564            },
565        );
566    }
567
568    #[test]
569    fn multiple_payloads() {
570        assert_ast(
571            &["#foo", "#foo"],
572            Ast {
573                root: vec![AstNode {
574                    predicate: Predicate {
575                        on_attr_exprs: vec![Expr {
576                            simple_expr: OnAttributesExpr::Id("foo".into()),
577                            negation: false,
578                        }],
579                        ..Default::default()
580                    },
581                    children: vec![],
582                    descendants: vec![],
583                    payload: set![0, 1],
584                }],
585                cumulative_node_count: 1,
586            },
587        );
588    }
589
590    #[test]
591    fn selector_list() {
592        assert_ast(
593            &["#foo > div, #foo > span", "#foo > .c1, #foo > .c2"],
594            Ast {
595                root: vec![AstNode {
596                    predicate: Predicate {
597                        on_attr_exprs: vec![Expr {
598                            simple_expr: OnAttributesExpr::Id("foo".into()),
599                            negation: false,
600                        }],
601                        ..Default::default()
602                    },
603                    children: vec![
604                        AstNode {
605                            predicate: Predicate {
606                                on_tag_name_exprs: vec![Expr {
607                                    simple_expr: OnTagNameExpr::LocalName("div".into()),
608                                    negation: false,
609                                }],
610                                ..Default::default()
611                            },
612                            children: vec![],
613                            descendants: vec![],
614                            payload: set![0],
615                        },
616                        AstNode {
617                            predicate: Predicate {
618                                on_tag_name_exprs: vec![Expr {
619                                    simple_expr: OnTagNameExpr::LocalName("span".into()),
620                                    negation: false,
621                                }],
622                                ..Default::default()
623                            },
624                            children: vec![],
625                            descendants: vec![],
626                            payload: set![0],
627                        },
628                        AstNode {
629                            predicate: Predicate {
630                                on_attr_exprs: vec![Expr {
631                                    simple_expr: OnAttributesExpr::Class("c1".into()),
632                                    negation: false,
633                                }],
634                                ..Default::default()
635                            },
636                            children: vec![],
637                            descendants: vec![],
638                            payload: set![1],
639                        },
640                        AstNode {
641                            predicate: Predicate {
642                                on_attr_exprs: vec![Expr {
643                                    simple_expr: OnAttributesExpr::Class("c2".into()),
644                                    negation: false,
645                                }],
646                                ..Default::default()
647                            },
648                            children: vec![],
649                            descendants: vec![],
650                            payload: set![1],
651                        },
652                    ],
653                    descendants: vec![],
654                    payload: set![],
655                }],
656                cumulative_node_count: 5,
657            },
658        );
659    }
660
661    #[test]
662    fn combinators() {
663        assert_ast(
664            &[
665                ".c1 > .c2 .c3 #foo",
666                ".c1 > .c2 #bar",
667                ".c1 > #qux",
668                ".c1 #baz",
669                ".c1 [foo] [bar]",
670                "#quz",
671            ],
672            Ast {
673                root: vec![
674                    AstNode {
675                        predicate: Predicate {
676                            on_attr_exprs: vec![Expr {
677                                simple_expr: OnAttributesExpr::Class("c1".into()),
678                                negation: false,
679                            }],
680                            ..Default::default()
681                        },
682                        children: vec![
683                            AstNode {
684                                predicate: Predicate {
685                                    on_attr_exprs: vec![Expr {
686                                        simple_expr: OnAttributesExpr::Class("c2".into()),
687                                        negation: false,
688                                    }],
689                                    ..Default::default()
690                                },
691                                children: vec![],
692                                descendants: vec![
693                                    AstNode {
694                                        predicate: Predicate {
695                                            on_attr_exprs: vec![Expr {
696                                                simple_expr: OnAttributesExpr::Class("c3".into()),
697                                                negation: false,
698                                            }],
699                                            ..Default::default()
700                                        },
701                                        children: vec![],
702                                        descendants: vec![AstNode {
703                                            predicate: Predicate {
704                                                on_attr_exprs: vec![Expr {
705                                                    simple_expr: OnAttributesExpr::Id("foo".into()),
706                                                    negation: false,
707                                                }],
708                                                ..Default::default()
709                                            },
710                                            children: vec![],
711                                            descendants: vec![],
712                                            payload: set![0],
713                                        }],
714                                        payload: set![],
715                                    },
716                                    AstNode {
717                                        predicate: Predicate {
718                                            on_attr_exprs: vec![Expr {
719                                                simple_expr: OnAttributesExpr::Id("bar".into()),
720                                                negation: false,
721                                            }],
722                                            ..Default::default()
723                                        },
724                                        children: vec![],
725                                        descendants: vec![],
726                                        payload: set![1],
727                                    },
728                                ],
729                                payload: set![],
730                            },
731                            AstNode {
732                                predicate: Predicate {
733                                    on_attr_exprs: vec![Expr {
734                                        simple_expr: OnAttributesExpr::Id("qux".into()),
735                                        negation: false,
736                                    }],
737                                    ..Default::default()
738                                },
739                                children: vec![],
740                                descendants: vec![],
741                                payload: set![2],
742                            },
743                        ],
744                        descendants: vec![
745                            AstNode {
746                                predicate: Predicate {
747                                    on_attr_exprs: vec![Expr {
748                                        simple_expr: OnAttributesExpr::Id("baz".into()),
749                                        negation: false,
750                                    }],
751                                    ..Default::default()
752                                },
753                                children: vec![],
754                                descendants: vec![],
755                                payload: set![3],
756                            },
757                            AstNode {
758                                predicate: Predicate {
759                                    on_attr_exprs: vec![Expr {
760                                        simple_expr: OnAttributesExpr::AttributeExists(
761                                            "foo".into(),
762                                        ),
763                                        negation: false,
764                                    }],
765                                    ..Default::default()
766                                },
767                                children: vec![],
768                                descendants: vec![AstNode {
769                                    predicate: Predicate {
770                                        on_attr_exprs: vec![Expr {
771                                            simple_expr: OnAttributesExpr::AttributeExists(
772                                                "bar".into(),
773                                            ),
774                                            negation: false,
775                                        }],
776                                        ..Default::default()
777                                    },
778                                    children: vec![],
779                                    descendants: vec![],
780                                    payload: set![4],
781                                }],
782                                payload: set![],
783                            },
784                        ],
785                        payload: set![],
786                    },
787                    AstNode {
788                        predicate: Predicate {
789                            on_attr_exprs: vec![Expr {
790                                simple_expr: OnAttributesExpr::Id("quz".into()),
791                                negation: false,
792                            }],
793                            ..Default::default()
794                        },
795                        children: vec![],
796                        descendants: vec![],
797                        payload: set![5],
798                    },
799                ],
800                cumulative_node_count: 10,
801            },
802        );
803    }
804
805    #[test]
806    fn parse_errors() {
807        assert_err("div@", SelectorError::UnexpectedToken);
808        assert_err("div.", SelectorError::UnexpectedEnd);
809        assert_err(r#"div[="foo"]"#, SelectorError::MissingAttributeName);
810        assert_err("", SelectorError::EmptySelector);
811        assert_err("div >", SelectorError::DanglingCombinator);
812        assert_err(
813            r#"div[foo~"bar"]"#,
814            SelectorError::UnexpectedTokenInAttribute,
815        );
816        assert_err("svg|img", SelectorError::NamespacedSelector);
817        assert_err(".foo()", SelectorError::InvalidClassName);
818        assert_err(":not()", SelectorError::EmptySelector);
819        assert_err("div + span", SelectorError::UnsupportedCombinator('+'));
820        assert_err("div ~ span", SelectorError::UnsupportedCombinator('~'));
821        assert_err(":nth-child(n of a)", SelectorError::UnexpectedToken);
822    }
823
824    #[test]
825    fn pseudo_class_parse_errors() {
826        for s in &[
827            ":active",
828            ":any-link",
829            ":blank",
830            ":checked",
831            ":current",
832            ":default",
833            ":defined",
834            ":dir(rtl)",
835            ":disabled",
836            ":drop",
837            ":empty",
838            ":enabled",
839            ":first",
840            ":fullscreen",
841            ":future",
842            ":focus",
843            ":focus-visible",
844            ":focus-within",
845            ":has(div)",
846            ":host",
847            ":host(h1)",
848            ":host-context(h1)",
849            ":hover",
850            ":indeterminate",
851            ":in-range",
852            ":invalid",
853            ":is(header)",
854            ":lang(en)",
855            ":last-child",
856            ":last-of-type",
857            ":left",
858            ":link",
859            ":local-link",
860            ":nth-col(1)",
861            ":nth-last-child(1)",
862            ":nth-last-col(1)",
863            ":nth-last-of-type(1)",
864            ":only-child",
865            ":only-of-type",
866            ":optional",
867            ":out-of-range",
868            ":past",
869            ":placeholder-shown",
870            ":read-only",
871            ":read-write",
872            ":required",
873            ":right",
874            ":root",
875            ":scope",
876            ":target",
877            ":target-within",
878            ":user-invalid",
879            ":valid",
880            ":visited",
881            ":where(p)",
882        ] {
883            assert_err(s, SelectorError::UnsupportedPseudoClassOrElement);
884        }
885    }
886
887    #[test]
888    fn pseudo_elements_parse_errors() {
889        for s in &[
890            "::after",
891            "::backdrop",
892            "::before",
893            "::cue",
894            "::first-letter",
895            "::first-line",
896            "::grammar-error",
897            "::marker",
898            "::placeholder",
899            "::selection",
900            "::slotted()",
901            "::spelling-error",
902        ] {
903            assert_err(s, SelectorError::UnsupportedPseudoClassOrElement);
904        }
905    }
906
907    #[test]
908    fn negated_pseudo_class_parse_error() {
909        assert_err(
910            ":not(:nth-last-child(even))",
911            SelectorError::UnsupportedPseudoClassOrElement,
912        );
913    }
914
915    #[test]
916    fn nth_child_is_index() {
917        let even = NthChild::new(2, 0);
918        assert!(even.has_index(2));
919        assert!(!even.has_index(1));
920
921        let odd = NthChild::new(2, 1);
922        assert!(odd.has_index(1));
923        assert!(!odd.has_index(2));
924        assert!(odd.has_index(3));
925
926        let first = NthChild::new(0, 1);
927        assert!(first.has_index(1));
928        assert!(!first.has_index(2));
929        assert!(!first.has_index(3));
930    }
931}