1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Merge pull request #7027 from rarila/issue-6914

This commit is contained in:
Bruce Weirdan 2021-11-30 23:34:00 +02:00 committed by GitHub
commit 28c4f86993
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 85 additions and 17 deletions

View File

@ -10273,7 +10273,7 @@ return [
'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'],

View File

@ -14454,7 +14454,7 @@ return [
'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'],

View File

@ -259,14 +259,14 @@ class CodeLocation
}
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]);
}
}

View File

@ -635,7 +635,7 @@ class ArgumentsAnalyzer
}
}
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);
}

View File

@ -325,7 +325,7 @@ class ClassLikeDocblockParser
$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);

View File

@ -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
*

View File

@ -1282,6 +1282,13 @@ class FunctionCallTest extends TestCase
takesInt(preg_match("{foo}", "foo"));',
],
'pregMatch2' => [
'<?php
$r = preg_match("{foo}", "foo");',
'assertions' => [
'$r===' => '0|1|false',
],
],
'pregMatchWithMatches' => [
'<?php
/** @param string[] $matches */
@ -1291,6 +1298,14 @@ class FunctionCallTest extends TestCase
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 */
@ -1300,18 +1315,46 @@ class FunctionCallTest extends TestCase
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 {
@ -1449,7 +1492,10 @@ class FunctionCallTest extends TestCase
],
'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);
}

View File

@ -35,14 +35,18 @@ trait ValidCodeAnalysisTestTrait
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.');