Skip to content

Commit 94a0731

Browse files
authored
Merge branch refs/heads/2.1.x into 2.2.x
2 parents fd4c63b + 1991098 commit 94a0731

File tree

9 files changed

+111
-11
lines changed

9 files changed

+111
-11
lines changed

phpstan-baseline.neon

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -411,12 +411,6 @@ parameters:
411411
count: 1
412412
path: src/Reflection/ClassReflection.php
413413

414-
-
415-
rawMessage: 'Method PHPStan\Reflection\ClassReflection::getCacheKey() should return string but returns string|null.'
416-
identifier: return.type
417-
count: 1
418-
path: src/Reflection/ClassReflection.php
419-
420414
-
421415
rawMessage: Binary operation "&" between bool|float|int|string|null and bool|float|int|string|null results in an error.
422416
identifier: binaryOp.invalid

src/Analyser/NodeScopeResolver.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
174174
use PHPStan\Type\Constant\ConstantIntegerType;
175175
use PHPStan\Type\Constant\ConstantStringType;
176+
use PHPStan\Type\ConstantTypeHelper;
176177
use PHPStan\Type\FileTypeMapper;
177178
use PHPStan\Type\GeneralizePrecision;
178179
use PHPStan\Type\Generic\TemplateTypeHelper;
@@ -6108,6 +6109,41 @@ private function processAssignVar(
61086109
$conditionalExpressions = $this->processSureNotTypesForConditionalExpressionsAfterAssign($scope, $var->name, $conditionalExpressions, $falseySpecifiedTypes, $falseyType);
61096110
}
61106111

6112+
foreach ([null, false, 0, 0.0, '', '0', []] as $falseyScalar) {
6113+
$falseyType = ConstantTypeHelper::getTypeFromValue($falseyScalar);
6114+
$withoutFalseyType = TypeCombinator::remove($type, $falseyType);
6115+
if (
6116+
$withoutFalseyType->equals($type)
6117+
|| $withoutFalseyType->equals($truthyType)
6118+
) {
6119+
continue;
6120+
}
6121+
6122+
if ($falseyScalar === null) {
6123+
$astNode = new ConstFetch(new Name('null'));
6124+
} elseif ($falseyScalar === false) {
6125+
$astNode = new ConstFetch(new Name('false'));
6126+
} elseif ($falseyScalar === 0) {
6127+
$astNode = new Node\Scalar\Int_($falseyScalar);
6128+
} elseif ($falseyScalar === 0.0) {
6129+
$astNode = new Node\Scalar\Float_($falseyScalar);
6130+
} elseif (in_array($falseyScalar, ['', '0'], true)) {
6131+
$astNode = new Node\Scalar\String_($falseyScalar);
6132+
} elseif ($falseyScalar === []) {
6133+
$astNode = new Node\Expr\Array_($falseyScalar);
6134+
}
6135+
6136+
$notIdenticalConditionExpr = new Expr\BinaryOp\NotIdentical($assignedExpr, $astNode);
6137+
$notIdenticalSpecifiedTypes = $this->typeSpecifier->specifyTypesInCondition($scope, $notIdenticalConditionExpr, TypeSpecifierContext::createTrue());
6138+
$conditionalExpressions = $this->processSureTypesForConditionalExpressionsAfterAssign($scope, $var->name, $conditionalExpressions, $notIdenticalSpecifiedTypes, $withoutFalseyType);
6139+
$conditionalExpressions = $this->processSureNotTypesForConditionalExpressionsAfterAssign($scope, $var->name, $conditionalExpressions, $notIdenticalSpecifiedTypes, $withoutFalseyType);
6140+
6141+
$identicalConditionExpr = new Expr\BinaryOp\Identical($assignedExpr, $astNode);
6142+
$identicalSpecifiedTypes = $this->typeSpecifier->specifyTypesInCondition($scope, $identicalConditionExpr, TypeSpecifierContext::createTrue());
6143+
$conditionalExpressions = $this->processSureTypesForConditionalExpressionsAfterAssign($scope, $var->name, $conditionalExpressions, $identicalSpecifiedTypes, $falseyType);
6144+
$conditionalExpressions = $this->processSureNotTypesForConditionalExpressionsAfterAssign($scope, $var->name, $conditionalExpressions, $identicalSpecifiedTypes, $falseyType);
6145+
}
6146+
61116147
$this->callNodeCallback($nodeCallback, new VariableAssignNode($var, $assignedExpr), $scopeBeforeAssignEval, $storage);
61126148
$scope = $scope->assignVariable($var->name, $type, $scope->getNativeType($assignedExpr), TrinaryLogic::createYes());
61136149
foreach ($conditionalExpressions as $exprString => $holders) {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Bug10482;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Test
8+
{
9+
public ?int $id;
10+
}
11+
12+
$test = new Test();
13+
if (rand(0, 1)) {
14+
$test = null;
15+
}
16+
17+
$testId = $test?->id;
18+
if (null !== $testId) {
19+
assertType('Bug10482\Test', $test);
20+
}
21+
22+
if ($testId) {
23+
assertType('Bug10482\Test', $test);
24+
}

tests/PHPStan/Analyser/nsrt/bug-13546.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ function mixedLast($mixed): void
6666
function firstInCondition(array $array)
6767
{
6868
if (($key = array_key_first($array)) !== null) {
69-
assertType('list<string>', $array); // could be 'non-empty-list<string>'
69+
assertType('non-empty-list<string>', $array);
7070
return $array[$key];
7171
}
72-
assertType('list<string>', $array);
72+
assertType('array{}', $array);
7373
return null;
7474
}
7575

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Bug13709;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function doFOo(string $shortName): void
8+
{
9+
$pos = strpos($shortName, '\\');
10+
if ($pos === false) {
11+
return;
12+
}
13+
14+
assertType('non-falsy-string', $shortName);
15+
}
16+
17+

tests/PHPStan/Analyser/nsrt/bug-14081.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ function firstNotNull(array $array): mixed
4848
{
4949
if (($key = array_key_first($array)) !== null) {
5050
assertType('int<0, max>', $key);
51-
assertType('list<string>', $array); // could be non-empty-list<string>
51+
assertType('non-empty-list<string>', $array);
5252
assertType('string', $array[$key]);
5353
return $array[$key];
5454
}
@@ -60,7 +60,7 @@ function lastNotNull(array $array): mixed
6060
{
6161
if (($key = array_key_last($array)) !== null) {
6262
assertType('int<0, max>', $key);
63-
assertType('list<string>', $array); // could be non-empty-list<string>
63+
assertType('non-empty-list<string>', $array);
6464
assertType('string', $array[$key]);
6565
return $array[$key];
6666
}

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,7 @@ public function testArrayDimFetchAfterArraySearch(): void
840840

841841
$this->analyse([__DIR__ . '/data/array-dim-after-array-search.php'], [
842842
[
843-
'Offset int|string might not exist on array.',
843+
'Offset int|string might not exist on non-empty-array.',
844844
20,
845845
],
846846
]);

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3891,4 +3891,13 @@ public function testBug9820(): void
38913891
]);
38923892
}
38933893

3894+
#[RequiresPhp('>= 8.1')]
3895+
public function testBug6120(): void
3896+
{
3897+
$this->checkThisOnly = false;
3898+
$this->checkNullables = true;
3899+
$this->checkUnionTypes = true;
3900+
$this->analyse([__DIR__ . '/data/bug-6120.php'], []);
3901+
}
3902+
38943903
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types=1);
4+
5+
namespace Bug6120;
6+
7+
class Clazz
8+
{
9+
10+
public int $foo = 0;
11+
12+
public function bar(?Clazz $clazz): void
13+
{
14+
$result = $clazz?->foo;
15+
if ($result !== null) {
16+
$clazz->bar(null);
17+
}
18+
}
19+
20+
}

0 commit comments

Comments
 (0)