Skip to content

Commit 515317d

Browse files
authored
[BUGFIX] Reject selector comprising only whitespace (#1433)
1 parent 18a6467 commit 515317d

File tree

3 files changed

+57
-9
lines changed

3 files changed

+57
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Please also have a look at our
2424

2525
### Fixed
2626

27+
- Reject selector comprising only whitespace (#1433)
2728
- Improve recovery parsing when a rogue `}` is encountered (#1425, #1426)
2829
- Parse comment(s) immediately preceding a selector (#1421)
2930
- Parse consecutive comments (#1421)

src/RuleSet/DeclarationBlock.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,11 @@ private static function parseSelector(ParserState $parserState, array &$comments
354354
throw new UnexpectedTokenException(')', $nextCharacter);
355355
}
356356

357-
return \implode('', $selectorParts);
357+
$selector = \trim(\implode('', $selectorParts));
358+
if ($selector === '') {
359+
throw new UnexpectedTokenException('selector', $nextCharacter, 'literal', $parserState->currentLine());
360+
}
361+
362+
return $selector;
358363
}
359364
}

tests/Unit/RuleSet/DeclarationBlockTest.php

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,20 +159,43 @@ public function parsesTwoCommaSeparatedSelectors(string $firstSelector, string $
159159
}
160160

161161
/**
162-
* @return array<non-empty-string, array{0: non-empty-string}>
162+
* @return array<non-empty-string, array{0: string, 1: non-empty-string}>
163163
*/
164-
public static function provideInvalidSelector(): array
164+
public static function provideInvalidSelectorAndExpectedExceptionMessage(): array
165165
{
166166
// TODO: the `parse` method consumes the first character without inspection,
167-
// so the 'lone' test strings are prefixed with a space.
167+
// so some of the test strings are prefixed with a space.
168168
return [
169-
'lone `(`' => [' ('],
170-
'lone `)`' => [' )'],
171-
'unclosed `(`' => [':not(#your-mug'],
172-
'extra `)`' => [':not(#your-mug))'],
169+
'no selector' => [' ', 'Token “selector” (literal) not found. Got “{”. [line no: 1]'],
170+
'lone `(`' => [' (', 'Token “)” (literal) not found. Got “{”.'],
171+
'lone `)`' => [' )', 'Token “anything but” (literal) not found. Got “)”.'],
172+
'lone `,`' => [' ,', 'Token “selector” (literal) not found. Got “,”. [line no: 1]'],
173+
'unclosed `(`' => [':not(#your-mug', 'Token “)” (literal) not found. Got “{”.'],
174+
'extra `)`' => [':not(#your-mug))', 'Token “anything but” (literal) not found. Got “)”.'],
175+
'`,` missing left operand' => [' , a', 'Token “selector” (literal) not found. Got “,”. [line no: 1]'],
176+
'`,` missing right operand' => ['a,', 'Token “selector” (literal) not found. Got “{”. [line no: 1]'],
173177
];
174178
}
175179

180+
/**
181+
* @return array<non-empty-string, array{0: string}>
182+
*/
183+
public static function provideInvalidSelector(): array
184+
{
185+
// Re-use the set of invalid selectors, but remove the expected exception message for tests that don't need it.
186+
return \array_map(
187+
/**
188+
* @param array{0: string, 1: non-empty-string}
189+
*
190+
* @return array<{0: string}>
191+
*/
192+
static function (array $testData): array {
193+
return [$testData[0]];
194+
},
195+
self::provideInvalidSelectorAndExpectedExceptionMessage()
196+
);
197+
}
198+
176199
/**
177200
* @test
178201
*
@@ -192,6 +215,25 @@ public function parseSkipsBlockWithInvalidSelector(string $selector): void
192215
self::assertTrue($parserState->comes($nextCss));
193216
}
194217

218+
/**
219+
* @test
220+
*
221+
* @param non-empty-string $expectedExceptionMessage
222+
*
223+
* @dataProvider provideInvalidSelectorAndExpectedExceptionMessage
224+
*/
225+
public function parseInStrictModeThrowsExceptionWithInvalidSelector(
226+
string $selector,
227+
string $expectedExceptionMessage
228+
): void {
229+
$this->expectException(UnexpectedTokenException::class);
230+
$this->expectExceptionMessage($expectedExceptionMessage);
231+
232+
$parserState = new ParserState($selector . ' {}', Settings::create()->beStrict());
233+
234+
$subject = DeclarationBlock::parse($parserState);
235+
}
236+
195237
/**
196238
* @return array<non-empty-string, array{0: non-empty-string}>
197239
*/
@@ -436,7 +478,7 @@ public static function provideInvalidStandaloneSelector(): array
436478
public function setSelectorsThrowsExceptionWithInvalidSelector(string $selector): void
437479
{
438480
$this->expectException(UnexpectedTokenException::class);
439-
$this->expectExceptionMessageMatches('/^Selector\\(s\\) string is not valid. /');
481+
$this->expectExceptionMessageMatches('/^Selector\\(s\\) string is not valid./');
440482

441483
$subject = new DeclarationBlock();
442484

0 commit comments

Comments
 (0)