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:
parent
22fe7458d8
commit
4d82d3ddad
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user