Skip to content

Commit 63aaeac

Browse files
committed
Merge branch '2.x'
2 parents 7cafee3 + 26859a8 commit 63aaeac

File tree

4 files changed

+43
-12
lines changed

4 files changed

+43
-12
lines changed

src/PHPStan/PregMatchFlags.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33
namespace Composer\Pcre\PHPStan;
44

55
use PHPStan\Analyser\Scope;
6+
use PHPStan\Type\ArrayType;
7+
use PHPStan\Type\Constant\ConstantArrayType;
68
use PHPStan\Type\Constant\ConstantIntegerType;
9+
use PHPStan\Type\IntersectionType;
710
use PHPStan\Type\TypeCombinator;
811
use PHPStan\Type\Type;
912
use PhpParser\Node\Arg;
1013
use PHPStan\Type\Php\RegexArrayShapeMatcher;
14+
use PHPStan\Type\TypeTraverser;
15+
use PHPStan\Type\UnionType;
1116

1217
final class PregMatchFlags
1318
{
@@ -34,4 +39,32 @@ static public function getType(?Arg $flagsArg, Scope $scope): ?Type
3439
}
3540
return TypeCombinator::union(...$internalFlagsTypes);
3641
}
42+
43+
static public function removeNullFromMatches(Type $matchesType): Type
44+
{
45+
return TypeTraverser::map($matchesType, static function (Type $type, callable $traverse): Type {
46+
if ($type instanceof UnionType || $type instanceof IntersectionType) {
47+
return $traverse($type);
48+
}
49+
50+
if ($type instanceof ConstantArrayType) {
51+
return new ConstantArrayType(
52+
$type->getKeyTypes(),
53+
array_map(static function (Type $valueType) use ($traverse): Type {
54+
return $traverse($valueType);
55+
}, $type->getValueTypes()),
56+
$type->getNextAutoIndexes(),
57+
[],
58+
$type->isList()
59+
);
60+
}
61+
62+
if ($type instanceof ArrayType) {
63+
return new ArrayType($type->getKeyType(), $traverse($type->getItemType()));
64+
}
65+
66+
return TypeCombinator::removeNull($type);
67+
});
68+
}
69+
3770
}

src/PHPStan/PregMatchTypeSpecifyingExtension.php

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,9 @@ public function specifyTypes(MethodReflection $methodReflection, StaticCall $nod
8282
}
8383

8484
if (
85-
in_array($methodReflection->getName(), ['matchStrictGroups', 'isMatchStrictGroups'], true)
86-
&& count($matchedType->getConstantArrays()) === 1
85+
in_array($methodReflection->getName(), ['matchStrictGroups', 'isMatchStrictGroups', 'matchAllStrictGroups', 'isMatchAllStrictGroups'], true)
8786
) {
88-
$matchedType = $matchedType->getConstantArrays()[0];
89-
$matchedType = new ConstantArrayType(
90-
$matchedType->getKeyTypes(),
91-
array_map(static function (Type $valueType): Type {
92-
return TypeCombinator::removeNull($valueType);
93-
}, $matchedType->getValueTypes()),
94-
$matchedType->getNextAutoIndexes(),
95-
[],
96-
$matchedType->isList()
97-
);
87+
$matchedType = PregMatchFlags::removeNullFromMatches($matchedType);
9888
}
9989

10090
$overwrite = false;

tests/PHPStanTests/UnsafeStrictGroupsCallRuleTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ public function testRule(): void
4444
'The isMatchStrictGroups call is potentially unsafe as $matches\' type could not be inferred.',
4545
86,
4646
],
47+
[
48+
'The isMatchAllStrictGroups call is unsafe as match groups "test", "1" are optional and may be null.',
49+
114
50+
]
4751
]);
4852
}
4953

tests/PHPStanTests/nsrt/preg-match.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ function doMatchAllStrictGroups(string $s): void
110110
assertType('array{}', $matches);
111111
}
112112
assertType('array{}|array{0: list<string>, test: list<non-empty-string>, 1: list<non-empty-string>}', $matches);
113+
114+
if (Preg::isMatchAllStrictGroups('/Price: (?<test>£|€)?\d+/', $s, $matches)) {
115+
assertType('array{0: list<string>, test: list<non-empty-string>, 1: list<non-empty-string>}', $matches);
116+
}
113117
}
114118

115119
// disabled until https://github.com/phpstan/phpstan-src/pull/3185 can be resolved

0 commit comments

Comments
 (0)