Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions tests/PHPStan/Rules/Pure/PureFunctionRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,57 @@ public function testPureUnlessCallableIsImpure(): void
]);
}

#[RequiresPhp('>= 8.0.0')]
public function testPureUnlessCallableIsImpureNamedArgs(): void
{
$this->analyse([__DIR__ . '/data/pure-unless-callable-is-impure-named-arg.php'], [
[
'Impure call to function PureUnlessCallableIsImpureNamedArg\myMap() in pure function PureUnlessCallableIsImpureNamedArg\pureCallingUserlandWithImpureCallbackByName().',
61,
],
[
'Impure echo in pure function PureUnlessCallableIsImpureNamedArg\pureCallingUserlandWithImpureCallbackByName().',
62,
],
[
'Possibly impure call to a callable in pure function PureUnlessCallableIsImpureNamedArg\pureCallingUserlandWithOpaqueCallbackByName().',
75,
],
[
'Possibly impure call to function PureUnlessCallableIsImpureNamedArg\myMap() in pure function PureUnlessCallableIsImpureNamedArg\pureCallingUserlandWithOpaqueCallbackByName().',
75,
],
[
'Impure call to function array_map() in pure function PureUnlessCallableIsImpureNamedArg\pureWithImpureCallbackByName().',
85,
],
[
'Impure echo in pure function PureUnlessCallableIsImpureNamedArg\pureWithImpureCallbackByName().',
86,
],
[
'Possibly impure call to a callable in pure function PureUnlessCallableIsImpureNamedArg\pureWithOpaqueCallbackByName().',
99,
],
[
'Possibly impure call to function array_map() in pure function PureUnlessCallableIsImpureNamedArg\pureWithOpaqueCallbackByName().',
99,
],
[
'Impure call to function PureUnlessCallableIsImpureNamedArg\myMap() in pure function PureUnlessCallableIsImpureNamedArg\pureCallingUserlandWithImpureCallbackShuffled().',
121,
],
[
'Impure echo in pure function PureUnlessCallableIsImpureNamedArg\pureCallingUserlandWithImpureCallbackShuffled().',
122,
],
[
'Impure call to method PureUnlessCallableIsImpureNamedArg\Mapper::map() in pure function PureUnlessCallableIsImpureNamedArg\pureCallingMethodWithImpureCallbackByName().',
134,
],
]);
}

#[RequiresPhp('>= 8.4.0')]
public function testPureUnlessCallableIsImpurePhp84(): void
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php // lint >= 8.0

declare(strict_types = 1);

namespace PureUnlessCallableIsImpureNamedArg;

/**
* @param callable(int): int $f
* @param array<int> $arr
* @return array<int>
* @pure-unless-callable-is-impure $f
*/
function myMap(callable $f, array $arr): array
{
$result = [];
foreach ($arr as $i => $v) {
$result[$i] = $f($v);
}

return $result;
}

class Mapper
{

/**
* @param callable(int): int $f
* @param array<int> $arr
* @return array<int>
* @pure-unless-callable-is-impure $f
*/
public function map(callable $f, array $arr): array
{
$result = [];
foreach ($arr as $i => $v) {
$result[$i] = $f($v);
}

return $result;
}

}

/**
* @param array<int> $arr
* @return array<int>
* @phpstan-pure
*/
function pureCallingUserlandWithPureCallbackByName(array $arr): array
{
return myMap(f: static fn (int $x): int => $x * 2, arr: $arr);
}

/**
* @param array<int> $arr
* @return array<int>
* @phpstan-pure
*/
function pureCallingUserlandWithImpureCallbackByName(array $arr): array
{
return myMap(f: static function (int $x): int {
echo $x;
return $x * 2;
}, arr: $arr);
}

/**
* @param array<int> $arr
* @param callable(int): int $cb
* @return array<int>
* @phpstan-pure
*/
function pureCallingUserlandWithOpaqueCallbackByName(array $arr, callable $cb): array
{
return myMap(f: $cb, arr: $arr);
}

/**
* @param array<int> $arr
* @return array<int>
* @phpstan-pure
*/
function pureWithImpureCallbackByName(array $arr): array
{
return array_map(callback: static function (int $x): int {
echo $x;
return $x * 2;
}, array: $arr);
}

/**
* @param array<int> $arr
* @param callable(int): int $cb
* @return array<int>
* @phpstan-pure
*/
function pureWithOpaqueCallbackByName(array $arr, callable $cb): array
{
return array_map(callback: $cb, array: $arr);
}

/**
* @param array<int> $arr
* @return array<int>
* @phpstan-pure
*/
function pureCallingUserlandWithPureCallbackShuffled(array $arr): array
{
// Arguments are passed in shuffled order using names, so name-based matching
// (not positional matching) determines which argument is judged for purity.
return myMap(arr: $arr, f: static fn (int $x): int => $x * 2);
}

/**
* @param array<int> $arr
* @return array<int>
* @phpstan-pure
*/
function pureCallingUserlandWithImpureCallbackShuffled(array $arr): array
{
return myMap(arr: $arr, f: static function (int $x): int {
echo $x;
return $x * 2;
});
}

/**
* @param array<int> $arr
* @return array<int>
* @phpstan-pure
*/
function pureCallingMethodWithImpureCallbackByName(Mapper $mapper, array $arr): array
{
return $mapper->map(f: static function (int $x): int {
echo $x;
return $x * 2;
}, arr: $arr);
}
Loading