Skip to content

Commit 5c3eed5

Browse files
Consider assert as side effect
1 parent c3babe1 commit 5c3eed5

10 files changed

+108
-0
lines changed

src/Rules/Functions/CallToFunctionStatementWithoutSideEffectsRule.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PHPStan\Rules\RuleErrorBuilder;
1212
use PHPStan\Type\NeverType;
1313
use PHPStan\Type\Type;
14+
use function count;
1415
use function in_array;
1516
use function sprintf;
1617

@@ -65,6 +66,10 @@ public function processNode(Node $node, Scope $scope): array
6566
}
6667

6768
$function = $this->reflectionProvider->getFunction($funcCall->name, $scope);
69+
if (count($function->getAsserts()->getAsserts()) > 0) {
70+
return [];
71+
}
72+
6873
$functionName = $function->getName();
6974
$functionHasSideEffects = !$function->hasSideEffects()->no();
7075

src/Rules/Methods/CallToConstructorStatementWithoutSideEffectsRule.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\Rules\Rule;
1111
use PHPStan\Rules\RuleErrorBuilder;
1212
use PHPStan\Type\NeverType;
13+
use function count;
1314
use function sprintf;
1415

1516
/**
@@ -57,6 +58,10 @@ public function processNode(Node $node, Scope $scope): array
5758
}
5859

5960
$constructor = $classReflection->getConstructor();
61+
if (count($constructor->getAsserts()->getAsserts()) > 0) {
62+
return [];
63+
}
64+
6065
$methodResult = $scope->getType($instantiation);
6166
if ($methodResult instanceof NeverType && $methodResult->isExplicit()) {
6267
return [];

src/Rules/Methods/CallToMethodStatementWithoutSideEffectsRule.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHPStan\Type\ErrorType;
1414
use PHPStan\Type\NeverType;
1515
use PHPStan\Type\Type;
16+
use function count;
1617
use function sprintf;
1718

1819
/**
@@ -69,6 +70,9 @@ public function processNode(Node $node, Scope $scope): array
6970
}
7071

7172
$method = $calledOnType->getMethod($methodName, $scope);
73+
if (count($method->getAsserts()->getAsserts()) > 0) {
74+
return [];
75+
}
7276

7377
return [
7478
RuleErrorBuilder::message(sprintf(

src/Rules/Methods/CallToStaticMethodStatementWithoutSideEffectsRule.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPStan\Type\NeverType;
1616
use PHPStan\Type\ObjectType;
1717
use PHPStan\Type\Type;
18+
use function count;
1819
use function sprintf;
1920
use function strtolower;
2021

@@ -87,6 +88,10 @@ public function processNode(Node $node, Scope $scope): array
8788
return [];
8889
}
8990

91+
if (count($method->getAsserts()->getAsserts()) > 0) {
92+
return [];
93+
}
94+
9095
$methodResult = $scope->getType($staticCall);
9196
if ($methodResult instanceof NeverType && $methodResult->isExplicit()) {
9297
return [];

tests/PHPStan/Rules/Functions/CallToFunctionStatementWithoutSideEffectsRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ public function testPhpDoc(): void
8585
]);
8686
}
8787

88+
public function testBug12224(): void
89+
{
90+
$this->analyse([__DIR__ . '/data/bug-12224.php'], []);
91+
}
92+
8893
public function testBug4455(): void
8994
{
9095
require_once __DIR__ . '/data/bug-4455.php';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12224Function;
4+
5+
/**
6+
* @phpstan-pure
7+
* @phpstan-assert string $value
8+
*/
9+
function string(mixed $value): string
10+
{
11+
if (\is_string($value)) {
12+
return $value;
13+
}
14+
throw new \RuntimeException();
15+
}
16+
17+
/** @var string|null $a */
18+
$a = '';
19+
string($a);

tests/PHPStan/Rules/Methods/CallToConstructorStatementWithoutSideEffectsRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,9 @@ public function testBug4455(): void
5151
$this->analyse([__DIR__ . '/data/bug-4455-constructor.php'], []);
5252
}
5353

54+
public function testBug12224(): void
55+
{
56+
$this->analyse([__DIR__ . '/data/bug-12224.php'], []);
57+
}
58+
5459
}

tests/PHPStan/Rules/Methods/CallToMethodStatementWithoutSideEffectsRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ public function testBug11503(): void
136136
$this->analyse([__DIR__ . '/data/bug-11503.php'], $errors);
137137
}
138138

139+
public function testBug12224(): void
140+
{
141+
$this->analyse([__DIR__ . '/data/bug-12224.php'], []);
142+
}
143+
139144
public function testFirstClassCallables(): void
140145
{
141146
$this->analyse([__DIR__ . '/data/first-class-callable-method-without-side-effect.php'], [

tests/PHPStan/Rules/Methods/CallToStaticMethodStatementWithoutSideEffectsRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ public function testBug4455(): void
8484
$this->analyse([__DIR__ . '/data/bug-4455-static.php'], []);
8585
}
8686

87+
public function testBug12224(): void
88+
{
89+
$this->analyse([__DIR__ . '/data/bug-12224.php'], []);
90+
}
91+
8792
public function testFirstClassCallables(): void
8893
{
8994
$this->analyse([__DIR__ . '/data/first-class-callable-static-method-without-side-effect.php'], [
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12224Method;
4+
5+
class Assert
6+
{
7+
/**
8+
* @phpstan-pure
9+
* @phpstan-assert string $value
10+
*/
11+
public static function staticString(mixed $value): string
12+
{
13+
if (\is_string($value)) {
14+
return $value;
15+
}
16+
throw new \RuntimeException();
17+
}
18+
19+
/**
20+
* @phpstan-pure
21+
* @phpstan-assert string $value
22+
*/
23+
public function string(mixed $value): string
24+
{
25+
if (\is_string($value)) {
26+
return $value;
27+
}
28+
throw new \RuntimeException();
29+
}
30+
}
31+
32+
class AssertConstructor
33+
{
34+
/**
35+
* @phpstan-pure
36+
* @phpstan-assert string $value
37+
*/
38+
public function __construct(mixed $value)
39+
{
40+
if (!\is_string($value)) {
41+
throw new \RuntimeException();
42+
}
43+
}
44+
}
45+
46+
/** @var string|null $a */
47+
$a = '';
48+
Assert::staticString($a);
49+
(new Assert())->string($a);
50+
new AssertConstructor($a);

0 commit comments

Comments
 (0)