Skip to content

Commit

Permalink
Merge pull request #7027 from rarila/issue-6914
Browse files Browse the repository at this point in the history
  • Loading branch information
weirdan committed Nov 30, 2021
2 parents 295d5d9 + 98b0b05 commit 28c4f86
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 17 deletions.
2 changes: 1 addition & 1 deletion dictionaries/CallMap.php
Expand Up @@ -10273,7 +10273,7 @@
'preg_filter' => ['null|string|string[]', 'pattern'=>'mixed', 'replacement'=>'mixed', 'subject'=>'mixed', 'limit='=>'int', '&w_count='=>'int'],
'preg_grep' => ['array|false', 'pattern'=>'string', 'array'=>'array', 'flags='=>'int'],
'preg_last_error' => ['int'],
'preg_match' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_matches='=>'string[]', 'flags='=>'0|', 'offset='=>'int'],
'preg_match' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_matches='=>'string[]', 'flags='=>'0', 'offset='=>'int'],
'preg_match\'1' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_matches='=>'array', 'flags='=>'int', 'offset='=>'int'],
'preg_match_all' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_matches='=>'array', 'flags='=>'int', 'offset='=>'int'],
'preg_quote' => ['string', 'str'=>'string', 'delimiter='=>'string'],
Expand Down
2 changes: 1 addition & 1 deletion dictionaries/CallMap_historical.php
Expand Up @@ -14454,7 +14454,7 @@
'preg_filter' => ['null|string|string[]', 'pattern'=>'mixed', 'replacement'=>'mixed', 'subject'=>'mixed', 'limit='=>'int', '&w_count='=>'int'],
'preg_grep' => ['array|false', 'pattern'=>'string', 'array'=>'array', 'flags='=>'int'],
'preg_last_error' => ['int'],
'preg_match' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_matches='=>'string[]', 'flags='=>'0|', 'offset='=>'int'],
'preg_match' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_matches='=>'string[]', 'flags='=>'0', 'offset='=>'int'],
'preg_match\'1' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_matches='=>'array', 'flags='=>'int', 'offset='=>'int'],
'preg_match_all' => ['int|false', 'pattern'=>'string', 'subject'=>'string', '&w_matches='=>'array', 'flags='=>'int', 'offset='=>'int'],
'preg_quote' => ['string', 'str'=>'string', 'delimiter='=>'string'],
Expand Down
6 changes: 3 additions & 3 deletions src/Psalm/CodeLocation.php
Expand Up @@ -259,14 +259,14 @@ private function calculateRealLocation(): void
}

if (preg_match($regex, $preview_snippet, $matches, PREG_OFFSET_CAPTURE)) {
if (!isset($matches[1]) || (int)$matches[1][1] === -1) {
if (!isset($matches[1]) || $matches[1][1] === -1) {
throw new \LogicException(
"Failed to match anything to 1st capturing group, "
. "or regex doesn't contain 1st capturing group, regex type " . $this->regex_type
);
}
$this->selection_start = $this->selection_start + (int)$matches[1][1];
$this->selection_end = $this->selection_start + strlen((string)$matches[1][0]);
$this->selection_start = $this->selection_start + $matches[1][1];
$this->selection_end = $this->selection_start + strlen($matches[1][0]);
}
}

Expand Down
Expand Up @@ -635,7 +635,7 @@ public static function checkArgumentsMatch(
}
}

if ($method_id === 'preg_match_all' && count($args) > 3) {
if (($method_id === 'preg_match_all' || $method_id === 'preg_match') && count($args) > 3) {
$args = array_reverse($args, true);
}

Expand Down
Expand Up @@ -325,7 +325,7 @@ public static function parse(
$end_of_method_regex = '/(?<!array\()\) ?(\: ?(\??[\\\\a-zA-Z0-9_]+))?/';

if (preg_match($end_of_method_regex, $method_entry, $matches, PREG_OFFSET_CAPTURE)) {
$method_entry = substr($method_entry, 0, (int) $matches[0][1] + strlen((string) $matches[0][0]));
$method_entry = substr($method_entry, 0, $matches[0][1] + strlen($matches[0][0]));
}

$method_entry = str_replace([', ', '( '], [',', '('], $method_entry);
Expand Down
18 changes: 18 additions & 0 deletions stubs/CoreGenericFunctions.phpstub
Expand Up @@ -963,6 +963,24 @@ function preg_replace_callback($pattern, $callback, $subject, int $limit = -1, &
*/
function preg_match_all($pattern, $subject, &$matches = [], int $flags = 1, int $offset = 0) {}

/**
* @psalm-pure
* @template TFlags as int-mask<0, 256, 512>
*
* @param string $pattern
* @param string $subject
* @param mixed $matches
* @param TFlags $flags
* @param-out (TFlags is 256 ? array<array-key, array{string, 0|positive-int}|array{'', -1}> :
* TFlags is 512 ? array<array-key, string|null> :
* TFlags is 768 ? array<array-key, array{string, 0|positive-int}|array{null, -1}> :
* array<array-key, string>
* ) $matches
* @return 1|0|false
* @psalm-ignore-falsable-return
*/
function preg_match($pattern, $subject, &$matches = [], int $flags = 0, int $offset = 0) {}

/**
* @psalm-pure
*
Expand Down
56 changes: 51 additions & 5 deletions tests/FunctionCallTest.php
Expand Up @@ -1282,6 +1282,13 @@ function takesInt(int $i) : void {}
takesInt(preg_match("{foo}", "foo"));',
],
'pregMatch2' => [
'<?php
$r = preg_match("{foo}", "foo");',
'assertions' => [
'$r===' => '0|1|false',
],
],
'pregMatchWithMatches' => [
'<?php
/** @param string[] $matches */
Expand All @@ -1291,6 +1298,14 @@ function takesMatches(array $matches) : void {}
takesMatches($matches);',
],
'pregMatchWithMatches2' => [
'<?php
$r = preg_match("{foo}", "foo", $matches);',
'assertions' => [
'$r===' => '0|1|false',
'$matches===' => 'array<array-key, string>',
],
],
'pregMatchWithOffset' => [
'<?php
/** @param string[] $matches */
Expand All @@ -1300,18 +1315,46 @@ function takesMatches(array $matches) : void {}
takesMatches($matches);',
],
'pregMatchWithOffset2' => [
'<?php
$r = preg_match("{foo}", "foo", $matches, 0, 10);',
'assertions' => [
'$r===' => '0|1|false',
'$matches===' => 'array<array-key, string>',
],
],
'pregMatchWithFlags' => [
'<?php
function takesInt(int $i) : void {}
if (preg_match("{foo}", "this is foo", $matches, PREG_OFFSET_CAPTURE)) {
/**
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArgument
*/
takesInt($matches[0][1]);
}',
],
'pregMatchWithFlagOffsetCapture' => [
'<?php
$r = preg_match("{foo}", "foo", $matches, PREG_OFFSET_CAPTURE);',
'assertions' => [
'$r===' => '0|1|false',
'$matches===' => 'array<array-key, array{string, int}>',
],
],
'PHP72-pregMatchWithFlagUnmatchedAsNull' => [
'<?php
$r = preg_match("{foo}", "foo", $matches, PREG_UNMATCHED_AS_NULL);',
'assertions' => [
'$r===' => '0|1|false',
'$matches===' => 'array<array-key, null|string>',
],
],
'PHP72-pregMatchWithFlagOffsetCaptureAndUnmatchedAsNull' => [
'<?php
$r = preg_match("{foo}", "foo", $matches, PREG_OFFSET_CAPTURE | PREG_UNMATCHED_AS_NULL);',
'assertions' => [
'$r===' => '0|1|false',
'$matches===' => 'array<array-key, array{null|string, int}>',
],
],
'pregReplaceCallback' => [
'<?php
function foo(string $s) : string {
Expand Down Expand Up @@ -1449,7 +1492,10 @@ function test() : void {
],
'writeArgsAllowed' => [
'<?php
/** @return false|int */
/**
* @param 0|256|512|768 $flags
* @return false|int
*/
function safeMatch(string $pattern, string $subject, ?array $matches = null, int $flags = 0) {
return \preg_match($pattern, $subject, $matches, $flags);
}
Expand Down
14 changes: 9 additions & 5 deletions tests/Traits/ValidCodeAnalysisTestTrait.php
Expand Up @@ -35,14 +35,18 @@ public function testValidCode(
string $php_version = '7.3'
): void {
$test_name = $this->getTestName();
if (strpos($test_name, 'PHP73-') !== false) {
if (version_compare(PHP_VERSION, '7.3.0', '<')) {
$this->markTestSkipped('Test case requires PHP 7.3.');
}
} elseif (strpos($test_name, 'PHP71-') !== false) {
if (strpos($test_name, 'PHP71-') !== false) {
if (version_compare(PHP_VERSION, '7.1.0', '<')) {
$this->markTestSkipped('Test case requires PHP 7.1.');
}
} elseif (strpos($test_name, 'PHP72-') !== false) {
if (version_compare(PHP_VERSION, '7.2.0', '<')) {
$this->markTestSkipped('Test case requires PHP 7.2.');
}
} elseif (strpos($test_name, 'PHP73-') !== false) {
if (version_compare(PHP_VERSION, '7.3.0', '<')) {
$this->markTestSkipped('Test case requires PHP 7.3.');
}
} elseif (strpos($test_name, 'PHP80-') !== false) {
if (version_compare(PHP_VERSION, '8.0.0', '<')) {
$this->markTestSkipped('Test case requires PHP 8.0.');
Expand Down

0 comments on commit 28c4f86

Please sign in to comment.