mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Add psalm-flow for string functions from sscanf to wordwrap (#4591)
* Add string functions from sscanf to wordwrap This should conclude all string functions from https://www.php.net/manual/en/book.strings.php Continuation of https://github.com/vimeo/psalm/pull/4576 Ref https://github.com/vimeo/psalm/issues/3636 * Add StrTrReturnTypeProvider * Fix psalm error * phpcs * Line length * Ignore false return on vsprintf Co-authored-by: Matthew Brown <github@muglug.com>
This commit is contained in:
parent
7edb8ef3f8
commit
4de2bf8f7f
@ -48,6 +48,7 @@ class FunctionReturnTypeProvider
|
||||
$this->registerClass(ReturnTypeProvider\IteratorToArrayReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\ParseUrlReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\StrReplaceReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\StrTrReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\VersionCompareReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\MktimeReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\ExplodeReturnTypeProvider::class);
|
||||
|
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace Psalm\Internal\Provider\ReturnTypeProvider;
|
||||
|
||||
use PhpParser;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Type;
|
||||
use Psalm\Internal\DataFlow\DataFlowNode;
|
||||
|
||||
class StrTrReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturnTypeProviderInterface
|
||||
{
|
||||
public static function getFunctionIds() : array
|
||||
{
|
||||
return [
|
||||
'strtr',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<PhpParser\Node\Arg> $call_args
|
||||
*/
|
||||
public static function getFunctionReturnType(
|
||||
StatementsSource $statements_source,
|
||||
string $function_id,
|
||||
array $call_args,
|
||||
Context $context,
|
||||
CodeLocation $code_location
|
||||
) : Type\Union {
|
||||
if (!$statements_source instanceof \Psalm\Internal\Analyzer\StatementsAnalyzer) {
|
||||
throw new \UnexpectedValueException();
|
||||
}
|
||||
|
||||
$type = Type::getString();
|
||||
|
||||
if ($statements_source->data_flow_graph
|
||||
&& !\in_array('TaintedInput', $statements_source->getSuppressedIssues())) {
|
||||
$function_return_sink = DataFlowNode::getForMethodReturn(
|
||||
$function_id,
|
||||
$function_id,
|
||||
null,
|
||||
$code_location
|
||||
);
|
||||
|
||||
$statements_source->data_flow_graph->addNode($function_return_sink);
|
||||
foreach ($call_args as $i => $_) {
|
||||
$function_param_sink = DataFlowNode::getForMethodArgument(
|
||||
$function_id,
|
||||
$function_id,
|
||||
$i,
|
||||
null,
|
||||
$code_location
|
||||
);
|
||||
|
||||
$statements_source->data_flow_graph->addNode($function_param_sink);
|
||||
|
||||
$statements_source->data_flow_graph->addPath(
|
||||
$function_param_sink,
|
||||
$function_return_sink,
|
||||
'arg'
|
||||
);
|
||||
}
|
||||
|
||||
$type->parent_nodes = [$function_return_sink->id => $function_return_sink];
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
}
|
@ -484,6 +484,20 @@ function strtolower(string $str) : string {}
|
||||
*/
|
||||
function strtoupper(string $str) : string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @param string|array<string> $string
|
||||
* @param string|array<string> $replacement
|
||||
* @param int|array<int> $start
|
||||
* @param null|int|array<int> $length
|
||||
*
|
||||
* @return ($string is array<string> ? array<string> : string)
|
||||
*
|
||||
* @psalm-flow ($string, $replacement) -> return
|
||||
*/
|
||||
function substr_replace($string, $replacement, $start, $length) {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
@ -610,6 +624,20 @@ function array_product(array $input) {}
|
||||
*/
|
||||
function strip_tags(string $str, ?string $allowable_tags = null) : string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($str) -> return
|
||||
*/
|
||||
function stripcslashes(string $str) : string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($str) -> return
|
||||
*/
|
||||
function stripslashes(string $str) : string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
@ -661,6 +689,104 @@ function htmlspecialchars_decode(string $string, ?int $quote_style = null) : str
|
||||
*/
|
||||
function str_replace($search, $replace, $subject, &$count = null) {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @param string|array<string|int|float> $search
|
||||
* @param string|array<string|int|float> $replace
|
||||
* @param string|array<string|int|float> $subject
|
||||
* @param int $count
|
||||
* @return ($subject is array ? array<string> : string)
|
||||
*
|
||||
* @psalm-flow ($replace, $subject) -> return
|
||||
*/
|
||||
function str_ireplace($search, $replace, $subject, &$count = null) {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($input, $pad_string) -> return
|
||||
*/
|
||||
function str_pad(string $input, int $pad_length, $pad_string = '', int $pad_type = STR_PAD_RIGHT): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($input) -> return
|
||||
*/
|
||||
function str_repeat(string $input, int $multiplier): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($str) -> return
|
||||
*/
|
||||
function str_rot13(string $str): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($str) -> return
|
||||
*/
|
||||
function str_shuffle(string $str): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
* @return array<string>
|
||||
*
|
||||
* @psalm-flow ($string) -> return
|
||||
*/
|
||||
function str_split(string $string, int $split_length = 1) {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($haystack) -> return
|
||||
*/
|
||||
function strstr(string $haystack, string $needle, bool $before_needle = false): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($haystack) -> return
|
||||
*/
|
||||
function stristr(string $haystack, string $needle, bool $before_needle = false): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($haystack) -> return
|
||||
*/
|
||||
function strchr(string $haystack, string $needle, bool $before_needle = false): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($haystack) -> return
|
||||
*/
|
||||
function strpbrk(string $haystack, string $char_list): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($haystack) -> return
|
||||
*/
|
||||
function strrchr(string $haystack, string $needle): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($string) -> return
|
||||
*/
|
||||
function strrev(string $string): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($string) -> return
|
||||
*/
|
||||
function strtok(string $str, string $token): string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
@ -681,7 +807,7 @@ function preg_filter($pattern, $replacement, $subject, int $limit = -1, &$count
|
||||
* @param string|array<string|int|float> $replace
|
||||
* @param string|array<string|int|float> $subject
|
||||
* @param int $count
|
||||
* @return ($subject is array ? array<string> : string)
|
||||
* @return ($subject is array ? array<string>|null : string|null)
|
||||
*
|
||||
* @psalm-flow ($replace, $subject) -> return
|
||||
*/
|
||||
@ -692,7 +818,7 @@ function preg_replace($search, $replace, $subject, int $limit = -1, &$count = nu
|
||||
* @param callable(array<int, string>):string $replace
|
||||
* @param string|array<string|int|float> $subject
|
||||
* @param int $count
|
||||
* @return ($subject is array ? array<string> : string)
|
||||
* @return ($subject is array ? array<string>|null : string|null)
|
||||
*
|
||||
* @psalm-taint-specialize
|
||||
* @psalm-flow ($subject) -> return
|
||||
@ -761,6 +887,23 @@ function preg_quote(string $str, ?string $delimiter = null) : string {}
|
||||
*/
|
||||
function sprintf(string $format, ...$args) : string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
* @return string|false
|
||||
* @psalm-ignore-falsable-return
|
||||
*
|
||||
* @psalm-flow ($format, $args) -> return
|
||||
*/
|
||||
function vsprintf(string $format, array $args) {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
* @return string
|
||||
*
|
||||
* @psalm-flow ($str) -> return
|
||||
*/
|
||||
function wordwrap(string $str, int $width = 75, string $break = "\n", bool $cut = false) : string {}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*
|
||||
@ -802,6 +945,8 @@ function strval ($var): string {}
|
||||
/**
|
||||
* @return ($input is non-empty-string ? non-empty-list<string> : non-empty-list<string>|array{null})
|
||||
* @psalm-pure
|
||||
*
|
||||
* @psalm-flow ($input) -> return
|
||||
*/
|
||||
function str_getcsv(string $input, string $delimiter = ',', string $enclosure = '"', string $escape = '\\\\')
|
||||
{
|
||||
|
@ -557,6 +557,11 @@ class TaintTest extends TestCase
|
||||
|
||||
echo $file;'
|
||||
],
|
||||
'strTrNotTainted' => [
|
||||
'<?php
|
||||
$input = strtr(\'data\', \'data\', \'data\');
|
||||
setcookie($input, \'value\');',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -1862,6 +1867,12 @@ class TaintTest extends TestCase
|
||||
echo $a->getTaint();',
|
||||
'error_message' => 'TaintedHtml',
|
||||
],
|
||||
'strTrReturnTypeTaint' => [
|
||||
'<?php
|
||||
$input = strtr(\'data\', $_GET[\'taint\'], \'data\');
|
||||
setcookie($input, \'value\');',
|
||||
'error_message' => 'TaintedCookie',
|
||||
],
|
||||
/*
|
||||
// TODO: Stubs do not support this type of inference even with $this->message = $message.
|
||||
// Most uses of getMessage() would be with caught exceptions, so this is not representative of real code.
|
||||
|
Loading…
Reference in New Issue
Block a user