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

Fix #4128 - improve understanding of preg_match_all

This commit is contained in:
Brown 2020-09-04 18:10:14 -04:00
parent 22fe7458d8
commit 4d82d3ddad
6 changed files with 74 additions and 7 deletions

View File

@ -1518,10 +1518,10 @@ class ClassAnalyzer extends ClassLikeAnalyzer
),
$storage->suppressed_issues + $this->getSuppressedIssues()
)) {
return false;
// fall through
}
return null;
return false;
} else {
if (!$codebase->traitHasCorrectCase($fq_trait_name)) {
if (IssueBuffer::accepts(

View File

@ -58,6 +58,7 @@ class ArgumentAnalyzer
?FunctionLikeParameter $function_param,
int $argument_offset,
PhpParser\Node\Arg $arg,
?Type\Union $arg_value_type,
Context $context,
array $class_generic_params,
?TemplateResult $template_result,
@ -66,8 +67,6 @@ class ArgumentAnalyzer
) {
$codebase = $statements_analyzer->getCodebase();
$arg_value_type = $statements_analyzer->node_data->getType($arg->value);
if (!$arg_value_type) {
if ($function_param && !$function_param->by_ref) {
if (!$context->collect_initializations

View File

@ -10,6 +10,7 @@ use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Internal\Codebase\InternalCallMapHandler;
use Psalm\Internal\Stubs\Generator\StubsGenerator;
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
use Psalm\Internal\MethodIdentifier;
use Psalm\Internal\Type\TemplateResult;
@ -92,6 +93,8 @@ class ArgumentsAnalyzer
$args = array_reverse($args, true);
}
$codebase = $statements_analyzer->getCodebase();
foreach ($args as $argument_offset => $arg) {
if ($function_params === null) {
if (self::evaluateAribitraryParam(
@ -155,8 +158,6 @@ class ArgumentsAnalyzer
$toggled_class_exists = true;
}
$codebase = $statements_analyzer->getCodebase();
if (($arg->value instanceof PhpParser\Node\Expr\Closure
|| $arg->value instanceof PhpParser\Node\Expr\ArrowFunction)
&& $template_result
@ -569,6 +570,35 @@ class ArgumentsAnalyzer
$function_param_count = count($function_params);
if (count($function_params) > count($args) && !$has_packed_var) {
for ($i = count($args); $i < count($function_params); $i++) {
if ($function_params[$i]->default_type
&& $function_params[$i]->type
&& $function_params[$i]->type->hasTemplate()
&& $function_params[$i]->default_type->hasLiteralValue()
) {
ArgumentAnalyzer::checkArgumentMatches(
$statements_analyzer,
$cased_method_id,
$self_fq_class_name,
$static_fq_class_name,
$code_location,
$function_params[$i],
$i,
new PhpParser\Node\Arg(
StubsGenerator::getExpressionFromType($function_params[$i]->default_type)
),
$function_params[$i]->default_type,
$context,
$class_generic_params,
$template_result,
$function_storage ? $function_storage->specialize_call : true,
$in_call_map
);
}
}
}
foreach ($args as $argument_offset => $arg) {
$function_param = $function_param_count > $argument_offset
? $function_params[$argument_offset]
@ -594,8 +624,10 @@ class ArgumentsAnalyzer
}
}
$arg_value_type = $statements_analyzer->node_data->getType($arg->value);
if ($method_id === 'compact'
&& ($arg_value_type = $statements_analyzer->node_data->getType($arg->value))
&& $arg_value_type
&& $arg_value_type->isSingleStringLiteral()
) {
$literal = $arg_value_type->getSingleStringLiteral();
@ -622,6 +654,7 @@ class ArgumentsAnalyzer
$function_param,
$argument_offset,
$arg,
$arg_value_type,
$context,
$class_generic_params,
$template_result,

View File

@ -689,6 +689,13 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
$functionlike_storage = array_pop($this->functionlike_storages);
if ($functionlike_storage->docblock_issues
&& (strpos($this->file_path, 'CoreGenericFunctions.phpstub')
|| strpos($this->file_path, 'CoreGenericClasses.phpstub'))
) {
throw new \UnexpectedValueException('Error with core stub file docblocks');
}
if ($functionlike_storage->has_visitor_issues) {
$this->file_storage->has_visitor_issues = true;
}
@ -2646,6 +2653,8 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
new CodeLocation($this->file_scanner, $stmt, null, true)
);
continue;
}

View File

@ -634,6 +634,19 @@ function preg_replace($search, $replace, $subject, int $limit = -1, &$count = nu
*/
function preg_replace_callback($search, $replace, $subject, int $limit = -1, &$count = null) {}
/**
* @psalm-pure
* @template TFlags as int
*
* @param string $pattern
* @param string $subject
* @param mixed $matches
* @param TFlags $flags
* @param-out (TFlags is 256 ? list<list<array{string, int}>> : list<list<string>>) $matches
* @return int
*/
function preg_match_all($pattern, $replace, &$matches, int $flags = 1) {}
/**
* @psalm-pure
*

View File

@ -1322,6 +1322,19 @@ class FunctionCallTest extends TestCase
if ($one && array_values($one) === array_values($two)) {}
}'
],
'pregMatchAll' => [
'<?php
/**
* @psalm-pure
*
* @return list<list<string>>
*/
function extractUsernames(string $input): array {
preg_match_all(\'/@[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}(?!\w)/\', $input, $matches);
return $matches;
}'
],
];
}