Skip to content

Commit

Permalink
PropertyExistsTypeSpecifyingExtension: Cleanup `instanceof ConstantSt…
Browse files Browse the repository at this point in the history
…ringType`
  • Loading branch information
staabm committed Jan 5, 2025
1 parent 2132cc0 commit 2ae4e34
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 23 deletions.
5 changes: 0 additions & 5 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1448,11 +1448,6 @@ parameters:
count: 1
path: src/Type/Php/NumberFormatFunctionDynamicReturnTypeExtension.php

-
message: "#^Doing instanceof PHPStan\\\\Type\\\\Constant\\\\ConstantStringType is error\\-prone and deprecated\\. Use Type\\:\\:getConstantStrings\\(\\) instead\\.$#"
count: 2
path: src/Type/Php/PropertyExistsTypeSpecifyingExtension.php

-
message: "#^Doing instanceof PHPStan\\\\Type\\\\Constant\\\\ConstantStringType is error\\-prone and deprecated\\. Use Type\\:\\:getConstantStrings\\(\\) instead\\.$#"
count: 2
Expand Down
35 changes: 17 additions & 18 deletions src/Type/Php/PropertyExistsTypeSpecifyingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Rules\Properties\PropertyReflectionFinder;
use PHPStan\Type\Accessory\HasPropertyType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\FunctionTypeSpecifyingExtension;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\ObjectWithoutClassType;
Expand Down Expand Up @@ -51,36 +50,36 @@ public function specifyTypes(
TypeSpecifierContext $context,
): SpecifiedTypes
{
$propertyNameType = $scope->getType($node->getArgs()[1]->value);
if (!$propertyNameType instanceof ConstantStringType) {
$propertyNames = $scope->getType($node->getArgs()[1]->value)->getConstantStrings();
if ($propertyNames === []) {
return new SpecifiedTypes([], []);
}

$objectType = $scope->getType($node->getArgs()[0]->value);
if ($objectType instanceof ConstantStringType) {
return new SpecifiedTypes([], []);
} elseif ($objectType->isObject()->yes()) {
$types = [new ObjectWithoutClassType()];
foreach ($propertyNames as $propertyNameType) {
$objectType = $scope->getType($node->getArgs()[0]->value);
if (!$objectType->isObject()->yes()) {
return new SpecifiedTypes([], []);
}

$propertyNode = new PropertyFetch(
$node->getArgs()[0]->value,
new Identifier($propertyNameType->getValue()),
);
} else {
return new SpecifiedTypes([], []);
}

$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($propertyNode, $scope);
if ($propertyReflection !== null) {
if (!$propertyReflection->isNative()) {
return new SpecifiedTypes([], []);
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($propertyNode, $scope);
if ($propertyReflection !== null) {
if (!$propertyReflection->isNative()) {
return new SpecifiedTypes([], []);
}
}

$types[] = new HasPropertyType($propertyNameType->getValue());
}

return $this->typeSpecifier->create(
$node->getArgs()[0]->value,
new IntersectionType([
new ObjectWithoutClassType(),
new HasPropertyType($propertyNameType->getValue()),
]),
new IntersectionType($types),
$context,
false,
$scope,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,14 @@ public function testImpossibleCheckTypeFunctionCall(): void
927,
'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
],
[
"Call to function property_exists() with \$this(CheckTypeFunctionCall\\FinalClassWithPropertyExistsOfUnionStrings) and 'foo2Property'|'fooProperty' will always evaluate to true.",
994,
],
[
"Call to function property_exists() with \$this(CheckTypeFunctionCall\\FinalClassWithPropertyExistsOfUnionStrings) and 'barProperty'|'fooProperty' will always evaluate to false.",
1005,
],
],
);
}
Expand Down Expand Up @@ -372,6 +380,10 @@ public function testImpossibleCheckTypeFunctionCallWithoutAlwaysTrue(): void
927,
'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
],
[
"Call to function property_exists() with \$this(CheckTypeFunctionCall\\FinalClassWithPropertyExistsOfUnionStrings) and 'barProperty'|'fooProperty' will always evaluate to false.",
1005,
],
],
);
}
Expand Down
30 changes: 30 additions & 0 deletions tests/PHPStan/Rules/Comparison/data/check-type-function-call.php
Original file line number Diff line number Diff line change
Expand Up @@ -976,3 +976,33 @@ function checkClosedResource($resource): void {

}
}

final class FinalClassWithPropertyExistsOfUnionStrings
{
/** @var int */
private $fooProperty;
/** @var int */
private $foo2Property;

public function doFoo()
{
$prop = 'fooProperty';
if (rand(0, 1) === 0) {
$prop = 'foo2Property';
}

if (property_exists($this, $prop)) {
}
}

public function doFooBar()
{
$prop = 'fooProperty';
if (rand(0, 1) === 0) {
$prop = 'barProperty';
}

if (property_exists($this, $prop)) {
}
}
}

0 comments on commit 2ae4e34

Please sign in to comment.