Skip to content

Commit

Permalink
RegexArrayShapeMatcher - Fix matching literal dot character
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm authored Sep 16, 2024
1 parent b3c25b8 commit 9437056
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 5 deletions.
12 changes: 7 additions & 5 deletions src/Type/Regex/RegexGroupParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -445,14 +445,14 @@ private function walkGroupAst(
foreach ($children as $child) {
$oldLiterals = $onlyLiterals;

$this->getLiteralValue($child, $oldLiterals, true, $patternModifiers);
$this->getLiteralValue($child, $oldLiterals, true, $patternModifiers, true);
foreach ($oldLiterals ?? [] as $oldLiteral) {
$newLiterals[] = $oldLiteral;
}
}
$onlyLiterals = $newLiterals;
} elseif ($ast->getId() === 'token') {
$literalValue = $this->getLiteralValue($ast, $onlyLiterals, !$inClass, $patternModifiers);
$literalValue = $this->getLiteralValue($ast, $onlyLiterals, !$inClass, $patternModifiers, false);
if ($literalValue !== null) {
if (Strings::match($literalValue, '/^\d+$/') === null) {
$isNumeric = TrinaryLogic::createNo();
Expand Down Expand Up @@ -508,7 +508,7 @@ private function isMaybeEmptyNode(TreeNode $node, string $patternModifiers, bool
}
}

$literal = $this->getLiteralValue($node, $onlyLiterals, false, $patternModifiers);
$literal = $this->getLiteralValue($node, $onlyLiterals, false, $patternModifiers, false);
if ($literal !== null) {
if ($literal !== '' && $literal !== '0') {
$isNonFalsy = true;
Expand All @@ -528,7 +528,7 @@ private function isMaybeEmptyNode(TreeNode $node, string $patternModifiers, bool
/**
* @param array<string>|null $onlyLiterals
*/
private function getLiteralValue(TreeNode $node, ?array &$onlyLiterals, bool $appendLiterals, string $patternModifiers): ?string
private function getLiteralValue(TreeNode $node, ?array &$onlyLiterals, bool $appendLiterals, string $patternModifiers, bool $inCharacterClass): ?string
{
if ($node->getId() !== 'token') {
return null;
Expand All @@ -551,15 +551,17 @@ private function getLiteralValue(TreeNode $node, ?array &$onlyLiterals, bool $ap
return null;
}

$isEscaped = false;
if (strlen($value) > 1 && $value[0] === '\\') {
$value = substr($value, 1) ?: '';
$isEscaped = true;
}

if (
$appendLiterals
&& in_array($token, ['literal', 'range', 'class_', '_class_literal'], true)
&& $onlyLiterals !== null
&& !in_array($value, ['.'], true)
&& (!in_array($value, ['.'], true) || $isEscaped || $inCharacterClass)
) {
if ($onlyLiterals === []) {
$onlyLiterals = [$value];
Expand Down
42 changes: 42 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-11699.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types = 1);

namespace Bug11699;

use function PHPStan\Testing\assertType;

function doFoo():void {
$string = 'Foo.bar';
$match = [];
$result = preg_match('~(?<AB>[\~,\?\.])~', $string, $match);
if ($result === 1) {
assertType("','|'.'|'?'|'~'", $match['AB']);
}
}

function doFoo1():void {
$string = 'Foo.bar';
$match = [];
$result = preg_match('~(?<AB>[\~,\?.])~', $string, $match); // dot in character class does not need to be escaped
if ($result === 1) {
assertType("','|'.'|'?'|'~'", $match['AB']);
}
}

function doFoo2():void {
$string = 'Foo.bar';
$match = [];
$result = preg_match('~(?<AB>.)~', $string, $match);
if ($result === 1) {
assertType("non-empty-string", $match['AB']);
}
}


function doFoo3():void {
$string = 'Foo.bar';
$match = [];
$result = preg_match('~(?<AB>\.)~', $string, $match);
if ($result === 1) {
assertType("'.'", $match['AB']);
}
}

0 comments on commit 9437056

Please sign in to comment.