diff --git a/CHANGELOG.md b/CHANGELOG.md index ffbd588f..caf9aa4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Please also have a look at our ### Fixed +- Improve recovery parsing when a rogue `}` is encountered (#1425) - Parse comment(s) immediately preceding a selector (#1421) - Parse consecutive comments (#1421) - Support attribute selectors with values containing commas in diff --git a/src/RuleSet/DeclarationBlock.php b/src/RuleSet/DeclarationBlock.php index 456dc33b..7aa81f27 100644 --- a/src/RuleSet/DeclarationBlock.php +++ b/src/RuleSet/DeclarationBlock.php @@ -74,7 +74,7 @@ public static function parse(ParserState $parserState, ?CSSList $list = null): ? } } catch (UnexpectedTokenException $e) { if ($parserState->getSettings()->usesLenientParsing()) { - if (!$parserState->comes('}')) { + if (!$parserState->consumeIfComes('}')) { $parserState->consumeUntil('}', false, true); } return null; diff --git a/tests/ParserTest.php b/tests/ParserTest.php index 5b360bda..ffe27818 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -718,6 +718,7 @@ public function invalidSelectorsInFile(): void $document = self::parsedStructureForFile('invalid-selectors', Settings::create()->withMultibyteSupport(true)); $expected = '@keyframes mymove {from {top: 0px;}} #test {color: white;background: green;} +#test {display: block;background: red;color: white;} #test {display: block;background: white;color: black;}'; self::assertSame($expected, $document->render()); @@ -727,6 +728,7 @@ public function invalidSelectorsInFile(): void .super-menu > li:last-of-type {border-right-width: 0;} html[dir="rtl"] .super-menu > li:first-of-type {border-left-width: 1px;border-right-width: 0;} html[dir="rtl"] .super-menu > li:last-of-type {border-left-width: 0;}} +.super-menu.menu-floated {border-right-width: 1px;border-left-width: 1px;border-color: #5a4242;border-style: dotted;} body {background-color: red;}'; self::assertSame($expected, $document->render()); } @@ -740,15 +742,6 @@ public function selectorEscapesInFile(): void $expected = '#\\# {color: red;} .col-sm-1\\/5 {width: 20%;}'; self::assertSame($expected, $document->render()); - - $document = self::parsedStructureForFile('invalid-selectors-2', Settings::create()->withMultibyteSupport(true)); - $expected = '@media only screen and (max-width: 1215px) {.breadcrumb {padding-left: 10px;} - .super-menu > li:first-of-type {border-left-width: 0;} - .super-menu > li:last-of-type {border-right-width: 0;} - html[dir="rtl"] .super-menu > li:first-of-type {border-left-width: 1px;border-right-width: 0;} - html[dir="rtl"] .super-menu > li:last-of-type {border-left-width: 0;}} -body {background-color: red;}'; - self::assertSame($expected, $document->render()); } /** diff --git a/tests/Unit/RuleSet/DeclarationBlockTest.php b/tests/Unit/RuleSet/DeclarationBlockTest.php index 685112d5..caddbd13 100644 --- a/tests/Unit/RuleSet/DeclarationBlockTest.php +++ b/tests/Unit/RuleSet/DeclarationBlockTest.php @@ -192,6 +192,45 @@ public function parseSkipsBlockWithInvalidSelector(string $selector): void self::assertTrue($parserState->comes($nextCss)); } + /** + * @return array + */ + public static function provideClosingBrace(): array + { + return [ + 'as is' => ['}'], + 'with space before' => [' }'], + 'with newline before' => ["\n}"], + ]; + } + + /** + * @return DataProvider + */ + public static function provideInvalidSelectorAndClosingBrace(): DataProvider + { + return DataProvider::cross(self::provideInvalidSelector(), self::provideClosingBrace()); + } + + /** + * TODO: It's probably not the responsibility of `DeclarationBlock` to deal with this. + * + * @test + * + * @param non-empty-string $selector + * @param non-empty-string $closingBrace + * + * @dataProvider provideInvalidSelectorAndClosingBrace + */ + public function parseConsumesClosingBraceAfterInvalidSelector(string $selector, string $closingBrace): void + { + $parserState = new ParserState($selector . $closingBrace, Settings::create()); + + DeclarationBlock::parse($parserState); + + self::assertTrue($parserState->isEnd()); + } + /** * @return array */