Skip to content

Commit 0ce47c5

Browse files
committed
Fix a selector parsing bug
Parsing e.g. `:is(:is(foo))` (as a selector) does not consume the last `)`, which is obviously erroneous behaviour. The bug stems from the body of the `parse_any_value` procedure in the `selectors` module which does not increment the counter number corresponding to parentheses when encountering a function token (which features these). With the wrong counter value, the first subsequently encountered `)` token (a `CloseParenToken`) would break the `any-value` parsing procedure before the latter token is consumed, as it would be considered "non-matching" (borrowing the term from the "Selectors" spec.). This change fixes the issue by adding a `case` for `FunctionToken`, and supplements it all with an additional test, that was missing as well (meaning lack of coverage hid the issue all up until now, unfortunately).
1 parent caea4bc commit 0ce47c5

2 files changed

Lines changed: 6 additions & 2 deletions

File tree

src/csspring/selectors.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"""
99

1010
from .syntax.parsing import Input as TokenStream, Product, tokens # Conveniently reusing some of the helpful constructs offered by the [CSS] syntax-level parsing module
11-
from .syntax.tokenizing import Token, BadStringToken, BadURLToken, CloseBraceToken, CloseBracketToken, CloseParenToken, ColonToken, DelimToken, FunctionToken, HashToken, IdentToken, OpenBraceToken, OpenBracketToken, OpenParenToken, StringToken
11+
from .syntax.tokenizing import Token, token_value, BadStringToken, BadURLToken, CloseBraceToken, CloseBracketToken, CloseParenToken, ColonToken, DelimToken, FunctionToken, HashToken, IdentToken, OpenBraceToken, OpenBracketToken, OpenParenToken, StringToken
1212

1313
from .syntax.grammar import any_value
1414
from .values import Production, AlternativesProduction, CommaSeparatedRepetitionProduction, ConcatenationProduction, NonEmptyProduction, OptionalProduction, ReferenceProduction, RepetitionProduction, TokenProduction, OWS
@@ -56,6 +56,9 @@ def parse_any_value(input: TokenStream) -> Product | None:
5656
break
5757
case OpenParenToken() | OpenBracketToken() | OpenBraceToken():
5858
count[type(token)] += 1
59+
case FunctionToken():
60+
assert token.source[-1] == token_value(OpenParenToken)
61+
count[OpenParenToken] += 1
5962
case CloseParenToken() | CloseBracketToken() | CloseBraceToken():
6063
if count[token.mirror_type] == 0:
6164
break

tests/test_selectors.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
'foo[ bar ]',
2727
'foo[bar=baz]'
2828
'foo[bar= baz ]'
29-
'foo[bar= "baz" ]'
29+
'foo[bar= "baz" ]',
30+
':is(:is(foo))'
3031
))
3132
def test_selector_parsing(text: str) -> None:
3233
"""Test whether the text recovered from the product of parsing given text (as a selector [list]) equals given text.

0 commit comments

Comments
 (0)