mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +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\IteratorToArrayReturnTypeProvider::class);
|
||||||
$this->registerClass(ReturnTypeProvider\ParseUrlReturnTypeProvider::class);
|
$this->registerClass(ReturnTypeProvider\ParseUrlReturnTypeProvider::class);
|
||||||
$this->registerClass(ReturnTypeProvider\StrReplaceReturnTypeProvider::class);
|
$this->registerClass(ReturnTypeProvider\StrReplaceReturnTypeProvider::class);
|
||||||
|
$this->registerClass(ReturnTypeProvider\StrTrReturnTypeProvider::class);
|
||||||
$this->registerClass(ReturnTypeProvider\VersionCompareReturnTypeProvider::class);
|
$this->registerClass(ReturnTypeProvider\VersionCompareReturnTypeProvider::class);
|
||||||
$this->registerClass(ReturnTypeProvider\MktimeReturnTypeProvider::class);
|
$this->registerClass(ReturnTypeProvider\MktimeReturnTypeProvider::class);
|
||||||
$this->registerClass(ReturnTypeProvider\ExplodeReturnTypeProvider::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 {}
|
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
|
* @psalm-pure
|
||||||
*
|
*
|
||||||
@ -610,6 +624,20 @@ function array_product(array $input) {}
|
|||||||
*/
|
*/
|
||||||
function strip_tags(string $str, ?string $allowable_tags = null) : string {}
|
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
|
* @psalm-pure
|
||||||
*
|
*
|
||||||
@ -661,6 +689,104 @@ function htmlspecialchars_decode(string $string, ?int $quote_style = null) : str
|
|||||||
*/
|
*/
|
||||||
function str_replace($search, $replace, $subject, &$count = null) {}
|
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
|
* @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> $replace
|
||||||
* @param string|array<string|int|float> $subject
|
* @param string|array<string|int|float> $subject
|
||||||
* @param int $count
|
* @param int $count
|
||||||
* @return ($subject is array ? array<string> : string)
|
* @return ($subject is array ? array<string>|null : string|null)
|
||||||
*
|
*
|
||||||
* @psalm-flow ($replace, $subject) -> return
|
* @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 callable(array<int, string>):string $replace
|
||||||
* @param string|array<string|int|float> $subject
|
* @param string|array<string|int|float> $subject
|
||||||
* @param int $count
|
* @param int $count
|
||||||
* @return ($subject is array ? array<string> : string)
|
* @return ($subject is array ? array<string>|null : string|null)
|
||||||
*
|
*
|
||||||
* @psalm-taint-specialize
|
* @psalm-taint-specialize
|
||||||
* @psalm-flow ($subject) -> return
|
* @psalm-flow ($subject) -> return
|
||||||
@ -761,6 +887,23 @@ function preg_quote(string $str, ?string $delimiter = null) : string {}
|
|||||||
*/
|
*/
|
||||||
function sprintf(string $format, ...$args) : 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
|
* @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})
|
* @return ($input is non-empty-string ? non-empty-list<string> : non-empty-list<string>|array{null})
|
||||||
* @psalm-pure
|
* @psalm-pure
|
||||||
|
*
|
||||||
|
* @psalm-flow ($input) -> return
|
||||||
*/
|
*/
|
||||||
function str_getcsv(string $input, string $delimiter = ',', string $enclosure = '"', string $escape = '\\\\')
|
function str_getcsv(string $input, string $delimiter = ',', string $enclosure = '"', string $escape = '\\\\')
|
||||||
{
|
{
|
||||||
|
@ -557,6 +557,11 @@ class TaintTest extends TestCase
|
|||||||
|
|
||||||
echo $file;'
|
echo $file;'
|
||||||
],
|
],
|
||||||
|
'strTrNotTainted' => [
|
||||||
|
'<?php
|
||||||
|
$input = strtr(\'data\', \'data\', \'data\');
|
||||||
|
setcookie($input, \'value\');',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1862,6 +1867,12 @@ class TaintTest extends TestCase
|
|||||||
echo $a->getTaint();',
|
echo $a->getTaint();',
|
||||||
'error_message' => 'TaintedHtml',
|
'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.
|
// 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.
|
// 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