Skip to content

Commit

Permalink
Pure cont'd
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Apr 3, 2024
1 parent 3ef4883 commit d7579c4
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,14 @@ private function processStmtNode(
return;
}
if ($node instanceof PropertyAssignNode) {
if (
$node->getPropertyFetch() instanceof Expr\PropertyFetch
&& $scope->getFunction() instanceof PhpMethodFromParserNodeReflection
&& $scope->getFunction()->getDeclaringClass()->hasConstructor()
&& $scope->getFunction()->getDeclaringClass()->getConstructor()->getName() === $scope->getFunction()->getName()
) {
return;
}
$methodImpurePoints[] = new ImpurePoint(
$scope,
$node,
Expand Down Expand Up @@ -2934,6 +2942,27 @@ static function (): void {
$impurePoints[] = new ImpurePoint($scope, $expr, 'print', 'print', true);
$hasYield = $result->hasYield();

$scope = $result->getScope();
} elseif ($expr instanceof Cast\String_) {
$result = $this->processExprNode($stmt, $expr->expr, $scope, $nodeCallback, $context->enterDeep());
$throwPoints = $result->getThrowPoints();
$impurePoints = $result->getImpurePoints();
$hasYield = $result->hasYield();

$exprType = $scope->getType($expr->expr);
$toStringMethod = $scope->getMethodReflection($exprType, '__toString');
if ($toStringMethod !== null) {
if (!$toStringMethod->hasSideEffects()->no()) {
$impurePoints[] = new ImpurePoint(
$scope,
$expr,
'methodCall',
sprintf('call to method %s::%s()', $toStringMethod->getDeclaringClass()->getDisplayName(), $toStringMethod->getName()),
$toStringMethod->isPure()->no(),
);
}
}

$scope = $result->getScope();
} elseif (
$expr instanceof Expr\BitwiseNot
Expand Down
27 changes: 27 additions & 0 deletions tests/PHPStan/Rules/Pure/PureMethodRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use const PHP_VERSION_ID;

/**
* @extends RuleTestCase<PureMethodRule>
Expand Down Expand Up @@ -115,6 +116,32 @@ public function testRule(): void
'Impure assign to superglobal variable in pure method PureMethod\ClassWithVoidMethods::purePostGetAssign().',
231,
],
[
'Possibly impure call to method PureMethod\MaybePureMagicMethods::__toString() in pure method PureMethod\TestMagicMethods::doFoo().',
295,
],
[
'Impure call to method PureMethod\ImpureMagicMethods::__toString() in pure method PureMethod\TestMagicMethods::doFoo().',
296,
],
]);
}

public function testPureConstructor(): void
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Test requires PHP 8.0.');
}

$this->analyse([__DIR__ . '/data/pure-constructor.php'], [
[
'Impure property assignment in pure method PureConstructor\Foo::__construct().',
19,
],
[
'Method PureConstructor\Bar::__construct() is marked as impure but does not have any side effects.',
30,
],
]);
}

Expand Down
38 changes: 38 additions & 0 deletions tests/PHPStan/Rules/Pure/data/pure-constructor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php // lint >= 8.0

namespace PureConstructor;

class Foo
{

private string $prop;

public static $staticProp = 1;

/** @phpstan-pure */
public function __construct(
public int $test,
string $prop,
)
{
$this->prop = $prop;
self::$staticProp++;
}

}

class Bar
{

private string $prop;

/** @phpstan-impure */
public function __construct(
public int $test,
string $prop,
)
{
$this->prop = $prop;
}

}
63 changes: 63 additions & 0 deletions tests/PHPStan/Rules/Pure/data/pure-method.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,66 @@ public function purePostGetAssign(array $post = [], array $get = []): int
}

}

class NoMagicMethods
{

}

class PureMagicMethods
{

/**
* @phpstan-pure
*/
public function __toString(): string
{
return 'one';
}

}

class MaybePureMagicMethods
{

public function __toString(): string
{
return 'one';
}

}

class ImpureMagicMethods
{

/**
* @phpstan-impure
*/
public function __toString(): string
{
sleep(1);
return 'one';
}

}

class TestMagicMethods
{

/**
* @phpstan-pure
*/
public function doFoo(
NoMagicMethods $no,
PureMagicMethods $pure,
MaybePureMagicMethods $maybe,
ImpureMagicMethods $impure
)
{
(string) $no;
(string) $pure;
(string) $maybe;
(string) $impure;
}

}

0 comments on commit d7579c4

Please sign in to comment.