Skip to content

Commit 05942ca

Browse files
committed
Fix false positive with isset() and empty()
1 parent d7957d8 commit 05942ca

File tree

5 files changed

+68
-6
lines changed

5 files changed

+68
-6
lines changed

src/Analyser/EnsuredNonNullabilityResultExpression.php

+13-1
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,17 @@ class EnsuredNonNullabilityResultExpression
1212

1313
private Type $originalType;
1414

15-
public function __construct(Expr $expression, Type $originalType)
15+
private Type $originalNativeType;
16+
17+
public function __construct(
18+
Expr $expression,
19+
Type $originalType,
20+
Type $originalNativeType
21+
)
1622
{
1723
$this->expression = $expression;
1824
$this->originalType = $originalType;
25+
$this->originalNativeType = $originalNativeType;
1926
}
2027

2128
public function getExpression(): Expr
@@ -28,4 +35,9 @@ public function getOriginalType(): Type
2835
return $this->originalType;
2936
}
3037

38+
public function getOriginalNativeType(): Type
39+
{
40+
return $this->originalNativeType;
41+
}
42+
3143
}

src/Analyser/MutatingScope.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -2958,7 +2958,7 @@ public function unsetExpression(Expr $expr): self
29582958
return $this;
29592959
}
29602960

2961-
public function specifyExpressionType(Expr $expr, Type $type): self
2961+
public function specifyExpressionType(Expr $expr, Type $type, ?Type $nativeType = null): self
29622962
{
29632963
if ($expr instanceof Node\Scalar || $expr instanceof Array_) {
29642964
return $this;
@@ -2998,8 +2998,12 @@ public function specifyExpressionType(Expr $expr, Type $type): self
29982998
$variableTypes = $this->getVariableTypes();
29992999
$variableTypes[$variableName] = VariableTypeHolder::createYes($type);
30003000

3001+
if ($nativeType === null) {
3002+
$nativeType = $type;
3003+
}
3004+
30013005
$nativeTypes = $this->nativeExpressionTypes;
3002-
$nativeTypes[sprintf('$%s', $variableName)] = $type;
3006+
$nativeTypes[sprintf('$%s', $variableName)] = $nativeType;
30033007

30043008
return $this->scopeFactory->create(
30053009
$this->context,

src/Analyser/NodeScopeResolver.php

+12-3
Original file line numberDiff line numberDiff line change
@@ -1231,8 +1231,13 @@ private function ensureNonNullability(MutatingScope $scope, Expr $expr, bool $fi
12311231
$exprType = $scope->getType($exprToSpecify);
12321232
$exprTypeWithoutNull = TypeCombinator::removeNull($exprType);
12331233
if (!$exprType->equals($exprTypeWithoutNull)) {
1234-
$specifiedExpressions[] = new EnsuredNonNullabilityResultExpression($exprToSpecify, $exprType);
1235-
$scope = $scope->specifyExpressionType($exprToSpecify, $exprTypeWithoutNull);
1234+
$nativeType = $scope->getNativeType($exprToSpecify);
1235+
$specifiedExpressions[] = new EnsuredNonNullabilityResultExpression($exprToSpecify, $exprType, $nativeType);
1236+
$scope = $scope->specifyExpressionType(
1237+
$exprToSpecify,
1238+
$exprTypeWithoutNull,
1239+
TypeCombinator::removeNull($nativeType)
1240+
);
12361241
}
12371242

12381243
if ($exprToSpecify instanceof PropertyFetch) {
@@ -1259,7 +1264,11 @@ private function ensureNonNullability(MutatingScope $scope, Expr $expr, bool $fi
12591264
private function revertNonNullability(MutatingScope $scope, array $specifiedExpressions): MutatingScope
12601265
{
12611266
foreach ($specifiedExpressions as $specifiedExpressionResult) {
1262-
$scope = $scope->specifyExpressionType($specifiedExpressionResult->getExpression(), $specifiedExpressionResult->getOriginalType());
1267+
$scope = $scope->specifyExpressionType(
1268+
$specifiedExpressionResult->getExpression(),
1269+
$specifiedExpressionResult->getOriginalType(),
1270+
$specifiedExpressionResult->getOriginalNativeType()
1271+
);
12631272
}
12641273

12651274
return $scope;

tests/PHPStan/Analyser/NodeScopeResolverTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -10235,6 +10235,11 @@ public function dataBug3990(): array
1023510235
return $this->gatherAssertTypes(__DIR__ . '/data/bug-3990.php');
1023610236
}
1023710237

10238+
public function dataBug3991(): array
10239+
{
10240+
return $this->gatherAssertTypes(__DIR__ . '/data/bug-3991.php');
10241+
}
10242+
1023810243
/**
1023910244
* @dataProvider dataBug2574
1024010245
* @dataProvider dataBug2577
@@ -10324,6 +10329,7 @@ public function dataBug3990(): array
1032410329
* @dataProvider dataBug3985
1032510330
* @dataProvider dataArraySliceNonEmpty
1032610331
* @dataProvider dataBug3990
10332+
* @dataProvider dataBug3991
1032710333
* @param string $assertType
1032810334
* @param string $file
1032910335
* @param mixed ...$args
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Bug3991;
4+
5+
use function PHPStan\Analyser\assertNativeType;
6+
use function PHPStan\Analyser\assertType;
7+
8+
class Foo
9+
{
10+
/**
11+
* @param \stdClass|array|null $config
12+
*
13+
* @return \stdClass
14+
*/
15+
public static function email($config = null)
16+
{
17+
assertNativeType('mixed', $config);
18+
assertType('array|stdClass|null', $config);
19+
if (empty($config))
20+
{
21+
assertNativeType('mixed', $config);
22+
assertType('array|stdClass|null', $config);
23+
$config = new \stdClass();
24+
} elseif (! (is_array($config) || $config instanceof \stdClass)) {
25+
assertNativeType('mixed~array|stdClass|false|null', $config);
26+
assertType('*NEVER*', $config);
27+
}
28+
29+
return new \stdClass($config);
30+
}
31+
}

0 commit comments

Comments
 (0)