Skip to content

Commit 10b295f

Browse files
committed
Fix Equal handling in TypeSpecifier
1 parent 316f269 commit 10b295f

File tree

4 files changed

+198
-0
lines changed

4 files changed

+198
-0
lines changed

src/Analyser/TypeSpecifier.php

+8
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,14 @@ public function specifyTypesInCondition(
438438
return $this->specifyTypesInCondition($scope, new Expr\BinaryOp\Identical($expr->left, $expr->right), $context);
439439
}
440440

441+
$leftExprString = $this->printer->prettyPrintExpr($expr->left);
442+
$rightExprString = $this->printer->prettyPrintExpr($expr->right);
443+
if ($leftExprString === $rightExprString) {
444+
if (!$expr->left instanceof Expr\Variable || !$expr->right instanceof Expr\Variable) {
445+
return new SpecifiedTypes();
446+
}
447+
}
448+
441449
$leftTypes = $this->create($expr->left, $leftType, $context, false, $scope);
442450
$rightTypes = $this->create($expr->right, $rightType, $context, false, $scope);
443451

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Comparison;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<ImpossibleCheckTypeMethodCallRule>
10+
*/
11+
class ImpossibleCheckTypeMethodCallRuleEqualsTest extends RuleTestCase
12+
{
13+
14+
public function getRule(): Rule
15+
{
16+
return new ImpossibleCheckTypeMethodCallRule(
17+
new ImpossibleCheckTypeHelper(
18+
$this->createReflectionProvider(),
19+
$this->getTypeSpecifier(),
20+
[],
21+
true,
22+
),
23+
true,
24+
true,
25+
);
26+
}
27+
28+
public function testRule(): void
29+
{
30+
$this->analyse([__DIR__ . '/data/impossible-method-call.php'], [
31+
[
32+
'Call to method PHPStan\Tests\AssertionClass::assertString() with string will always evaluate to true.',
33+
14,
34+
],
35+
[
36+
'Call to method PHPStan\Tests\AssertionClass::assertString() with int will always evaluate to false.',
37+
15,
38+
],
39+
[
40+
'Call to method PHPStan\Tests\AssertionClass::assertNotInt() with int will always evaluate to false.',
41+
30,
42+
],
43+
[
44+
'Call to method PHPStan\Tests\AssertionClass::assertNotInt() with string will always evaluate to true.',
45+
36,
46+
],
47+
[
48+
'Call to method ImpossibleMethodCall\Foo::isSame() with 1 and 1 will always evaluate to true.',
49+
60,
50+
],
51+
[
52+
'Call to method ImpossibleMethodCall\Foo::isSame() with 1 and 2 will always evaluate to false.',
53+
63,
54+
],
55+
[
56+
'Call to method ImpossibleMethodCall\Foo::isNotSame() with 1 and 1 will always evaluate to false.',
57+
66,
58+
],
59+
[
60+
'Call to method ImpossibleMethodCall\Foo::isNotSame() with 1 and 2 will always evaluate to true.',
61+
69,
62+
],
63+
[
64+
'Call to method ImpossibleMethodCall\Foo::isSame() with stdClass and stdClass will always evaluate to true.',
65+
78,
66+
],
67+
[
68+
'Call to method ImpossibleMethodCall\Foo::isNotSame() with stdClass and stdClass will always evaluate to false.',
69+
81,
70+
],
71+
]);
72+
}
73+
74+
public static function getAdditionalConfigFiles(): array
75+
{
76+
return [
77+
__DIR__ . '/impossible-check-type-method-call-equals.neon',
78+
];
79+
}
80+
81+
}

tests/PHPStan/Rules/Comparison/data/TestMethodTypeSpecifyingExtensions.php

+90
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,93 @@ public function specifyTypes(
149149
}
150150

151151
}
152+
153+
class FooIsEqual implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
154+
{
155+
156+
/** @var TypeSpecifier */
157+
private $typeSpecifier;
158+
159+
public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
160+
{
161+
$this->typeSpecifier = $typeSpecifier;
162+
}
163+
164+
public function getClass(): string
165+
{
166+
return \ImpossibleMethodCall\Foo::class;
167+
}
168+
169+
public function isMethodSupported(
170+
MethodReflection $methodReflection,
171+
MethodCall $node,
172+
TypeSpecifierContext $context
173+
): bool
174+
{
175+
return $methodReflection->getName() === 'isSame'
176+
&& count($node->args) >= 2;
177+
}
178+
179+
public function specifyTypes(
180+
MethodReflection $methodReflection,
181+
MethodCall $node,
182+
Scope $scope,
183+
TypeSpecifierContext $context
184+
): SpecifiedTypes
185+
{
186+
return $this->typeSpecifier->specifyTypesInCondition(
187+
$scope,
188+
new \PhpParser\Node\Expr\BinaryOp\Equal(
189+
$node->args[0]->value,
190+
$node->args[1]->value
191+
),
192+
$context
193+
);
194+
}
195+
196+
}
197+
198+
class FooIsNotEqual implements MethodTypeSpecifyingExtension,
199+
TypeSpecifierAwareExtension {
200+
201+
/** @var TypeSpecifier */
202+
private $typeSpecifier;
203+
204+
public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
205+
{
206+
$this->typeSpecifier = $typeSpecifier;
207+
}
208+
209+
public function getClass(): string
210+
{
211+
return \ImpossibleMethodCall\Foo::class;
212+
}
213+
214+
public function isMethodSupported(
215+
MethodReflection $methodReflection,
216+
MethodCall $node,
217+
TypeSpecifierContext $context
218+
): bool
219+
{
220+
return $methodReflection->getName() === 'isNotSame'
221+
&& count($node->args) >= 2;
222+
}
223+
224+
public function specifyTypes(
225+
MethodReflection $methodReflection,
226+
MethodCall $node,
227+
Scope $scope,
228+
TypeSpecifierContext $context
229+
): SpecifiedTypes
230+
{
231+
return $this->typeSpecifier->specifyTypesInCondition(
232+
$scope,
233+
new \PhpParser\Node\Expr\BinaryOp\NotEqual(
234+
$node->args[0]->value,
235+
$node->args[1]->value
236+
),
237+
$context
238+
);
239+
}
240+
241+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
services:
2+
-
3+
class: PHPStan\Tests\AssertionClassMethodTypeSpecifyingExtension
4+
arguments:
5+
nullContext: null
6+
tags:
7+
- phpstan.typeSpecifier.methodTypeSpecifyingExtension
8+
-
9+
class: PHPStan\Rules\Comparison\AssertNotInt
10+
tags:
11+
- phpstan.typeSpecifier.methodTypeSpecifyingExtension
12+
-
13+
class: PHPStan\Rules\Comparison\FooIsEqual
14+
tags:
15+
- phpstan.typeSpecifier.methodTypeSpecifyingExtension
16+
-
17+
class: PHPStan\Rules\Comparison\FooIsNotEqual
18+
tags:
19+
- phpstan.typeSpecifier.methodTypeSpecifyingExtension

0 commit comments

Comments
 (0)